@bsv/sdk 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/auth/Peer.js +8 -13
- package/dist/cjs/src/auth/Peer.js.map +1 -1
- package/dist/cjs/src/auth/SessionManager.js +4 -7
- package/dist/cjs/src/auth/SessionManager.js.map +1 -1
- package/dist/cjs/src/auth/certificates/MasterCertificate.js +1 -1
- package/dist/cjs/src/auth/certificates/MasterCertificate.js.map +1 -1
- package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js +1 -1
- package/dist/cjs/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
- package/dist/cjs/src/auth/clients/AuthFetch.js +32 -32
- package/dist/cjs/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js +4 -4
- package/dist/cjs/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/cjs/src/compat/ECIES.js +29 -34
- package/dist/cjs/src/compat/ECIES.js.map +1 -1
- package/dist/cjs/src/compat/HD.js +9 -4
- package/dist/cjs/src/compat/HD.js.map +1 -1
- package/dist/cjs/src/compat/Mnemonic.js +12 -12
- package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
- package/dist/cjs/src/identity/ContactsManager.js +212 -234
- package/dist/cjs/src/identity/ContactsManager.js.map +1 -1
- package/dist/cjs/src/identity/IdentityClient.js +199 -63
- package/dist/cjs/src/identity/IdentityClient.js.map +1 -1
- package/dist/cjs/src/kvstore/GlobalKVStore.js +30 -31
- package/dist/cjs/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/LocalKVStore.js +9 -9
- package/dist/cjs/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/cjs/src/kvstore/kvStoreInterpreter.js +2 -2
- package/dist/cjs/src/kvstore/kvStoreInterpreter.js.map +1 -1
- package/dist/cjs/src/messages/SignedMessage.js +1 -1
- package/dist/cjs/src/messages/SignedMessage.js.map +1 -1
- package/dist/cjs/src/overlay-tools/Historian.js +1 -1
- package/dist/cjs/src/overlay-tools/Historian.js.map +1 -1
- package/dist/cjs/src/overlay-tools/LookupResolver.js +213 -93
- package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +75 -146
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
- package/dist/cjs/src/primitives/AESGCM.js +2 -2
- package/dist/cjs/src/primitives/AESGCM.js.map +1 -1
- package/dist/cjs/src/primitives/BigNumber.js +164 -148
- package/dist/cjs/src/primitives/BigNumber.js.map +1 -1
- package/dist/cjs/src/primitives/Curve.js +17 -15
- package/dist/cjs/src/primitives/Curve.js.map +1 -1
- package/dist/cjs/src/primitives/ECDSA.js +12 -7
- package/dist/cjs/src/primitives/ECDSA.js.map +1 -1
- package/dist/cjs/src/primitives/Hash.js +312 -105
- package/dist/cjs/src/primitives/Hash.js.map +1 -1
- package/dist/cjs/src/primitives/JacobianPoint.js +8 -8
- package/dist/cjs/src/primitives/JacobianPoint.js.map +1 -1
- package/dist/cjs/src/primitives/K256.js +3 -3
- package/dist/cjs/src/primitives/K256.js.map +1 -1
- package/dist/cjs/src/primitives/Point.js +36 -40
- package/dist/cjs/src/primitives/Point.js.map +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +4 -4
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/primitives/PublicKey.js +4 -4
- package/dist/cjs/src/primitives/PublicKey.js.map +1 -1
- package/dist/cjs/src/primitives/Random.js +10 -14
- package/dist/cjs/src/primitives/Random.js.map +1 -1
- package/dist/cjs/src/primitives/ReaderUint8Array.js +6 -6
- package/dist/cjs/src/primitives/ReaderUint8Array.js.map +1 -1
- package/dist/cjs/src/primitives/Schnorr.js +2 -2
- package/dist/cjs/src/primitives/Schnorr.js.map +1 -1
- package/dist/cjs/src/primitives/Secp256r1.js +2 -1
- package/dist/cjs/src/primitives/Secp256r1.js.map +1 -1
- package/dist/cjs/src/primitives/Signature.js +8 -8
- package/dist/cjs/src/primitives/Signature.js.map +1 -1
- package/dist/cjs/src/primitives/SymmetricKey.js +123 -1
- package/dist/cjs/src/primitives/SymmetricKey.js.map +1 -1
- package/dist/cjs/src/primitives/TransactionSignature.js +20 -21
- package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/cjs/src/primitives/utils.js +39 -46
- package/dist/cjs/src/primitives/utils.js.map +1 -1
- package/dist/cjs/src/registry/RegistryClient.js +31 -23
- package/dist/cjs/src/registry/RegistryClient.js.map +1 -1
- package/dist/cjs/src/remittance/RemittanceManager.js +19 -18
- package/dist/cjs/src/remittance/RemittanceManager.js.map +1 -1
- package/dist/cjs/src/remittance/modules/BasicBRC29.js.map +1 -1
- package/dist/cjs/src/script/Script.js +93 -170
- package/dist/cjs/src/script/Script.js.map +1 -1
- package/dist/cjs/src/script/ScriptEvaluationError.js +2 -2
- package/dist/cjs/src/script/ScriptEvaluationError.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +14 -12
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/script/templates/PushDrop.js +22 -18
- package/dist/cjs/src/script/templates/PushDrop.js.map +1 -1
- package/dist/cjs/src/script/templates/RPuzzle.js +2 -4
- package/dist/cjs/src/script/templates/RPuzzle.js.map +1 -1
- package/dist/cjs/src/storage/StorageDownloader.js +42 -9
- package/dist/cjs/src/storage/StorageDownloader.js.map +1 -1
- package/dist/cjs/src/totp/totp.js +1 -1
- package/dist/cjs/src/totp/totp.js.map +1 -1
- package/dist/cjs/src/transaction/Beef.js +239 -192
- package/dist/cjs/src/transaction/Beef.js.map +1 -1
- package/dist/cjs/src/transaction/BeefConstants.js +19 -0
- package/dist/cjs/src/transaction/BeefConstants.js.map +1 -0
- package/dist/cjs/src/transaction/BeefTx.js +12 -12
- package/dist/cjs/src/transaction/BeefTx.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +4 -4
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/src/transaction/Transaction.js +49 -52
- package/dist/cjs/src/transaction/Transaction.js.map +1 -1
- package/dist/cjs/src/transaction/fee-models/SatoshisPerKilobyte.js +1 -1
- package/dist/cjs/src/transaction/fee-models/SatoshisPerKilobyte.js.map +1 -1
- package/dist/cjs/src/transaction/http/BinaryFetchClient.js +9 -9
- package/dist/cjs/src/transaction/http/BinaryFetchClient.js.map +1 -1
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js +9 -9
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -1
- package/dist/cjs/src/wallet/CachedKeyDeriver.js +1 -1
- package/dist/cjs/src/wallet/CachedKeyDeriver.js.map +1 -1
- package/dist/cjs/src/wallet/WalletClient.js.map +1 -1
- package/dist/cjs/src/wallet/WalletError.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js +5 -4
- package/dist/cjs/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/ReactNativeWebView.js +9 -9
- package/dist/cjs/src/wallet/substrates/ReactNativeWebView.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +92 -92
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +387 -711
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/XDM.js +4 -4
- package/dist/cjs/src/wallet/substrates/XDM.js.map +1 -1
- package/dist/cjs/src/wallet/substrates/window.CWI.js +2 -2
- package/dist/cjs/src/wallet/substrates/window.CWI.js.map +1 -1
- package/dist/cjs/src/wallet/validationHelpers.js +9 -9
- package/dist/cjs/src/wallet/validationHelpers.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/Peer.js +25 -13
- package/dist/esm/src/auth/Peer.js.map +1 -1
- package/dist/esm/src/auth/SessionManager.js +4 -7
- package/dist/esm/src/auth/SessionManager.js.map +1 -1
- package/dist/esm/src/auth/certificates/MasterCertificate.js +1 -1
- package/dist/esm/src/auth/certificates/MasterCertificate.js.map +1 -1
- package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js +1 -1
- package/dist/esm/src/auth/certificates/__tests/CompletedProtoWallet.js.map +1 -1
- package/dist/esm/src/auth/clients/AuthFetch.js +32 -32
- package/dist/esm/src/auth/clients/AuthFetch.js.map +1 -1
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js +4 -4
- package/dist/esm/src/auth/transports/SimplifiedFetchTransport.js.map +1 -1
- package/dist/esm/src/compat/ECIES.js +29 -34
- package/dist/esm/src/compat/ECIES.js.map +1 -1
- package/dist/esm/src/compat/HD.js +9 -4
- package/dist/esm/src/compat/HD.js.map +1 -1
- package/dist/esm/src/compat/Mnemonic.js +12 -12
- package/dist/esm/src/compat/Mnemonic.js.map +1 -1
- package/dist/esm/src/identity/ContactsManager.js +212 -234
- package/dist/esm/src/identity/ContactsManager.js.map +1 -1
- package/dist/esm/src/identity/IdentityClient.js +199 -63
- package/dist/esm/src/identity/IdentityClient.js.map +1 -1
- package/dist/esm/src/kvstore/GlobalKVStore.js +30 -31
- package/dist/esm/src/kvstore/GlobalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/LocalKVStore.js +9 -9
- package/dist/esm/src/kvstore/LocalKVStore.js.map +1 -1
- package/dist/esm/src/kvstore/kvStoreInterpreter.js +2 -2
- package/dist/esm/src/kvstore/kvStoreInterpreter.js.map +1 -1
- package/dist/esm/src/messages/SignedMessage.js +1 -1
- package/dist/esm/src/messages/SignedMessage.js.map +1 -1
- package/dist/esm/src/overlay-tools/Historian.js +1 -1
- package/dist/esm/src/overlay-tools/Historian.js.map +1 -1
- package/dist/esm/src/overlay-tools/LookupResolver.js +213 -93
- package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -1
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +74 -146
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -1
- package/dist/esm/src/primitives/AESGCM.js +2 -2
- package/dist/esm/src/primitives/AESGCM.js.map +1 -1
- package/dist/esm/src/primitives/BigNumber.js +167 -154
- package/dist/esm/src/primitives/BigNumber.js.map +1 -1
- package/dist/esm/src/primitives/Curve.js +17 -15
- package/dist/esm/src/primitives/Curve.js.map +1 -1
- package/dist/esm/src/primitives/ECDSA.js +12 -7
- package/dist/esm/src/primitives/ECDSA.js.map +1 -1
- package/dist/esm/src/primitives/Hash.js +316 -105
- package/dist/esm/src/primitives/Hash.js.map +1 -1
- package/dist/esm/src/primitives/JacobianPoint.js +8 -8
- package/dist/esm/src/primitives/JacobianPoint.js.map +1 -1
- package/dist/esm/src/primitives/K256.js +3 -3
- package/dist/esm/src/primitives/K256.js.map +1 -1
- package/dist/esm/src/primitives/Point.js +36 -40
- package/dist/esm/src/primitives/Point.js.map +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +4 -4
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/primitives/PublicKey.js +4 -4
- package/dist/esm/src/primitives/PublicKey.js.map +1 -1
- package/dist/esm/src/primitives/Random.js +10 -14
- package/dist/esm/src/primitives/Random.js.map +1 -1
- package/dist/esm/src/primitives/ReaderUint8Array.js +6 -6
- package/dist/esm/src/primitives/ReaderUint8Array.js.map +1 -1
- package/dist/esm/src/primitives/Schnorr.js +1 -1
- package/dist/esm/src/primitives/Schnorr.js.map +1 -1
- package/dist/esm/src/primitives/Secp256r1.js +2 -1
- package/dist/esm/src/primitives/Secp256r1.js.map +1 -1
- package/dist/esm/src/primitives/Signature.js +8 -8
- package/dist/esm/src/primitives/Signature.js.map +1 -1
- package/dist/esm/src/primitives/SymmetricKey.js +123 -1
- package/dist/esm/src/primitives/SymmetricKey.js.map +1 -1
- package/dist/esm/src/primitives/TransactionSignature.js +20 -21
- package/dist/esm/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/esm/src/primitives/utils.js +39 -48
- package/dist/esm/src/primitives/utils.js.map +1 -1
- package/dist/esm/src/registry/RegistryClient.js +31 -23
- package/dist/esm/src/registry/RegistryClient.js.map +1 -1
- package/dist/esm/src/remittance/RemittanceManager.js +19 -18
- package/dist/esm/src/remittance/RemittanceManager.js.map +1 -1
- package/dist/esm/src/remittance/modules/BasicBRC29.js.map +1 -1
- package/dist/esm/src/script/Script.js +93 -170
- package/dist/esm/src/script/Script.js.map +1 -1
- package/dist/esm/src/script/ScriptEvaluationError.js +2 -2
- package/dist/esm/src/script/ScriptEvaluationError.js.map +1 -1
- package/dist/esm/src/script/Spend.js +14 -12
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/script/templates/PushDrop.js +4 -3
- package/dist/esm/src/script/templates/PushDrop.js.map +1 -1
- package/dist/esm/src/script/templates/RPuzzle.js +2 -4
- package/dist/esm/src/script/templates/RPuzzle.js.map +1 -1
- package/dist/esm/src/storage/StorageDownloader.js +1 -1
- package/dist/esm/src/storage/StorageDownloader.js.map +1 -1
- package/dist/esm/src/totp/totp.js +1 -1
- package/dist/esm/src/totp/totp.js.map +1 -1
- package/dist/esm/src/transaction/Beef.js +229 -186
- package/dist/esm/src/transaction/Beef.js.map +1 -1
- package/dist/esm/src/transaction/BeefConstants.js +16 -0
- package/dist/esm/src/transaction/BeefConstants.js.map +1 -0
- package/dist/esm/src/transaction/BeefTx.js +3 -3
- package/dist/esm/src/transaction/BeefTx.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +4 -4
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/src/transaction/Transaction.js +49 -52
- package/dist/esm/src/transaction/Transaction.js.map +1 -1
- package/dist/esm/src/transaction/fee-models/SatoshisPerKilobyte.js +1 -1
- package/dist/esm/src/transaction/fee-models/SatoshisPerKilobyte.js.map +1 -1
- package/dist/esm/src/transaction/http/BinaryFetchClient.js +9 -9
- package/dist/esm/src/transaction/http/BinaryFetchClient.js.map +1 -1
- package/dist/esm/src/transaction/http/DefaultHttpClient.js +9 -9
- package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -1
- package/dist/esm/src/wallet/CachedKeyDeriver.js +1 -1
- package/dist/esm/src/wallet/CachedKeyDeriver.js.map +1 -1
- package/dist/esm/src/wallet/WalletClient.js.map +1 -1
- package/dist/esm/src/wallet/WalletError.js.map +1 -1
- package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js +5 -4
- package/dist/esm/src/wallet/substrates/HTTPWalletJSON.js.map +1 -1
- package/dist/esm/src/wallet/substrates/ReactNativeWebView.js +9 -9
- package/dist/esm/src/wallet/substrates/ReactNativeWebView.js.map +1 -1
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +92 -92
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -1
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +387 -711
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -1
- package/dist/esm/src/wallet/substrates/XDM.js +4 -4
- package/dist/esm/src/wallet/substrates/XDM.js.map +1 -1
- package/dist/esm/src/wallet/substrates/window.CWI.js +2 -2
- package/dist/esm/src/wallet/substrates/window.CWI.js.map +1 -1
- package/dist/esm/src/wallet/validationHelpers.js +9 -9
- package/dist/esm/src/wallet/validationHelpers.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/Peer.d.ts +13 -0
- package/dist/types/src/auth/Peer.d.ts.map +1 -1
- package/dist/types/src/auth/SessionManager.d.ts.map +1 -1
- package/dist/types/src/auth/clients/AuthFetch.d.ts.map +1 -1
- package/dist/types/src/compat/ECIES.d.ts.map +1 -1
- package/dist/types/src/compat/HD.d.ts.map +1 -1
- package/dist/types/src/identity/ContactsManager.d.ts +31 -2
- package/dist/types/src/identity/ContactsManager.d.ts.map +1 -1
- package/dist/types/src/identity/IdentityClient.d.ts +75 -10
- package/dist/types/src/identity/IdentityClient.d.ts.map +1 -1
- package/dist/types/src/kvstore/GlobalKVStore.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/LookupResolver.d.ts +73 -2
- package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -1
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts +18 -3
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -1
- package/dist/types/src/primitives/BigNumber.d.ts +13 -3
- package/dist/types/src/primitives/BigNumber.d.ts.map +1 -1
- package/dist/types/src/primitives/Curve.d.ts.map +1 -1
- package/dist/types/src/primitives/ECDSA.d.ts.map +1 -1
- package/dist/types/src/primitives/Hash.d.ts +22 -17
- package/dist/types/src/primitives/Hash.d.ts.map +1 -1
- package/dist/types/src/primitives/JacobianPoint.d.ts +3 -1
- package/dist/types/src/primitives/JacobianPoint.d.ts.map +1 -1
- package/dist/types/src/primitives/Point.d.ts.map +1 -1
- package/dist/types/src/primitives/Random.d.ts +2 -2
- package/dist/types/src/primitives/Random.d.ts.map +1 -1
- package/dist/types/src/primitives/ReaderUint8Array.d.ts.map +1 -1
- package/dist/types/src/primitives/Schnorr.d.ts +2 -1
- package/dist/types/src/primitives/Schnorr.d.ts.map +1 -1
- package/dist/types/src/primitives/Secp256r1.d.ts.map +1 -1
- package/dist/types/src/primitives/SymmetricKey.d.ts.map +1 -1
- package/dist/types/src/primitives/utils.d.ts +2 -4
- package/dist/types/src/primitives/utils.d.ts.map +1 -1
- package/dist/types/src/registry/RegistryClient.d.ts.map +1 -1
- package/dist/types/src/remittance/RemittanceManager.d.ts.map +1 -1
- package/dist/types/src/remittance/modules/BasicBRC29.d.ts.map +1 -1
- package/dist/types/src/script/Script.d.ts +15 -8
- package/dist/types/src/script/Script.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- package/dist/types/src/script/templates/PushDrop.d.ts +3 -1
- package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -1
- package/dist/types/src/script/templates/RPuzzle.d.ts.map +1 -1
- package/dist/types/src/transaction/Beef.d.ts +46 -8
- package/dist/types/src/transaction/Beef.d.ts.map +1 -1
- package/dist/types/src/transaction/BeefConstants.d.ts +15 -0
- package/dist/types/src/transaction/BeefConstants.d.ts.map +1 -0
- package/dist/types/src/transaction/Transaction.d.ts.map +1 -1
- package/dist/types/src/wallet/CachedKeyDeriver.d.ts.map +1 -1
- package/dist/types/src/wallet/KeyDeriver.d.ts +1 -1
- package/dist/types/src/wallet/KeyDeriver.d.ts.map +1 -1
- package/dist/types/src/wallet/Wallet.interfaces.d.ts +18 -3
- package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -1
- package/dist/types/src/wallet/WalletClient.d.ts +8 -8
- package/dist/types/src/wallet/WalletClient.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts +7 -7
- package/dist/types/src/wallet/substrates/HTTPWalletJSON.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts +36 -7
- package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts.map +1 -1
- package/dist/types/src/wallet/substrates/window.CWI.d.ts +9 -9
- package/dist/types/src/wallet/substrates/window.CWI.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +3 -3
- package/package.json +1 -1
- package/src/auth/Peer.ts +26 -13
- package/src/auth/SessionManager.ts +4 -7
- package/src/auth/certificates/MasterCertificate.ts +1 -1
- package/src/auth/certificates/__tests/CompletedProtoWallet.ts +1 -1
- package/src/auth/clients/AuthFetch.ts +41 -41
- package/src/auth/transports/SimplifiedFetchTransport.ts +4 -4
- package/src/compat/ECIES.ts +29 -34
- package/src/compat/HD.ts +10 -5
- package/src/compat/Mnemonic.ts +11 -11
- package/src/compat/__tests/HD.test.ts +19 -0
- package/src/identity/ContactsManager.ts +236 -258
- package/src/identity/IdentityClient.ts +244 -71
- package/src/identity/__tests/IdentityClient.additional.test.ts +150 -1
- package/src/identity/__tests/IdentityClient.test.ts +27 -3
- package/src/kvstore/GlobalKVStore.ts +31 -32
- package/src/kvstore/LocalKVStore.ts +8 -8
- package/src/kvstore/kvStoreInterpreter.ts +2 -2
- package/src/messages/SignedMessage.ts +1 -1
- package/src/overlay-tools/Historian.ts +1 -1
- package/src/overlay-tools/LookupResolver.ts +264 -90
- package/src/overlay-tools/SHIPBroadcaster.ts +92 -168
- package/src/primitives/AESGCM.ts +2 -2
- package/src/primitives/BigNumber.ts +122 -113
- package/src/primitives/Curve.ts +16 -15
- package/src/primitives/ECDSA.ts +10 -8
- package/src/primitives/Hash.ts +381 -146
- package/src/primitives/JacobianPoint.ts +13 -11
- package/src/primitives/K256.ts +3 -3
- package/src/primitives/Point.ts +35 -38
- package/src/primitives/PrivateKey.ts +3 -3
- package/src/primitives/PublicKey.ts +3 -3
- package/src/primitives/Random.ts +11 -14
- package/src/primitives/ReaderUint8Array.ts +7 -7
- package/src/primitives/Schnorr.ts +2 -1
- package/src/primitives/Secp256r1.ts +2 -1
- package/src/primitives/Signature.ts +8 -8
- package/src/primitives/SymmetricKey.ts +145 -1
- package/src/primitives/TransactionSignature.ts +16 -16
- package/src/primitives/__tests/Hash.additional.test.ts +65 -0
- package/src/primitives/__tests/Hash.test.ts +6 -1
- package/src/primitives/utils.ts +37 -47
- package/src/registry/RegistryClient.ts +25 -25
- package/src/remittance/RemittanceManager.ts +17 -18
- package/src/remittance/modules/BasicBRC29.ts +2 -5
- package/src/script/Script.ts +114 -170
- package/src/script/ScriptEvaluationError.ts +2 -2
- package/src/script/Spend.ts +14 -15
- package/src/script/templates/PushDrop.ts +5 -3
- package/src/script/templates/RPuzzle.ts +2 -4
- package/src/storage/StorageDownloader.ts +1 -1
- package/src/totp/totp.ts +1 -1
- package/src/transaction/Beef.ts +241 -203
- package/src/transaction/BeefConstants.ts +16 -0
- package/src/transaction/BeefTx.ts +3 -3
- package/src/transaction/MerklePath.ts +4 -4
- package/src/transaction/Transaction.ts +48 -51
- package/src/transaction/fee-models/SatoshisPerKilobyte.ts +1 -1
- package/src/transaction/http/BinaryFetchClient.ts +8 -8
- package/src/transaction/http/DefaultHttpClient.ts +8 -8
- package/src/wallet/CachedKeyDeriver.ts +8 -6
- package/src/wallet/KeyDeriver.ts +1 -1
- package/src/wallet/Wallet.interfaces.ts +18 -5
- package/src/wallet/WalletClient.ts +9 -9
- package/src/wallet/WalletError.ts +1 -1
- package/src/wallet/__tests/WalletClient.substrate.test.ts +10 -6
- package/src/wallet/substrates/HTTPWalletJSON.ts +22 -21
- package/src/wallet/substrates/ReactNativeWebView.ts +9 -9
- package/src/wallet/substrates/WalletWireProcessor.ts +83 -83
- package/src/wallet/substrates/WalletWireTransceiver.ts +528 -938
- package/src/wallet/substrates/XDM.ts +4 -4
- package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +38 -25
- package/src/wallet/substrates/__tests/ReactNativeWebView.test.ts +174 -0
- package/src/wallet/substrates/__tests/window.CWI.test.ts +256 -0
- package/src/wallet/substrates/window.CWI.ts +11 -11
- package/src/wallet/validationHelpers.ts +9 -9
|
@@ -394,6 +394,25 @@ describe('HD', () => {
|
|
|
394
394
|
})
|
|
395
395
|
})
|
|
396
396
|
|
|
397
|
+
describe('#derive path validation', () => {
|
|
398
|
+
it('should derive mixed hardened and non-hardened path segments', () => {
|
|
399
|
+
const bip32 = HD.fromString(vector1mPrivate)
|
|
400
|
+
expect(() => bip32.derive("m/0'/0'/2/3/4'/4")).not.toThrow()
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it('should reject apostrophes that are not trailing hardening markers', () => {
|
|
404
|
+
const bip32 = HD.fromString(vector1mPrivate)
|
|
405
|
+
expect(() => bip32.derive("m/1'2")).toThrow('invalid path')
|
|
406
|
+
expect(() => bip32.derive("m/1''")).toThrow('invalid path')
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('should reject child indexes outside the non-hardened range', () => {
|
|
410
|
+
const bip32 = HD.fromString(vector1mPrivate)
|
|
411
|
+
expect(() => bip32.derive('m/2147483648')).toThrow('invalid path')
|
|
412
|
+
expect(() => bip32.derive("m/2147483648'")).toThrow('invalid path')
|
|
413
|
+
})
|
|
414
|
+
})
|
|
415
|
+
|
|
397
416
|
describe('#toString', () => {
|
|
398
417
|
const bip32 = new HD()
|
|
399
418
|
bip32.fromRandom()
|
|
@@ -34,68 +34,110 @@ export class ContactsManager {
|
|
|
34
34
|
private readonly CONTACTS_CACHE_KEY = 'metanet-contacts'
|
|
35
35
|
private readonly originator?: string
|
|
36
36
|
|
|
37
|
+
// Performance state — prevents thundering herd of concurrent contact loads and
|
|
38
|
+
// short-circuits the overlay path entirely when we've previously observed an
|
|
39
|
+
// empty contacts basket. Both are invalidated by saveContact/removeContact and
|
|
40
|
+
// by an explicit forceRefresh.
|
|
41
|
+
private inFlightLoad: Promise<Contact[]> | null = null
|
|
42
|
+
private knownEmpty = false
|
|
43
|
+
|
|
37
44
|
constructor (wallet?: WalletInterface, originator?: string) {
|
|
38
45
|
this.wallet = wallet ?? new WalletClient()
|
|
39
46
|
this.originator = originator
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
|
-
* Load all records from the contacts basket
|
|
50
|
+
* Load all records from the contacts basket.
|
|
51
|
+
*
|
|
52
|
+
* Concurrent calls share a single in-flight load (no thundering herd). After
|
|
53
|
+
* the basket has been observed empty once, subsequent calls return `[]`
|
|
54
|
+
* synchronously without hitting the wallet — until `forceRefresh` is passed
|
|
55
|
+
* or a contact is saved/removed.
|
|
56
|
+
*
|
|
44
57
|
* @param identityKey Optional specific identity key to fetch
|
|
45
58
|
* @param forceRefresh Whether to force a check for new contact data
|
|
46
59
|
* @param limit Maximum number of contacts to return
|
|
47
|
-
* @returns A promise that resolves with an array of contacts
|
|
48
60
|
*/
|
|
49
61
|
async getContacts (identityKey?: PubKeyHex, forceRefresh = false, limit = 1000): Promise<Contact[]> {
|
|
50
|
-
|
|
62
|
+
if (forceRefresh) this.invalidate()
|
|
63
|
+
|
|
64
|
+
if (this.knownEmpty) return []
|
|
65
|
+
|
|
51
66
|
if (!forceRefresh) {
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
try {
|
|
55
|
-
const cachedContacts: Contact[] = JSON.parse(cached)
|
|
56
|
-
return identityKey != null
|
|
57
|
-
? cachedContacts.filter(c => c.identityKey === identityKey)
|
|
58
|
-
: cachedContacts
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.warn('Invalid cached contacts JSON; will reload from chain', e)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
67
|
+
const fromCache = this.loadCachedContacts(identityKey)
|
|
68
|
+
if (fromCache !== null) return fromCache
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}, this.originator)
|
|
74
|
-
tags.push(`identityKey ${Utils.toHex(hashedIdentityKey)}`)
|
|
75
|
-
}
|
|
71
|
+
// Coalesce concurrent loads onto a single Promise so a fan-out of N
|
|
72
|
+
// identity calls produces ONE listOutputs + decrypt batch, not N.
|
|
73
|
+
this.inFlightLoad ??= this.loadContactsFromWallet(limit).finally(() => {
|
|
74
|
+
this.inFlightLoad = null
|
|
75
|
+
})
|
|
76
|
+
const all = await this.inFlightLoad
|
|
77
|
+
return identityKey == null ? all : all.filter(c => c.identityKey === identityKey)
|
|
78
|
+
}
|
|
76
79
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
/** Reset cached state. Call after writes. */
|
|
81
|
+
private invalidate (): void {
|
|
82
|
+
this.cache.removeItem(this.CONTACTS_CACHE_KEY)
|
|
83
|
+
this.knownEmpty = false
|
|
84
|
+
this.inFlightLoad = null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Underlying wallet load — invoked at most once concurrently via `inFlightLoad`. */
|
|
88
|
+
private async loadContactsFromWallet (limit: number): Promise<Contact[]> {
|
|
89
|
+
// Always load the full basket so subsequent filters (by identityKey) hit cache.
|
|
90
|
+
// Tag filtering is reserved for explicit per-key write paths.
|
|
91
|
+
const outputs = await this.wallet.listOutputs(
|
|
92
|
+
{ basket: 'contacts', include: 'locking scripts', includeCustomInstructions: true, tags: [], limit },
|
|
93
|
+
this.originator
|
|
94
|
+
)
|
|
85
95
|
|
|
86
96
|
if (outputs.outputs == null || outputs.outputs.length === 0) {
|
|
87
97
|
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify([]))
|
|
98
|
+
this.knownEmpty = true
|
|
88
99
|
return []
|
|
89
100
|
}
|
|
90
101
|
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
const contacts = await this.decryptContactOutputs(outputs.outputs)
|
|
103
|
+
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(contacts))
|
|
104
|
+
return contacts
|
|
105
|
+
}
|
|
93
106
|
|
|
94
|
-
|
|
107
|
+
/** Returns cached contacts (optionally filtered) or null if cache is missing/invalid. */
|
|
108
|
+
private loadCachedContacts (identityKey?: PubKeyHex): Contact[] | null {
|
|
109
|
+
const cached = this.cache.getItem(this.CONTACTS_CACHE_KEY)
|
|
110
|
+
if (cached == null || cached === '') return null
|
|
111
|
+
try {
|
|
112
|
+
const cachedContacts: Contact[] = JSON.parse(cached)
|
|
113
|
+
return identityKey != null ? cachedContacts.filter(c => c.identityKey === identityKey) : cachedContacts
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.warn('Invalid cached contacts JSON; will reload from chain', e)
|
|
116
|
+
return null
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Builds the HMAC-based identity-key tag array; empty array if no identity key is given. */
|
|
121
|
+
private async buildIdentityKeyTags (identityKey?: PubKeyHex): Promise<string[]> {
|
|
122
|
+
if (identityKey == null) return []
|
|
123
|
+
const { hmac: hashedIdentityKey } = await this.wallet.createHmac({
|
|
124
|
+
protocolID: CONTACT_PROTOCOL_ID,
|
|
125
|
+
keyID: identityKey,
|
|
126
|
+
counterparty: 'self',
|
|
127
|
+
data: Utils.toArray(identityKey, 'utf8')
|
|
128
|
+
}, this.originator)
|
|
129
|
+
return [`identityKey ${Utils.toHex(hashedIdentityKey)}`]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Decodes and decrypts all contact outputs in parallel, returning valid Contact objects. */
|
|
133
|
+
private async decryptContactOutputs (
|
|
134
|
+
rawOutputs: Awaited<ReturnType<WalletInterface['listOutputs']>>['outputs']
|
|
135
|
+
): Promise<Contact[]> {
|
|
136
|
+
const decryptTasks: Array<{ keyID: string, ciphertext: number[] }> = []
|
|
137
|
+
for (const output of rawOutputs) {
|
|
95
138
|
try {
|
|
96
|
-
if (output.lockingScript == null) continue
|
|
139
|
+
if (output.lockingScript == null || output.customInstructions == null) continue
|
|
97
140
|
const decoded = PushDrop.decode(LockingScript.fromHex(output.lockingScript))
|
|
98
|
-
if (output.customInstructions == null) continue
|
|
99
141
|
const keyID = JSON.parse(output.customInstructions).keyID
|
|
100
142
|
decryptTasks.push({ keyID, ciphertext: decoded.fields[0] })
|
|
101
143
|
} catch (error) {
|
|
@@ -103,25 +145,17 @@ export class ContactsManager {
|
|
|
103
145
|
}
|
|
104
146
|
}
|
|
105
147
|
|
|
106
|
-
// Decrypt all contacts in parallel — each call is a network round-trip over localhost
|
|
107
148
|
const decryptResults = await Promise.allSettled(
|
|
108
149
|
decryptTasks.map(async task =>
|
|
109
|
-
await this.wallet.decrypt({
|
|
110
|
-
ciphertext: task.ciphertext,
|
|
111
|
-
protocolID: CONTACT_PROTOCOL_ID,
|
|
112
|
-
keyID: task.keyID,
|
|
113
|
-
counterparty: 'self'
|
|
114
|
-
}, this.originator)
|
|
150
|
+
await this.wallet.decrypt({ ciphertext: task.ciphertext, protocolID: CONTACT_PROTOCOL_ID, keyID: task.keyID, counterparty: 'self' }, this.originator)
|
|
115
151
|
)
|
|
116
152
|
)
|
|
117
153
|
|
|
118
154
|
const contacts: Contact[] = []
|
|
119
|
-
for (
|
|
120
|
-
const result = decryptResults[i]
|
|
155
|
+
for (const result of decryptResults) {
|
|
121
156
|
if (result.status === 'fulfilled') {
|
|
122
157
|
try {
|
|
123
|
-
|
|
124
|
-
contacts.push(contactData)
|
|
158
|
+
contacts.push(JSON.parse(Utils.toUTF8(result.value.plaintext)) as Contact)
|
|
125
159
|
} catch (error) {
|
|
126
160
|
console.warn('ContactsManager: Failed to parse contact data:', error)
|
|
127
161
|
}
|
|
@@ -129,13 +163,7 @@ export class ContactsManager {
|
|
|
129
163
|
console.warn('ContactsManager: Failed to decrypt contact output:', result.reason)
|
|
130
164
|
}
|
|
131
165
|
}
|
|
132
|
-
|
|
133
|
-
// Cache the loaded contacts
|
|
134
|
-
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(contacts))
|
|
135
|
-
const filteredContacts = identityKey != null
|
|
136
|
-
? contacts.filter(c => c.identityKey === identityKey)
|
|
137
|
-
: contacts
|
|
138
|
-
return filteredContacts
|
|
166
|
+
return contacts
|
|
139
167
|
}
|
|
140
168
|
|
|
141
169
|
/**
|
|
@@ -144,152 +172,125 @@ export class ContactsManager {
|
|
|
144
172
|
* @param metadata Optional metadata to store with the contact (ex. notes, aliases, etc)
|
|
145
173
|
*/
|
|
146
174
|
async saveContact (contact: DisplayableIdentity, metadata?: Record<string, any>): Promise<void> {
|
|
147
|
-
// Get current contacts from cache or blockchain
|
|
148
175
|
const cached = this.cache.getItem(this.CONTACTS_CACHE_KEY)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
contacts = JSON.parse(cached)
|
|
152
|
-
} else {
|
|
153
|
-
// If cache is empty, get current data from blockchain
|
|
154
|
-
contacts = await this.getContacts()
|
|
155
|
-
}
|
|
156
|
-
|
|
176
|
+
const contacts: Contact[] = (cached != null && cached !== '') ? JSON.parse(cached) : await this.getContacts()
|
|
177
|
+
const contactToStore: Contact = { ...contact, metadata }
|
|
157
178
|
const existingIndex = contacts.findIndex(c => c.identityKey === contact.identityKey)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
179
|
+
if (existingIndex >= 0) contacts[existingIndex] = contactToStore
|
|
180
|
+
else contacts.push(contactToStore)
|
|
181
|
+
|
|
182
|
+
const hashedIdentityKey = await this.hashIdentityKey(contact.identityKey)
|
|
183
|
+
const outputs = await this.wallet.listOutputs({
|
|
184
|
+
basket: 'contacts', include: 'entire transactions', includeCustomInstructions: true,
|
|
185
|
+
tags: [`identityKey ${Utils.toHex(hashedIdentityKey)}`], limit: 100
|
|
186
|
+
}, this.originator)
|
|
162
187
|
|
|
163
|
-
|
|
164
|
-
|
|
188
|
+
const { existingOutput, keyID } = await this.findExistingOutput(outputs, contact.identityKey)
|
|
189
|
+
const lockingScript = await this.encryptAndLock(contactToStore, keyID)
|
|
190
|
+
|
|
191
|
+
if (existingOutput != null) {
|
|
192
|
+
await this.updateContactOutput(outputs, existingOutput, lockingScript, keyID, hashedIdentityKey, contact)
|
|
165
193
|
} else {
|
|
166
|
-
|
|
194
|
+
await this.createContactOutput(lockingScript, keyID, hashedIdentityKey, contact)
|
|
167
195
|
}
|
|
196
|
+
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(contacts))
|
|
197
|
+
this.knownEmpty = false
|
|
198
|
+
this.inFlightLoad = null
|
|
199
|
+
}
|
|
168
200
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
counterparty: 'self',
|
|
173
|
-
data: Utils.toArray(
|
|
174
|
-
}, this.originator)
|
|
175
|
-
|
|
176
|
-
// Check if this contact already exists (to update it)
|
|
177
|
-
const outputs = await this.wallet.listOutputs({
|
|
178
|
-
basket: 'contacts',
|
|
179
|
-
include: 'entire transactions',
|
|
180
|
-
includeCustomInstructions: true,
|
|
181
|
-
tags: [`identityKey ${Utils.toHex(hashedIdentityKey)}`],
|
|
182
|
-
limit: 100 // Should only be one contact!
|
|
201
|
+
/** Computes the HMAC-based hash of an identity key for tag indexing. */
|
|
202
|
+
private async hashIdentityKey (identityKey: string): Promise<number[]> {
|
|
203
|
+
const { hmac } = await this.wallet.createHmac({
|
|
204
|
+
protocolID: CONTACT_PROTOCOL_ID, keyID: identityKey, counterparty: 'self',
|
|
205
|
+
data: Utils.toArray(identityKey, 'utf8')
|
|
183
206
|
}, this.originator)
|
|
207
|
+
return hmac
|
|
208
|
+
}
|
|
184
209
|
|
|
210
|
+
/** Scans existing outputs to find the one matching the given identity key; returns output + keyID. */
|
|
211
|
+
private async findExistingOutput (
|
|
212
|
+
outputs: Awaited<ReturnType<WalletInterface['listOutputs']>>,
|
|
213
|
+
identityKey: string
|
|
214
|
+
): Promise<{ existingOutput: any, keyID: string }> {
|
|
185
215
|
let existingOutput: any = null
|
|
186
216
|
let keyID = Utils.toBase64(Random(32))
|
|
187
|
-
if (outputs.outputs
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
counterparty: 'self'
|
|
202
|
-
}, this.originator)
|
|
203
|
-
|
|
204
|
-
const storedContact: Contact = JSON.parse(Utils.toUTF8(plaintext))
|
|
205
|
-
if (storedContact.identityKey === contact.identityKey) {
|
|
206
|
-
// Found the right output
|
|
207
|
-
existingOutput = output
|
|
208
|
-
break
|
|
209
|
-
}
|
|
210
|
-
} catch (e) {
|
|
211
|
-
// Skip malformed or undecryptable outputs
|
|
212
|
-
}
|
|
213
|
-
}
|
|
217
|
+
if (outputs.outputs == null) return { existingOutput, keyID }
|
|
218
|
+
for (const output of outputs.outputs) {
|
|
219
|
+
try {
|
|
220
|
+
const [txid, outputIndex] = output.outpoint.split('.')
|
|
221
|
+
const tx = Transaction.fromBEEF(outputs.BEEF as number[], txid)
|
|
222
|
+
const decoded = PushDrop.decode(tx.outputs[Number(outputIndex)].lockingScript)
|
|
223
|
+
if (output.customInstructions == null) continue
|
|
224
|
+
keyID = JSON.parse(output.customInstructions).keyID
|
|
225
|
+
const { plaintext } = await this.wallet.decrypt(
|
|
226
|
+
{ ciphertext: decoded.fields[0], protocolID: CONTACT_PROTOCOL_ID, keyID, counterparty: 'self' }, this.originator
|
|
227
|
+
)
|
|
228
|
+
const storedContact: Contact = JSON.parse(Utils.toUTF8(plaintext))
|
|
229
|
+
if (storedContact.identityKey === identityKey) { existingOutput = output; break }
|
|
230
|
+
} catch (_malformedOrUndecryptableOutput) { /* skip */ }
|
|
214
231
|
}
|
|
232
|
+
return { existingOutput, keyID }
|
|
233
|
+
}
|
|
215
234
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
...contact,
|
|
219
|
-
metadata
|
|
220
|
-
}
|
|
235
|
+
/** Encrypts a contact and produces its PushDrop locking script. */
|
|
236
|
+
private async encryptAndLock (contactData: Contact, keyID: string): Promise<LockingScript> {
|
|
221
237
|
const { ciphertext } = await this.wallet.encrypt({
|
|
222
|
-
plaintext: Utils.toArray(JSON.stringify(
|
|
223
|
-
protocolID: CONTACT_PROTOCOL_ID,
|
|
224
|
-
keyID,
|
|
225
|
-
counterparty: 'self'
|
|
238
|
+
plaintext: Utils.toArray(JSON.stringify(contactData), 'utf8'),
|
|
239
|
+
protocolID: CONTACT_PROTOCOL_ID, keyID, counterparty: 'self'
|
|
226
240
|
}, this.originator)
|
|
241
|
+
return await new PushDrop(this.wallet, this.originator).lock([ciphertext], CONTACT_PROTOCOL_ID, keyID, 'self')
|
|
242
|
+
}
|
|
227
243
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if (signableTransaction == null) throw new Error('Unable to update contact')
|
|
262
|
-
|
|
263
|
-
const unlocker = pushdrop.unlock(CONTACT_PROTOCOL_ID, keyID, 'self')
|
|
264
|
-
const unlockingScript = await unlocker.sign(
|
|
265
|
-
Transaction.fromBEEF(signableTransaction.tx),
|
|
266
|
-
0
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
const { tx } = await this.wallet.signAction({
|
|
270
|
-
reference: signableTransaction.reference,
|
|
271
|
-
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
272
|
-
}, this.originator)
|
|
244
|
+
/** Spends an existing contact output and creates a replacement with updated data. */
|
|
245
|
+
private async updateContactOutput (
|
|
246
|
+
outputs: Awaited<ReturnType<WalletInterface['listOutputs']>>,
|
|
247
|
+
existingOutput: any,
|
|
248
|
+
lockingScript: LockingScript,
|
|
249
|
+
keyID: string,
|
|
250
|
+
hashedIdentityKey: number[],
|
|
251
|
+
contact: DisplayableIdentity
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
const [txid, outputIndex] = String(existingOutput.outpoint).split('.')
|
|
254
|
+
const prevOutpoint = `${txid}.${outputIndex}` as const
|
|
255
|
+
const pushdrop = new PushDrop(this.wallet, this.originator)
|
|
256
|
+
const { signableTransaction } = await this.wallet.createAction({
|
|
257
|
+
description: 'Update Contact',
|
|
258
|
+
inputBEEF: outputs.BEEF as number[],
|
|
259
|
+
inputs: [{ outpoint: prevOutpoint, unlockingScriptLength: 74, inputDescription: 'Spend previous contact output' }],
|
|
260
|
+
outputs: [{
|
|
261
|
+
basket: 'contacts', satoshis: 1, lockingScript: lockingScript.toHex(),
|
|
262
|
+
outputDescription: `Updated Contact: ${contact.name ?? contact.identityKey.slice(0, 10)}`,
|
|
263
|
+
tags: [`identityKey ${Utils.toHex(hashedIdentityKey)}`], customInstructions: JSON.stringify({ keyID })
|
|
264
|
+
}],
|
|
265
|
+
options: { acceptDelayedBroadcast: false, randomizeOutputs: false }
|
|
266
|
+
}, this.originator)
|
|
267
|
+
if (signableTransaction == null) throw new Error('Unable to update contact')
|
|
268
|
+
const unlockingScript = await pushdrop.unlock(CONTACT_PROTOCOL_ID, keyID, 'self')
|
|
269
|
+
.sign(Transaction.fromBEEF(signableTransaction.tx), 0)
|
|
270
|
+
const { tx } = await this.wallet.signAction({
|
|
271
|
+
reference: signableTransaction.reference,
|
|
272
|
+
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
273
|
+
}, this.originator)
|
|
274
|
+
if (tx == null) throw new Error('Failed to update contact output')
|
|
275
|
+
}
|
|
273
276
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}],
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(contacts))
|
|
277
|
+
/** Creates a new on-chain contact output. */
|
|
278
|
+
private async createContactOutput (
|
|
279
|
+
lockingScript: LockingScript,
|
|
280
|
+
keyID: string,
|
|
281
|
+
hashedIdentityKey: number[],
|
|
282
|
+
contact: DisplayableIdentity
|
|
283
|
+
): Promise<void> {
|
|
284
|
+
const { tx } = await this.wallet.createAction({
|
|
285
|
+
description: 'Add Contact',
|
|
286
|
+
outputs: [{
|
|
287
|
+
basket: 'contacts', satoshis: 1, lockingScript: lockingScript.toHex(),
|
|
288
|
+
outputDescription: `Contact: ${contact.name ?? contact.identityKey.slice(0, 10)}`,
|
|
289
|
+
tags: [`identityKey ${Utils.toHex(hashedIdentityKey)}`], customInstructions: JSON.stringify({ keyID })
|
|
290
|
+
}],
|
|
291
|
+
options: { acceptDelayedBroadcast: false, randomizeOutputs: false }
|
|
292
|
+
}, this.originator)
|
|
293
|
+
if (tx == null) throw new Error('Failed to create contact output')
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
/**
|
|
@@ -302,87 +303,64 @@ export class ContactsManager {
|
|
|
302
303
|
if (cached != null && cached !== '') {
|
|
303
304
|
try {
|
|
304
305
|
const contacts: Contact[] = JSON.parse(cached)
|
|
305
|
-
const
|
|
306
|
-
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(
|
|
306
|
+
const remaining = contacts.filter(c => c.identityKey !== identityKey)
|
|
307
|
+
this.cache.setItem(this.CONTACTS_CACHE_KEY, JSON.stringify(remaining))
|
|
308
|
+
this.knownEmpty = remaining.length === 0
|
|
307
309
|
} catch (e) {
|
|
308
310
|
console.warn('Failed to update cache after contact removal:', e)
|
|
309
311
|
}
|
|
310
312
|
}
|
|
313
|
+
this.inFlightLoad = null
|
|
311
314
|
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
counterparty: 'self',
|
|
318
|
-
data: Utils.toArray(identityKey, 'utf8')
|
|
319
|
-
}, this.originator)
|
|
320
|
-
tags.push(`identityKey ${Utils.toHex(hashedIdentityKey)}`)
|
|
321
|
-
|
|
322
|
-
// Find and spend the contact's output
|
|
323
|
-
const outputs = await this.wallet.listOutputs({
|
|
324
|
-
basket: 'contacts',
|
|
325
|
-
include: 'entire transactions',
|
|
326
|
-
includeCustomInstructions: true,
|
|
327
|
-
tags,
|
|
328
|
-
limit: 100 // Should only be one contact!
|
|
329
|
-
}, this.originator)
|
|
330
|
-
|
|
315
|
+
const tags = await this.buildIdentityKeyTags(identityKey)
|
|
316
|
+
const outputs = await this.wallet.listOutputs(
|
|
317
|
+
{ basket: 'contacts', include: 'entire transactions', includeCustomInstructions: true, tags, limit: 100 },
|
|
318
|
+
this.originator
|
|
319
|
+
)
|
|
331
320
|
if (outputs.outputs == null) return
|
|
332
321
|
|
|
333
|
-
// Find the output for this specific contact by decrypting and checking identityKey
|
|
334
322
|
for (const output of outputs.outputs) {
|
|
335
323
|
try {
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if (output.customInstructions == null) continue
|
|
340
|
-
const keyID = JSON.parse(output.customInstructions).keyID
|
|
341
|
-
|
|
342
|
-
const { plaintext } = await this.wallet.decrypt({
|
|
343
|
-
ciphertext: decoded.fields[0],
|
|
344
|
-
protocolID: CONTACT_PROTOCOL_ID,
|
|
345
|
-
keyID,
|
|
346
|
-
counterparty: 'self'
|
|
347
|
-
}, this.originator)
|
|
348
|
-
|
|
349
|
-
const storedContact: Contact = JSON.parse(Utils.toUTF8(plaintext))
|
|
350
|
-
if (storedContact.identityKey === identityKey) {
|
|
351
|
-
// Found the contact's output, spend it without creating a new one
|
|
352
|
-
const prevOutpoint = `${txid}.${outputIndex}` as const
|
|
353
|
-
|
|
354
|
-
const pushdrop = new PushDrop(this.wallet, this.originator)
|
|
355
|
-
const { signableTransaction } = await this.wallet.createAction({
|
|
356
|
-
description: 'Delete Contact',
|
|
357
|
-
inputBEEF: outputs.BEEF as number[],
|
|
358
|
-
inputs: [{
|
|
359
|
-
outpoint: prevOutpoint,
|
|
360
|
-
unlockingScriptLength: 74,
|
|
361
|
-
inputDescription: 'Spend contact output to delete'
|
|
362
|
-
}],
|
|
363
|
-
outputs: [], // No outputs = deletion
|
|
364
|
-
options: { acceptDelayedBroadcast: false, randomizeOutputs: false } // TODO: Support custom config as needed.
|
|
365
|
-
}, this.originator)
|
|
366
|
-
|
|
367
|
-
if (signableTransaction == null) throw new Error('Unable to delete contact')
|
|
368
|
-
|
|
369
|
-
const unlocker = pushdrop.unlock(CONTACT_PROTOCOL_ID, keyID, 'self')
|
|
370
|
-
const unlockingScript = await unlocker.sign(
|
|
371
|
-
Transaction.fromBEEF(signableTransaction.tx),
|
|
372
|
-
0
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
const { tx: deleteTx } = await this.wallet.signAction({
|
|
376
|
-
reference: signableTransaction.reference,
|
|
377
|
-
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
378
|
-
}, this.originator)
|
|
379
|
-
|
|
380
|
-
if (deleteTx == null) throw new Error('Failed to delete contact output')
|
|
381
|
-
return
|
|
382
|
-
}
|
|
383
|
-
} catch (e) {
|
|
384
|
-
// Skip malformed or undecryptable outputs
|
|
385
|
-
}
|
|
324
|
+
const spent = await this.trySpendContactOutput(output, outputs, identityKey)
|
|
325
|
+
if (spent) return
|
|
326
|
+
} catch (_malformedOrUndecryptableOutput) { /* skip */ }
|
|
386
327
|
}
|
|
387
328
|
}
|
|
329
|
+
|
|
330
|
+
/** Attempts to decrypt and spend a single output if it matches the given identity key. Returns true if spent. */
|
|
331
|
+
private async trySpendContactOutput (
|
|
332
|
+
output: Awaited<ReturnType<WalletInterface['listOutputs']>>['outputs'][number],
|
|
333
|
+
outputs: Awaited<ReturnType<WalletInterface['listOutputs']>>,
|
|
334
|
+
identityKey: string
|
|
335
|
+
): Promise<boolean> {
|
|
336
|
+
const [txid, outputIndex] = String(output.outpoint).split('.')
|
|
337
|
+
const tx = Transaction.fromBEEF(outputs.BEEF as number[], txid)
|
|
338
|
+
const decoded = PushDrop.decode(tx.outputs[Number(outputIndex)].lockingScript)
|
|
339
|
+
if (output.customInstructions == null) return false
|
|
340
|
+
const keyID = JSON.parse(output.customInstructions).keyID
|
|
341
|
+
const { plaintext } = await this.wallet.decrypt(
|
|
342
|
+
{ ciphertext: decoded.fields[0], protocolID: CONTACT_PROTOCOL_ID, keyID, counterparty: 'self' }, this.originator
|
|
343
|
+
)
|
|
344
|
+
const storedContact: Contact = JSON.parse(Utils.toUTF8(plaintext))
|
|
345
|
+
if (storedContact.identityKey !== identityKey) return false
|
|
346
|
+
|
|
347
|
+
const prevOutpoint = `${txid}.${outputIndex}` as const
|
|
348
|
+
const pushdrop = new PushDrop(this.wallet, this.originator)
|
|
349
|
+
const { signableTransaction } = await this.wallet.createAction({
|
|
350
|
+
description: 'Delete Contact',
|
|
351
|
+
inputBEEF: outputs.BEEF as number[],
|
|
352
|
+
inputs: [{ outpoint: prevOutpoint, unlockingScriptLength: 74, inputDescription: 'Spend contact output to delete' }],
|
|
353
|
+
outputs: [],
|
|
354
|
+
options: { acceptDelayedBroadcast: false, randomizeOutputs: false }
|
|
355
|
+
}, this.originator)
|
|
356
|
+
if (signableTransaction == null) throw new Error('Unable to delete contact')
|
|
357
|
+
const unlockingScript = await pushdrop.unlock(CONTACT_PROTOCOL_ID, keyID, 'self')
|
|
358
|
+
.sign(Transaction.fromBEEF(signableTransaction.tx), 0)
|
|
359
|
+
const { tx: deleteTx } = await this.wallet.signAction({
|
|
360
|
+
reference: signableTransaction.reference,
|
|
361
|
+
spends: { 0: { unlockingScript: unlockingScript.toHex() } }
|
|
362
|
+
}, this.originator)
|
|
363
|
+
if (deleteTx == null) throw new Error('Failed to delete contact output')
|
|
364
|
+
return true
|
|
365
|
+
}
|
|
388
366
|
}
|