@bsv/sdk 1.1.33 → 1.2.1
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/dist/cjs/mod.js +4 -0
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +4 -3
- package/dist/cjs/src/auth/Certificate.js +163 -0
- package/dist/cjs/src/auth/Certificate.js.map +1 -0
- package/dist/cjs/src/auth/index.js +9 -0
- package/dist/cjs/src/auth/index.js.map +1 -0
- package/dist/cjs/src/compat/BSM.js +17 -7
- package/dist/cjs/src/compat/BSM.js.map +1 -1
- package/dist/cjs/src/compat/ECIES.js +17 -7
- package/dist/cjs/src/compat/ECIES.js.map +1 -1
- package/dist/cjs/src/compat/HD.js +17 -7
- package/dist/cjs/src/compat/HD.js.map +1 -1
- package/dist/cjs/src/compat/Mnemonic.js +17 -7
- package/dist/cjs/src/compat/Mnemonic.js.map +1 -1
- package/dist/cjs/src/compat/index.js +17 -7
- package/dist/cjs/src/compat/index.js.map +1 -1
- package/dist/cjs/src/messages/index.js +17 -7
- package/dist/cjs/src/messages/index.js.map +1 -1
- package/dist/cjs/src/overlay-tools/LookupResolver.js +170 -0
- package/dist/cjs/src/overlay-tools/LookupResolver.js.map +1 -0
- package/dist/cjs/src/overlay-tools/OverlayAdminTokenTemplate.js +69 -0
- package/dist/cjs/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -0
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js +336 -0
- package/dist/cjs/src/overlay-tools/SHIPBroadcaster.js.map +1 -0
- package/dist/cjs/src/overlay-tools/index.js +29 -0
- package/dist/cjs/src/overlay-tools/index.js.map +1 -0
- package/dist/cjs/src/primitives/PrivateKey.js +17 -7
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/primitives/TransactionSignature.js +17 -7
- package/dist/cjs/src/primitives/TransactionSignature.js.map +1 -1
- package/dist/cjs/src/primitives/index.js +17 -7
- package/dist/cjs/src/primitives/index.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +17 -7
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/script/templates/PushDrop.js +218 -0
- package/dist/cjs/src/script/templates/PushDrop.js.map +1 -0
- package/dist/cjs/src/script/templates/index.js +3 -1
- package/dist/cjs/src/script/templates/index.js.map +1 -1
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js +1 -1
- package/dist/cjs/src/transaction/http/DefaultHttpClient.js.map +1 -1
- package/dist/cjs/src/wallet/CachedKeyDeriver.js +177 -0
- package/dist/cjs/src/wallet/CachedKeyDeriver.js.map +1 -0
- package/dist/cjs/src/wallet/KeyDeriver.js +174 -0
- package/dist/cjs/src/wallet/KeyDeriver.js.map +1 -0
- package/dist/cjs/src/wallet/ProtoWallet.js +245 -0
- package/dist/cjs/src/wallet/ProtoWallet.js.map +1 -0
- package/dist/cjs/src/wallet/Wallet.interfaces.js +3 -0
- package/dist/cjs/src/wallet/Wallet.interfaces.js.map +1 -0
- package/dist/cjs/src/wallet/WalletClient.js +181 -0
- package/dist/cjs/src/wallet/WalletClient.js.map +1 -0
- package/dist/cjs/src/wallet/WalletError.js +28 -0
- package/dist/cjs/src/wallet/WalletError.js.map +1 -0
- package/dist/cjs/src/wallet/index.js +34 -0
- package/dist/cjs/src/wallet/index.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js +45 -0
- package/dist/cjs/src/wallet/substrates/HTTPWalletWire.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/WalletWire.js +3 -0
- package/dist/cjs/src/wallet/substrates/WalletWire.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/WalletWireCalls.js +36 -0
- package/dist/cjs/src/wallet/substrates/WalletWireCalls.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js +1821 -0
- package/dist/cjs/src/wallet/substrates/WalletWireProcessor.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js +1305 -0
- package/dist/cjs/src/wallet/substrates/WalletWireTransceiver.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/XDM.js +130 -0
- package/dist/cjs/src/wallet/substrates/XDM.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/index.js +33 -0
- package/dist/cjs/src/wallet/substrates/index.js.map +1 -0
- package/dist/cjs/src/wallet/substrates/window.CWI.js +102 -0
- package/dist/cjs/src/wallet/substrates/window.CWI.js.map +1 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/mod.js +4 -0
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/auth/Certificate.js +185 -0
- package/dist/esm/src/auth/Certificate.js.map +1 -0
- package/dist/esm/src/auth/index.js +2 -0
- package/dist/esm/src/auth/index.js.map +1 -0
- package/dist/esm/src/overlay-tools/LookupResolver.js +167 -0
- package/dist/esm/src/overlay-tools/LookupResolver.js.map +1 -0
- package/dist/esm/src/overlay-tools/OverlayAdminTokenTemplate.js +64 -0
- package/dist/esm/src/overlay-tools/OverlayAdminTokenTemplate.js.map +1 -0
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js +335 -0
- package/dist/esm/src/overlay-tools/SHIPBroadcaster.js.map +1 -0
- package/dist/esm/src/overlay-tools/index.js +6 -0
- package/dist/esm/src/overlay-tools/index.js.map +1 -0
- package/dist/esm/src/script/templates/PushDrop.js +215 -0
- package/dist/esm/src/script/templates/PushDrop.js.map +1 -0
- package/dist/esm/src/script/templates/index.js +1 -0
- package/dist/esm/src/script/templates/index.js.map +1 -1
- package/dist/esm/src/transaction/http/DefaultHttpClient.js +1 -1
- package/dist/esm/src/transaction/http/DefaultHttpClient.js.map +1 -1
- package/dist/esm/src/wallet/CachedKeyDeriver.js +174 -0
- package/dist/esm/src/wallet/CachedKeyDeriver.js.map +1 -0
- package/dist/esm/src/wallet/KeyDeriver.js +172 -0
- package/dist/esm/src/wallet/KeyDeriver.js.map +1 -0
- package/dist/esm/src/wallet/ProtoWallet.js +207 -0
- package/dist/esm/src/wallet/ProtoWallet.js.map +1 -0
- package/dist/esm/src/wallet/Wallet.interfaces.js +2 -0
- package/dist/esm/src/wallet/Wallet.interfaces.js.map +1 -0
- package/dist/esm/src/wallet/WalletClient.js +177 -0
- package/dist/esm/src/wallet/WalletClient.js.map +1 -0
- package/dist/esm/src/wallet/WalletError.js +25 -0
- package/dist/esm/src/wallet/WalletError.js.map +1 -0
- package/dist/esm/src/wallet/index.js +9 -0
- package/dist/esm/src/wallet/index.js.map +1 -0
- package/dist/esm/src/wallet/substrates/HTTPWalletWire.js +42 -0
- package/dist/esm/src/wallet/substrates/HTTPWalletWire.js.map +1 -0
- package/dist/esm/src/wallet/substrates/WalletWire.js +2 -0
- package/dist/esm/src/wallet/substrates/WalletWire.js.map +1 -0
- package/dist/esm/src/wallet/substrates/WalletWireCalls.js +34 -0
- package/dist/esm/src/wallet/substrates/WalletWireCalls.js.map +1 -0
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js +1816 -0
- package/dist/esm/src/wallet/substrates/WalletWireProcessor.js.map +1 -0
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js +1300 -0
- package/dist/esm/src/wallet/substrates/WalletWireTransceiver.js.map +1 -0
- package/dist/esm/src/wallet/substrates/XDM.js +128 -0
- package/dist/esm/src/wallet/substrates/XDM.js.map +1 -0
- package/dist/esm/src/wallet/substrates/index.js +8 -0
- package/dist/esm/src/wallet/substrates/index.js.map +1 -0
- package/dist/esm/src/wallet/substrates/window.CWI.js +100 -0
- package/dist/esm/src/wallet/substrates/window.CWI.js.map +1 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/mod.d.ts +4 -0
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/auth/Certificate.d.ts +76 -0
- package/dist/types/src/auth/Certificate.d.ts.map +1 -0
- package/dist/types/src/auth/index.d.ts +2 -0
- package/dist/types/src/auth/index.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/LookupResolver.d.ts +71 -0
- package/dist/types/src/overlay-tools/LookupResolver.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/OverlayAdminTokenTemplate.d.ts +44 -0
- package/dist/types/src/overlay-tools/OverlayAdminTokenTemplate.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts +90 -0
- package/dist/types/src/overlay-tools/SHIPBroadcaster.d.ts.map +1 -0
- package/dist/types/src/overlay-tools/index.d.ts +6 -0
- package/dist/types/src/overlay-tools/index.d.ts.map +1 -0
- package/dist/types/src/script/templates/PushDrop.d.ts +53 -0
- package/dist/types/src/script/templates/PushDrop.d.ts.map +1 -0
- package/dist/types/src/script/templates/index.d.ts +1 -0
- package/dist/types/src/script/templates/index.d.ts.map +1 -1
- package/dist/types/src/wallet/CachedKeyDeriver.d.ts +92 -0
- package/dist/types/src/wallet/CachedKeyDeriver.d.ts.map +1 -0
- package/dist/types/src/wallet/KeyDeriver.d.ts +72 -0
- package/dist/types/src/wallet/KeyDeriver.d.ts.map +1 -0
- package/dist/types/src/wallet/ProtoWallet.d.ts +415 -0
- package/dist/types/src/wallet/ProtoWallet.d.ts.map +1 -0
- package/dist/types/src/wallet/Wallet.interfaces.d.ts +996 -0
- package/dist/types/src/wallet/Wallet.interfaces.d.ts.map +1 -0
- package/dist/types/src/wallet/WalletClient.d.ts +182 -0
- package/dist/types/src/wallet/WalletClient.d.ts.map +1 -0
- package/dist/types/src/wallet/WalletError.d.ts +14 -0
- package/dist/types/src/wallet/WalletError.d.ts.map +1 -0
- package/dist/types/src/wallet/index.d.ts +9 -0
- package/dist/types/src/wallet/index.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/HTTPWalletWire.d.ts +9 -0
- package/dist/types/src/wallet/substrates/HTTPWalletWire.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/WalletWire.d.ts +7 -0
- package/dist/types/src/wallet/substrates/WalletWire.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/WalletWireCalls.d.ts +33 -0
- package/dist/types/src/wallet/substrates/WalletWireCalls.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/WalletWireProcessor.d.ts +18 -0
- package/dist/types/src/wallet/substrates/WalletWireProcessor.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts +196 -0
- package/dist/types/src/wallet/substrates/WalletWireTransceiver.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/XDM.d.ts +412 -0
- package/dist/types/src/wallet/substrates/XDM.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/index.d.ts +8 -0
- package/dist/types/src/wallet/substrates/index.d.ts.map +1 -0
- package/dist/types/src/wallet/substrates/window.CWI.d.ts +410 -0
- package/dist/types/src/wallet/substrates/window.CWI.d.ts.map +1 -0
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/docs/overlay-tools.md +537 -0
- package/docs/script.md +135 -0
- package/docs/totp.md +119 -0
- package/docs/wallet-substrates.md +10 -0
- package/docs/wallet.md +3718 -0
- package/mod.ts +5 -1
- package/package.json +44 -3
- package/src/auth/Certificate.ts +233 -0
- package/src/auth/__tests/Certificate.test.ts +282 -0
- package/src/auth/index.ts +1 -0
- package/src/overlay-tools/LookupResolver.ts +228 -0
- package/src/overlay-tools/OverlayAdminTokenTemplate.ts +79 -0
- package/src/overlay-tools/SHIPBroadcaster.ts +405 -0
- package/src/overlay-tools/__tests/LookupResolver.test.ts +1403 -0
- package/src/overlay-tools/__tests/OverlayAdminTokenTemplate.test.ts +69 -0
- package/src/overlay-tools/__tests/SHIPBroadcaster.test.ts +904 -0
- package/src/overlay-tools/index.ts +5 -0
- package/src/script/templates/PushDrop.ts +246 -0
- package/src/script/templates/__tests/PushDrop.test.ts +158 -0
- package/src/script/templates/index.ts +1 -0
- package/src/transaction/http/DefaultHttpClient.ts +1 -1
- package/src/wallet/CachedKeyDeriver.ts +193 -0
- package/src/wallet/KeyDeriver.ts +178 -0
- package/src/wallet/ProtoWallet.ts +732 -0
- package/src/wallet/Wallet.interfaces.ts +1170 -0
- package/src/wallet/WalletClient.ts +201 -0
- package/src/wallet/WalletError.ts +27 -0
- package/src/wallet/__tests/CachedKeyDeriver.test.ts +322 -0
- package/src/wallet/__tests/KeyDeriver.test.ts +118 -0
- package/src/wallet/__tests/ProtoWallet.test.ts +543 -0
- package/src/wallet/index.ts +8 -0
- package/src/wallet/substrates/HTTPWalletWire.ts +47 -0
- package/src/wallet/substrates/WalletWire.ts +6 -0
- package/src/wallet/substrates/WalletWireCalls.ts +34 -0
- package/src/wallet/substrates/WalletWireProcessor.ts +2046 -0
- package/src/wallet/substrates/WalletWireTransceiver.ts +1454 -0
- package/src/wallet/substrates/XDM.ts +157 -0
- package/src/wallet/substrates/__tests/WalletWire.integration.test.ts +2194 -0
- package/src/wallet/substrates/__tests/XDM.test.ts +659 -0
- package/src/wallet/substrates/index.ts +7 -0
- package/src/wallet/substrates/window.CWI.ts +133 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { AcquireCertificateArgs, AcquireCertificateResult, Base64String, BasketStringUnder300Bytes, BEEF, BooleanDefaultFalse, BooleanDefaultTrue, Byte, CertificateFieldNameUnder50Bytes, CreateActionArgs, CreateActionResult, DescriptionString5to50Bytes, DiscoverCertificatesResult, EntityIconURLStringMax500Bytes, EntityNameStringMax100Bytes, HexString, InternalizeActionArgs, ISOTimestampString, KeyIDStringUnder800Bytes, LabelStringUnder300Bytes, ListActionsArgs, ListActionsResult, ListCertificatesResult, ListOutputsArgs, ListOutputsResult, OriginatorDomainNameStringUnder250Bytes, OutpointString, OutputTagStringUnder300Bytes, PositiveInteger, PositiveIntegerDefault10Max10000, PositiveIntegerMax10, PositiveIntegerOrZero, ProtocolString5To400Bytes, ProveCertificateArgs, ProveCertificateResult, PubKeyHex, SatoshiValue, SignActionArgs, SignActionResult, TXIDHexString, VersionString7To30Bytes, Wallet } from './Wallet.interfaces.js'
|
|
2
|
+
import WindowCWISubstrate from './substrates/window.CWI.js'
|
|
3
|
+
import XDMSubstrate from './substrates/XDM.js'
|
|
4
|
+
import WalletWireTransceiver from './substrates/WalletWireTransceiver.js'
|
|
5
|
+
import HTTPWalletWire from './substrates/HTTPWalletWire.js'
|
|
6
|
+
|
|
7
|
+
const MAX_XDM_RESPONSE_WAIT = 200
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The SDK is how applications communicate with wallets over a communications substrate.
|
|
11
|
+
*/
|
|
12
|
+
export default class WalletClient implements Wallet {
|
|
13
|
+
public substrate: 'auto' | Wallet
|
|
14
|
+
originator?: OriginatorDomainNameStringUnder250Bytes
|
|
15
|
+
constructor(substrate: 'auto' | 'Cicada' | 'XDM' | 'window.CWI' | Wallet = 'auto', originator?: OriginatorDomainNameStringUnder250Bytes) {
|
|
16
|
+
if (substrate === 'Cicada') substrate = new WalletWireTransceiver(new HTTPWalletWire(originator))
|
|
17
|
+
if (substrate === 'window.CWI') substrate = new WindowCWISubstrate()
|
|
18
|
+
if (substrate === 'XDM') substrate = new XDMSubstrate()
|
|
19
|
+
this.substrate = substrate
|
|
20
|
+
this.originator = originator
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async connectToSubstrate() {
|
|
24
|
+
if (typeof this.substrate === 'object') {
|
|
25
|
+
return // substrate is already connected
|
|
26
|
+
}
|
|
27
|
+
let sub: Wallet
|
|
28
|
+
const checkSub = async (timeout?: number) => {
|
|
29
|
+
let result
|
|
30
|
+
if (typeof timeout === 'number') {
|
|
31
|
+
result = await Promise.race([
|
|
32
|
+
sub.getVersion({}),
|
|
33
|
+
new Promise<never>((_, reject) => setTimeout(() => reject(new Error('Timed out.')), timeout))
|
|
34
|
+
])
|
|
35
|
+
} else {
|
|
36
|
+
result = await sub.getVersion({})
|
|
37
|
+
}
|
|
38
|
+
if (typeof result !== 'object' || typeof result.version !== 'string') {
|
|
39
|
+
throw new Error('Failed to use substrate.')
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
sub = new WindowCWISubstrate()
|
|
44
|
+
await checkSub()
|
|
45
|
+
this.substrate = sub
|
|
46
|
+
} catch (e) {
|
|
47
|
+
try {
|
|
48
|
+
sub = new XDMSubstrate()
|
|
49
|
+
await checkSub(MAX_XDM_RESPONSE_WAIT)
|
|
50
|
+
this.substrate = sub
|
|
51
|
+
} catch (e) {
|
|
52
|
+
try {
|
|
53
|
+
sub = new WalletWireTransceiver(new HTTPWalletWire(this.originator))
|
|
54
|
+
await checkSub()
|
|
55
|
+
this.substrate = sub
|
|
56
|
+
} catch (e) {
|
|
57
|
+
throw new Error('No wallet available over any communication substrate. Install a BSV wallet today!')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async createAction(args: CreateActionArgs): Promise<CreateActionResult> {
|
|
64
|
+
await this.connectToSubstrate()
|
|
65
|
+
return await (this.substrate as Wallet).createAction(args, this.originator)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async signAction(args: SignActionArgs): Promise<SignActionResult> {
|
|
69
|
+
await this.connectToSubstrate()
|
|
70
|
+
return await (this.substrate as Wallet).signAction(args, this.originator)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async abortAction(args: { reference: Base64String }): Promise<{ aborted: true }> {
|
|
74
|
+
await this.connectToSubstrate()
|
|
75
|
+
return await (this.substrate as Wallet).abortAction(args, this.originator)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async listActions(args: ListActionsArgs): Promise<ListActionsResult> {
|
|
79
|
+
await this.connectToSubstrate()
|
|
80
|
+
return await (this.substrate as Wallet).listActions(args, this.originator)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async internalizeAction(args: InternalizeActionArgs): Promise<{ accepted: true }> {
|
|
84
|
+
await this.connectToSubstrate()
|
|
85
|
+
return await (this.substrate as Wallet).internalizeAction(args, this.originator)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async listOutputs(args: ListOutputsArgs): Promise<ListOutputsResult> {
|
|
89
|
+
await this.connectToSubstrate()
|
|
90
|
+
return await (this.substrate as Wallet).listOutputs(args, this.originator)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async relinquishOutput(args: { basket: BasketStringUnder300Bytes, output: OutpointString }): Promise<{ relinquished: true }> {
|
|
94
|
+
await this.connectToSubstrate()
|
|
95
|
+
return await (this.substrate as Wallet).relinquishOutput(args, this.originator)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getPublicKey(args: { identityKey?: true, protocolID?: [0 | 1 | 2, ProtocolString5To400Bytes], keyID?: KeyIDStringUnder800Bytes, privileged?: BooleanDefaultFalse, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', forSelf?: BooleanDefaultFalse }): Promise<{ publicKey: PubKeyHex }> {
|
|
99
|
+
await this.connectToSubstrate()
|
|
100
|
+
return await (this.substrate as Wallet).getPublicKey(args, this.originator)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async revealCounterpartyKeyLinkage(args: { counterparty: PubKeyHex, verifier: PubKeyHex, privilegedReason?: DescriptionString5to50Bytes, privileged?: BooleanDefaultFalse }): Promise<{ prover: PubKeyHex, verifier: PubKeyHex, counterparty: PubKeyHex, revelationTime: ISOTimestampString, encryptedLinkage: Byte[], encryptedLinkageProof: Byte[] }> {
|
|
104
|
+
await this.connectToSubstrate()
|
|
105
|
+
return await (this.substrate as Wallet).revealCounterpartyKeyLinkage(args, this.originator)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async revealSpecificKeyLinkage(args: { counterparty: PubKeyHex, verifier: PubKeyHex, protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, privileged?: BooleanDefaultFalse }): Promise<{ prover: PubKeyHex, verifier: PubKeyHex, counterparty: PubKeyHex, protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, encryptedLinkage: Byte[], encryptedLinkageProof: Byte[], proofType: Byte }> {
|
|
109
|
+
await this.connectToSubstrate()
|
|
110
|
+
return await (this.substrate as Wallet).revealSpecificKeyLinkage(args, this.originator)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async encrypt(args: { plaintext: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', privileged?: BooleanDefaultFalse }): Promise<{ ciphertext: Byte[] }> {
|
|
114
|
+
await this.connectToSubstrate()
|
|
115
|
+
return await (this.substrate as Wallet).encrypt(args, this.originator)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async decrypt(args: { ciphertext: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', privileged?: BooleanDefaultFalse }): Promise<{ plaintext: Byte[] }> {
|
|
119
|
+
return await (this.substrate as Wallet).decrypt(args, this.originator)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createHmac(args: { data: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', privileged?: BooleanDefaultFalse }): Promise<{ hmac: Byte[] }> {
|
|
123
|
+
await this.connectToSubstrate()
|
|
124
|
+
return await (this.substrate as Wallet).createHmac(args, this.originator)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async verifyHmac(args: { data: Byte[], hmac: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', privileged?: BooleanDefaultFalse }): Promise<{ valid: true }> {
|
|
128
|
+
await this.connectToSubstrate()
|
|
129
|
+
return await (this.substrate as Wallet).verifyHmac(args, this.originator)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async createSignature(args: { data?: Byte[], hashToDirectlySign?: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', privileged?: BooleanDefaultFalse }): Promise<{ signature: Byte[] }> {
|
|
133
|
+
await this.connectToSubstrate()
|
|
134
|
+
return await (this.substrate as Wallet).createSignature(args, this.originator)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async verifySignature(args: { data?: Byte[], hashToDirectlyVerify?: Byte[], signature: Byte[], protocolID: [0 | 1 | 2, ProtocolString5To400Bytes], keyID: KeyIDStringUnder800Bytes, privilegedReason?: DescriptionString5to50Bytes, counterparty?: PubKeyHex | 'self' | 'anyone', forSelf?: BooleanDefaultFalse, privileged?: BooleanDefaultFalse }): Promise<{ valid: true }> {
|
|
138
|
+
await this.connectToSubstrate()
|
|
139
|
+
return await (this.substrate as Wallet).verifySignature(args, this.originator)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async acquireCertificate(args: AcquireCertificateArgs): Promise<AcquireCertificateResult> {
|
|
143
|
+
await this.connectToSubstrate()
|
|
144
|
+
return await (this.substrate as Wallet).acquireCertificate(args, this.originator)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async listCertificates(args: { certifiers: PubKeyHex[], types: Base64String[], limit?: PositiveIntegerDefault10Max10000, offset?: PositiveIntegerOrZero, privileged?: BooleanDefaultFalse, privilegedReason?: DescriptionString5to50Bytes }): Promise<ListCertificatesResult> {
|
|
148
|
+
await this.connectToSubstrate()
|
|
149
|
+
return await (this.substrate as Wallet).listCertificates(args, this.originator)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async proveCertificate(args: ProveCertificateArgs): Promise<ProveCertificateResult> {
|
|
153
|
+
await this.connectToSubstrate()
|
|
154
|
+
return await (this.substrate as Wallet).proveCertificate(args, this.originator)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async relinquishCertificate(args: { type: Base64String, serialNumber: Base64String, certifier: PubKeyHex }): Promise<{ relinquished: true }> {
|
|
158
|
+
await this.connectToSubstrate()
|
|
159
|
+
return await (this.substrate as Wallet).relinquishCertificate(args, this.originator)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async discoverByIdentityKey(args: { identityKey: PubKeyHex, limit?: PositiveIntegerDefault10Max10000, offset?: PositiveIntegerOrZero }): Promise<DiscoverCertificatesResult> {
|
|
163
|
+
await this.connectToSubstrate()
|
|
164
|
+
return await (this.substrate as Wallet).discoverByIdentityKey(args, this.originator)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async discoverByAttributes(args: { attributes: Record<CertificateFieldNameUnder50Bytes, string>, limit?: PositiveIntegerDefault10Max10000, offset?: PositiveIntegerOrZero }): Promise<DiscoverCertificatesResult> {
|
|
168
|
+
await this.connectToSubstrate()
|
|
169
|
+
return await (this.substrate as Wallet).discoverByAttributes(args, this.originator)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async isAuthenticated(args: {} = {}): Promise<{ authenticated: boolean }> {
|
|
173
|
+
await this.connectToSubstrate()
|
|
174
|
+
return await (this.substrate as Wallet).isAuthenticated(args, this.originator)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async waitForAuthentication(args: {} = {}): Promise<{ authenticated: true }> {
|
|
178
|
+
await this.connectToSubstrate()
|
|
179
|
+
return await (this.substrate as Wallet).waitForAuthentication(args, this.originator)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async getHeight(args: {} = {}): Promise<{ height: PositiveInteger }> {
|
|
183
|
+
await this.connectToSubstrate()
|
|
184
|
+
return await (this.substrate as Wallet).getHeight(args, this.originator)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async getHeaderForHeight(args: { height: PositiveInteger }): Promise<{ header: HexString }> {
|
|
188
|
+
await this.connectToSubstrate()
|
|
189
|
+
return await (this.substrate as Wallet).getHeaderForHeight(args, this.originator)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async getNetwork(args: {} = {}): Promise<{ network: 'mainnet' | 'testnet' }> {
|
|
193
|
+
await this.connectToSubstrate()
|
|
194
|
+
return await (this.substrate as Wallet).getNetwork(args, this.originator)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getVersion(args: {} = {}): Promise<{ version: VersionString7To30Bytes }> {
|
|
198
|
+
await this.connectToSubstrate()
|
|
199
|
+
return await (this.substrate as Wallet).getVersion(args, this.originator)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class WalletError extends Error {
|
|
2
|
+
code: number
|
|
3
|
+
isError: boolean = true
|
|
4
|
+
|
|
5
|
+
constructor(message: string, code = 1, stack?: string) {
|
|
6
|
+
super(message)
|
|
7
|
+
this.code = code
|
|
8
|
+
this.name = this.constructor.name
|
|
9
|
+
|
|
10
|
+
if (stack) {
|
|
11
|
+
this.stack = stack
|
|
12
|
+
} else if (Error.captureStackTrace) {
|
|
13
|
+
Error.captureStackTrace(this, this.constructor)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// NOTE: Enum values must not exceed the UInt8 range (0–255)
|
|
19
|
+
enum walletErrors {
|
|
20
|
+
unknownError = 1,
|
|
21
|
+
unsupportedAction = 2,
|
|
22
|
+
invalidHmac = 3,
|
|
23
|
+
invalidSignature = 4,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default walletErrors
|
|
27
|
+
export type WalletErrorCode = keyof typeof walletErrors
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { PrivateKey, PublicKey, SymmetricKey } from '../../../dist/cjs/src/primitives/index.js'
|
|
2
|
+
import CachedKeyDeriver from '../../../dist/cjs/src/wallet/CachedKeyDeriver.js'
|
|
3
|
+
import KeyDeriver from '../../../dist/cjs/src/wallet/KeyDeriver.js'
|
|
4
|
+
|
|
5
|
+
describe('CachedKeyDeriver', () => {
|
|
6
|
+
let mockKeyDeriver: jest.Mocked<KeyDeriver>
|
|
7
|
+
let cachedKeyDeriver: CachedKeyDeriver
|
|
8
|
+
const rootKey = new PrivateKey(1)
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Reset the mocks and create a new CachedKeyDeriver instance before each test
|
|
12
|
+
jest.clearAllMocks()
|
|
13
|
+
mockKeyDeriver = new KeyDeriver(rootKey) as jest.Mocked<KeyDeriver>
|
|
14
|
+
// Mock the methods of KeyDeriver
|
|
15
|
+
mockKeyDeriver.derivePublicKey = jest.fn()
|
|
16
|
+
mockKeyDeriver.derivePrivateKey = jest.fn()
|
|
17
|
+
mockKeyDeriver.deriveSymmetricKey = jest.fn()
|
|
18
|
+
mockKeyDeriver.revealCounterpartySecret = jest.fn()
|
|
19
|
+
mockKeyDeriver.revealSpecificSecret = jest.fn()
|
|
20
|
+
|
|
21
|
+
// Replace the internal keyDeriver instance with the mocked one
|
|
22
|
+
cachedKeyDeriver = new CachedKeyDeriver(rootKey)
|
|
23
|
+
cachedKeyDeriver.keyDeriver = mockKeyDeriver
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('derivePublicKey', () => {
|
|
27
|
+
it('should call derivePublicKey on KeyDeriver and cache the result', () => {
|
|
28
|
+
const protocolID: [0, string] = [0, 'testprotocol']
|
|
29
|
+
const keyID = 'key1'
|
|
30
|
+
const counterparty = 'self'
|
|
31
|
+
const publicKey = new PublicKey(0)
|
|
32
|
+
|
|
33
|
+
mockKeyDeriver.derivePublicKey.mockReturnValue(publicKey)
|
|
34
|
+
|
|
35
|
+
// First call - should invoke the underlying method
|
|
36
|
+
const result1 = cachedKeyDeriver.derivePublicKey(protocolID, keyID, counterparty)
|
|
37
|
+
expect(mockKeyDeriver.derivePublicKey).toHaveBeenCalledTimes(1)
|
|
38
|
+
expect(result1).toBe(publicKey)
|
|
39
|
+
|
|
40
|
+
// Second call with the same parameters - should retrieve from cache
|
|
41
|
+
const result2 = cachedKeyDeriver.derivePublicKey(protocolID, keyID, counterparty)
|
|
42
|
+
expect(mockKeyDeriver.derivePublicKey).toHaveBeenCalledTimes(1) // No additional calls
|
|
43
|
+
expect(result2).toBe(publicKey)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should handle different parameters correctly', () => {
|
|
47
|
+
const protocolID1: [0, string] = [0, 'protocol1']
|
|
48
|
+
const protocolID2: [1, string] = [1, 'protocol2']
|
|
49
|
+
const keyID1 = 'key1'
|
|
50
|
+
const keyID2 = 'key2'
|
|
51
|
+
const counterparty1 = 'self'
|
|
52
|
+
const counterparty2 = 'anyone'
|
|
53
|
+
const publicKey1 = new PublicKey(0)
|
|
54
|
+
const publicKey2 = new PublicKey(0)
|
|
55
|
+
|
|
56
|
+
mockKeyDeriver.derivePublicKey
|
|
57
|
+
.mockReturnValueOnce(publicKey1)
|
|
58
|
+
.mockReturnValueOnce(publicKey2)
|
|
59
|
+
|
|
60
|
+
// Different parameters - should not hit cache
|
|
61
|
+
const result1 = cachedKeyDeriver.derivePublicKey(protocolID1, keyID1, counterparty1)
|
|
62
|
+
const result2 = cachedKeyDeriver.derivePublicKey(protocolID2, keyID2, counterparty2)
|
|
63
|
+
expect(mockKeyDeriver.derivePublicKey).toHaveBeenCalledTimes(2)
|
|
64
|
+
expect(result1).toBe(publicKey1)
|
|
65
|
+
expect(result2).toBe(publicKey2)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('derivePrivateKey', () => {
|
|
70
|
+
it('should call derivePrivateKey on KeyDeriver and cache the result', () => {
|
|
71
|
+
const protocolID: [1, string] = [1, 'testprotocol']
|
|
72
|
+
const keyID = 'key1'
|
|
73
|
+
const counterparty = 'anyone'
|
|
74
|
+
const privateKey = new PrivateKey()
|
|
75
|
+
|
|
76
|
+
mockKeyDeriver.derivePrivateKey.mockReturnValue(privateKey)
|
|
77
|
+
|
|
78
|
+
// First call - should invoke the underlying method
|
|
79
|
+
const result1 = cachedKeyDeriver.derivePrivateKey(protocolID, keyID, counterparty)
|
|
80
|
+
expect(mockKeyDeriver.derivePrivateKey).toHaveBeenCalledTimes(1)
|
|
81
|
+
expect(result1).toBe(privateKey)
|
|
82
|
+
|
|
83
|
+
// Second call with the same parameters - should retrieve from cache
|
|
84
|
+
const result2 = cachedKeyDeriver.derivePrivateKey(protocolID, keyID, counterparty)
|
|
85
|
+
expect(mockKeyDeriver.derivePrivateKey).toHaveBeenCalledTimes(1)
|
|
86
|
+
expect(result2).toBe(privateKey)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should differentiate cache entries based on parameters', () => {
|
|
90
|
+
const protocolID: [1, string] = [1, 'testprotocol']
|
|
91
|
+
const keyID = 'key1'
|
|
92
|
+
const counterparty = 'anyone'
|
|
93
|
+
const privateKey1 = new PrivateKey()
|
|
94
|
+
const privateKey2 = new PrivateKey()
|
|
95
|
+
|
|
96
|
+
mockKeyDeriver.derivePrivateKey
|
|
97
|
+
.mockReturnValueOnce(privateKey1)
|
|
98
|
+
.mockReturnValueOnce(privateKey2)
|
|
99
|
+
|
|
100
|
+
// First call
|
|
101
|
+
const result1 = cachedKeyDeriver.derivePrivateKey(protocolID, keyID, counterparty)
|
|
102
|
+
expect(result1).toBe(privateKey1)
|
|
103
|
+
|
|
104
|
+
// Second call with different keyID
|
|
105
|
+
const result2 = cachedKeyDeriver.derivePrivateKey(protocolID, 'key2', counterparty)
|
|
106
|
+
expect(result2).toBe(privateKey2)
|
|
107
|
+
expect(mockKeyDeriver.derivePrivateKey).toHaveBeenCalledTimes(2)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('deriveSymmetricKey', () => {
|
|
112
|
+
it('should call deriveSymmetricKey on KeyDeriver and cache the result', () => {
|
|
113
|
+
const protocolID: [2, string] = [2, 'testprotocol']
|
|
114
|
+
const keyID = 'key1'
|
|
115
|
+
const counterparty = new PublicKey(0)
|
|
116
|
+
const symmetricKey = new SymmetricKey(0)
|
|
117
|
+
|
|
118
|
+
mockKeyDeriver.deriveSymmetricKey.mockReturnValue(symmetricKey)
|
|
119
|
+
|
|
120
|
+
// First call
|
|
121
|
+
const result1 = cachedKeyDeriver.deriveSymmetricKey(protocolID, keyID, counterparty)
|
|
122
|
+
expect(mockKeyDeriver.deriveSymmetricKey).toHaveBeenCalledTimes(1)
|
|
123
|
+
expect(result1).toBe(symmetricKey)
|
|
124
|
+
|
|
125
|
+
// Second call with same parameters
|
|
126
|
+
const result2 = cachedKeyDeriver.deriveSymmetricKey(protocolID, keyID, counterparty)
|
|
127
|
+
expect(mockKeyDeriver.deriveSymmetricKey).toHaveBeenCalledTimes(1)
|
|
128
|
+
expect(result2).toBe(symmetricKey)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('should throw an error when KeyDeriver throws an error', () => {
|
|
132
|
+
const protocolID: [2, string] = [2, 'testprotocol']
|
|
133
|
+
const keyID = 'key1'
|
|
134
|
+
const counterparty = 'anyone'
|
|
135
|
+
|
|
136
|
+
mockKeyDeriver.deriveSymmetricKey.mockImplementation(() => {
|
|
137
|
+
throw new Error('Test error')
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
expect(() => {
|
|
141
|
+
cachedKeyDeriver.deriveSymmetricKey(protocolID, keyID, counterparty)
|
|
142
|
+
}).toThrow('Test error')
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('revealCounterpartySecret', () => {
|
|
147
|
+
it('should call revealCounterpartySecret on KeyDeriver and cache the result', () => {
|
|
148
|
+
const counterparty = new PublicKey(0)
|
|
149
|
+
const secret = [1, 2, 3]
|
|
150
|
+
|
|
151
|
+
mockKeyDeriver.revealCounterpartySecret.mockReturnValue(secret)
|
|
152
|
+
|
|
153
|
+
// First call
|
|
154
|
+
const result1 = cachedKeyDeriver.revealCounterpartySecret(counterparty)
|
|
155
|
+
expect(mockKeyDeriver.revealCounterpartySecret).toHaveBeenCalledTimes(1)
|
|
156
|
+
expect(result1).toBe(secret)
|
|
157
|
+
|
|
158
|
+
// Second call with same parameters
|
|
159
|
+
const result2 = cachedKeyDeriver.revealCounterpartySecret(counterparty)
|
|
160
|
+
expect(mockKeyDeriver.revealCounterpartySecret).toHaveBeenCalledTimes(1)
|
|
161
|
+
expect(result2).toBe(secret)
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe('revealSpecificSecret', () => {
|
|
166
|
+
it('should call revealSpecificSecret on KeyDeriver and cache the result', () => {
|
|
167
|
+
const counterparty = 'self'
|
|
168
|
+
const protocolID: [0, string] = [0, 'testprotocol']
|
|
169
|
+
const keyID = 'key1'
|
|
170
|
+
const secret = [4, 5, 6]
|
|
171
|
+
|
|
172
|
+
mockKeyDeriver.revealSpecificSecret.mockReturnValue(secret)
|
|
173
|
+
|
|
174
|
+
// First call
|
|
175
|
+
const result1 = cachedKeyDeriver.revealSpecificSecret(counterparty, protocolID, keyID)
|
|
176
|
+
expect(mockKeyDeriver.revealSpecificSecret).toHaveBeenCalledTimes(1)
|
|
177
|
+
expect(result1).toBe(secret)
|
|
178
|
+
|
|
179
|
+
// Second call with same parameters
|
|
180
|
+
const result2 = cachedKeyDeriver.revealSpecificSecret(counterparty, protocolID, keyID)
|
|
181
|
+
expect(mockKeyDeriver.revealSpecificSecret).toHaveBeenCalledTimes(1)
|
|
182
|
+
expect(result2).toBe(secret)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('should handle different parameters correctly', () => {
|
|
186
|
+
const counterparty = 'self'
|
|
187
|
+
const protocolID1: [0, string] = [0, 'protocol1']
|
|
188
|
+
const protocolID2: [1, string] = [1, 'protocol2']
|
|
189
|
+
const keyID1 = 'key1'
|
|
190
|
+
const keyID2 = 'key2'
|
|
191
|
+
const secret1 = [4, 5, 6]
|
|
192
|
+
const secret2 = [7, 8, 9]
|
|
193
|
+
|
|
194
|
+
mockKeyDeriver.revealSpecificSecret
|
|
195
|
+
.mockReturnValueOnce(secret1)
|
|
196
|
+
.mockReturnValueOnce(secret2)
|
|
197
|
+
|
|
198
|
+
// First call
|
|
199
|
+
const result1 = cachedKeyDeriver.revealSpecificSecret(counterparty, protocolID1, keyID1)
|
|
200
|
+
expect(result1).toBe(secret1)
|
|
201
|
+
|
|
202
|
+
// Second call with different parameters
|
|
203
|
+
const result2 = cachedKeyDeriver.revealSpecificSecret(counterparty, protocolID2, keyID2)
|
|
204
|
+
expect(result2).toBe(secret2)
|
|
205
|
+
expect(mockKeyDeriver.revealSpecificSecret).toHaveBeenCalledTimes(2)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
describe('Cache management', () => {
|
|
210
|
+
it('should not exceed the max cache size and evict least recently used items', () => {
|
|
211
|
+
const maxCacheSize = 5
|
|
212
|
+
// Create a new CachedKeyDeriver with a small cache size
|
|
213
|
+
cachedKeyDeriver = new CachedKeyDeriver(rootKey, { maxCacheSize })
|
|
214
|
+
cachedKeyDeriver.keyDeriver = mockKeyDeriver
|
|
215
|
+
|
|
216
|
+
const protocolID: [0, string] = [0, 'testprotocol']
|
|
217
|
+
const counterparty = 'self'
|
|
218
|
+
|
|
219
|
+
// Mock return values
|
|
220
|
+
const mockResults = [1, 2, 3, 4, 5, 6].map((n) => new PublicKey(0))
|
|
221
|
+
|
|
222
|
+
mockKeyDeriver.derivePublicKey
|
|
223
|
+
.mockReturnValueOnce(mockResults[0])
|
|
224
|
+
.mockReturnValueOnce(mockResults[1])
|
|
225
|
+
.mockReturnValueOnce(mockResults[2])
|
|
226
|
+
.mockReturnValueOnce(mockResults[3])
|
|
227
|
+
.mockReturnValueOnce(mockResults[4])
|
|
228
|
+
.mockReturnValueOnce(mockResults[5])
|
|
229
|
+
|
|
230
|
+
// Add entries to fill the cache
|
|
231
|
+
for (let i = 0; i < maxCacheSize; i++) {
|
|
232
|
+
cachedKeyDeriver.derivePublicKey(protocolID, `key${i}`, counterparty)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Cache should be full now
|
|
236
|
+
expect(cachedKeyDeriver.cache.size).toBe(maxCacheSize)
|
|
237
|
+
|
|
238
|
+
// Access one of the earlier keys to make it recently used
|
|
239
|
+
cachedKeyDeriver.derivePublicKey(protocolID, 'key0', counterparty)
|
|
240
|
+
|
|
241
|
+
// Add one more entry to exceed the cache size
|
|
242
|
+
cachedKeyDeriver.derivePublicKey(protocolID, 'key5', counterparty)
|
|
243
|
+
|
|
244
|
+
// Cache size should still be maxCacheSize
|
|
245
|
+
expect(cachedKeyDeriver.cache.size).toBe(maxCacheSize)
|
|
246
|
+
|
|
247
|
+
// The least recently used item (key1) should have been evicted
|
|
248
|
+
// The cache should contain keys: key0, key2, key3, key4, key5
|
|
249
|
+
expect(Array.from(cachedKeyDeriver.cache.keys())).toEqual([
|
|
250
|
+
expect.stringContaining('key2'),
|
|
251
|
+
expect.stringContaining('key3'),
|
|
252
|
+
expect.stringContaining('key4'),
|
|
253
|
+
expect.stringContaining('key0'),
|
|
254
|
+
expect.stringContaining('key5')
|
|
255
|
+
])
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should update the recentness of cache entries on access', () => {
|
|
259
|
+
const maxCacheSize = 3
|
|
260
|
+
cachedKeyDeriver = new CachedKeyDeriver(rootKey, { maxCacheSize })
|
|
261
|
+
cachedKeyDeriver.keyDeriver = mockKeyDeriver
|
|
262
|
+
|
|
263
|
+
const protocolID: [0, string] = [0, 'testprotocol']
|
|
264
|
+
const counterparty = 'self'
|
|
265
|
+
const keys = ['key1', 'key2', 'key3']
|
|
266
|
+
const publicKeys = keys.map(() => new PublicKey(0))
|
|
267
|
+
|
|
268
|
+
mockKeyDeriver.derivePublicKey
|
|
269
|
+
.mockReturnValueOnce(publicKeys[0])
|
|
270
|
+
.mockReturnValueOnce(publicKeys[1])
|
|
271
|
+
.mockReturnValueOnce(publicKeys[2])
|
|
272
|
+
|
|
273
|
+
// Fill the cache
|
|
274
|
+
keys.forEach((keyID) => {
|
|
275
|
+
cachedKeyDeriver.derivePublicKey(protocolID, keyID, counterparty)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// Access 'key1' to make it most recently used
|
|
279
|
+
cachedKeyDeriver.derivePublicKey(protocolID, 'key1', counterparty)
|
|
280
|
+
|
|
281
|
+
// Add a new key to trigger eviction
|
|
282
|
+
const newKeyID = 'key4'
|
|
283
|
+
const newPublicKey = new PublicKey(0)
|
|
284
|
+
mockKeyDeriver.derivePublicKey.mockReturnValueOnce(newPublicKey)
|
|
285
|
+
cachedKeyDeriver.derivePublicKey(protocolID, newKeyID, counterparty)
|
|
286
|
+
|
|
287
|
+
// 'key2' should be evicted as it is the least recently used
|
|
288
|
+
expect(Array.from(cachedKeyDeriver.cache.keys())).toEqual([
|
|
289
|
+
expect.stringContaining('key3'),
|
|
290
|
+
expect.stringContaining('key1'),
|
|
291
|
+
expect.stringContaining('key4')
|
|
292
|
+
])
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
describe('Performance considerations', () => {
|
|
297
|
+
it('should improve performance by caching expensive operations', () => {
|
|
298
|
+
const protocolID: [0, string] = [0, 'testprotocol']
|
|
299
|
+
const keyID = 'key1'
|
|
300
|
+
const counterparty = 'self'
|
|
301
|
+
const publicKey = new PublicKey(0)
|
|
302
|
+
|
|
303
|
+
// Simulate an expensive operation
|
|
304
|
+
mockKeyDeriver.derivePublicKey.mockImplementation(() => {
|
|
305
|
+
const start = Date.now()
|
|
306
|
+
while (Date.now() - start < 50) { } // Busy wait for 50ms
|
|
307
|
+
return publicKey
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
const startTime = Date.now()
|
|
311
|
+
cachedKeyDeriver.derivePublicKey(protocolID, keyID, counterparty)
|
|
312
|
+
const firstCallDuration = Date.now() - startTime
|
|
313
|
+
|
|
314
|
+
const startTime2 = Date.now()
|
|
315
|
+
cachedKeyDeriver.derivePublicKey(protocolID, keyID, counterparty)
|
|
316
|
+
const secondCallDuration = Date.now() - startTime2
|
|
317
|
+
|
|
318
|
+
expect(firstCallDuration).toBeGreaterThanOrEqual(50)
|
|
319
|
+
expect(secondCallDuration).toBeLessThan(10) // Should be much faster due to caching
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { PrivateKey, PublicKey, SymmetricKey, Utils, Hash } from '../../../dist/cjs/src/primitives/index.js'
|
|
2
|
+
import KeyDeriver from '../../../dist/cjs/src/wallet/KeyDeriver.js'
|
|
3
|
+
|
|
4
|
+
describe('KeyDeriver', () => {
|
|
5
|
+
const rootPrivateKey = new PrivateKey(42)
|
|
6
|
+
const rootPublicKey = rootPrivateKey.toPublicKey()
|
|
7
|
+
const counterpartyPrivateKey = new PrivateKey(69)
|
|
8
|
+
const counterpartyPublicKey = counterpartyPrivateKey.toPublicKey()
|
|
9
|
+
const anyonePublicKey = new PrivateKey(1).toPublicKey()
|
|
10
|
+
|
|
11
|
+
const protocolID: [0 | 1 | 2, string] = [0, 'testprotocol']
|
|
12
|
+
const keyID = '12345'
|
|
13
|
+
|
|
14
|
+
let keyDeriver: KeyDeriver
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
keyDeriver = new KeyDeriver(rootPrivateKey)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('should compute the correct invoice number', () => {
|
|
21
|
+
const invoiceNumber = (keyDeriver as any).computeInvoiceNumber(protocolID, keyID)
|
|
22
|
+
expect(invoiceNumber).toBe('0-testprotocol-12345')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test('should throw if no co counterparty given', () => {
|
|
26
|
+
expect(() => (keyDeriver as any).normalizeCounterparty()).toThrow()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('should normalize counterparty correctly for self', () => {
|
|
30
|
+
const normalized = (keyDeriver as any).normalizeCounterparty('self')
|
|
31
|
+
expect(normalized.toString()).toBe(rootPublicKey.toString())
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('should normalize counterparty correctly for anyone', () => {
|
|
35
|
+
const normalized = (keyDeriver as any).normalizeCounterparty('anyone')
|
|
36
|
+
expect(normalized.toString()).toBe(anyonePublicKey.toString())
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('should normalize counterparty correctly when given as a hex string', () => {
|
|
40
|
+
const normalized = (keyDeriver as any).normalizeCounterparty(counterpartyPublicKey.toString())
|
|
41
|
+
expect(normalized.toString()).toBe(counterpartyPublicKey.toString())
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('should normalize counterparty correctly when given as a public key', () => {
|
|
45
|
+
const normalized = (keyDeriver as any).normalizeCounterparty(counterpartyPublicKey)
|
|
46
|
+
expect(normalized.toString()).toBe(counterpartyPublicKey.toString())
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('should allow public key derivation as anyone', () => {
|
|
50
|
+
const anyoneDeriver = new KeyDeriver('anyone')
|
|
51
|
+
const derivedPublicKey = anyoneDeriver.derivePublicKey(protocolID, keyID, counterpartyPublicKey)
|
|
52
|
+
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
|
|
53
|
+
expect(derivedPublicKey.toString()).toEqual(counterpartyPublicKey.deriveChild(new PrivateKey(1), '0-testprotocol-12345').toString())
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('should derive the correct public key for counterparty', () => {
|
|
57
|
+
const derivedPublicKey = keyDeriver.derivePublicKey(protocolID, keyID, counterpartyPublicKey)
|
|
58
|
+
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
|
|
59
|
+
expect(derivedPublicKey.toString()).toEqual(counterpartyPublicKey.deriveChild(rootPrivateKey, '0-testprotocol-12345').toString())
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('should derive the correct public key for self', () => {
|
|
63
|
+
const derivedPublicKey = keyDeriver.derivePublicKey(protocolID, keyID, counterpartyPublicKey, true)
|
|
64
|
+
expect(derivedPublicKey).toBeInstanceOf(PublicKey)
|
|
65
|
+
expect(derivedPublicKey.toString()).toEqual(rootPrivateKey.deriveChild(counterpartyPublicKey, '0-testprotocol-12345').toPublicKey().toString())
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('should derive the correct private key', () => {
|
|
69
|
+
const derivedPrivateKey = keyDeriver.derivePrivateKey(protocolID, keyID, counterpartyPublicKey)
|
|
70
|
+
expect(derivedPrivateKey).toBeInstanceOf(PrivateKey)
|
|
71
|
+
expect(derivedPrivateKey.toString()).toEqual(rootPrivateKey.deriveChild(counterpartyPublicKey, '0-testprotocol-12345').toString())
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('should derive the correct symmetric key', () => {
|
|
75
|
+
const derivedSymmetricKey = keyDeriver.deriveSymmetricKey(protocolID, keyID, counterpartyPublicKey)
|
|
76
|
+
expect(derivedSymmetricKey).toBeInstanceOf(SymmetricKey)
|
|
77
|
+
const priv = rootPrivateKey.deriveChild(counterpartyPublicKey, '0-testprotocol-12345')
|
|
78
|
+
const pub = counterpartyPublicKey.deriveChild(rootPrivateKey, '0-testprotocol-12345')
|
|
79
|
+
expect(derivedSymmetricKey.toHex()).toEqual(new SymmetricKey(priv.deriveSharedSecret(pub).x?.toArray()).toHex())
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('should not derive symmetric key with anyone', () => {
|
|
83
|
+
expect(() => keyDeriver.deriveSymmetricKey(protocolID, keyID, 'anyone')).toThrow()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('should reveal the correct counterparty shared secret', () => {
|
|
87
|
+
const sharedSecret = keyDeriver.revealCounterpartySecret(counterpartyPublicKey)
|
|
88
|
+
expect(sharedSecret).toBeInstanceOf(Array)
|
|
89
|
+
expect(sharedSecret.length).toBeGreaterThan(0)
|
|
90
|
+
expect(sharedSecret).toEqual(rootPrivateKey.deriveSharedSecret(counterpartyPublicKey).encode(true))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('should not reveal shared secret for self', () => {
|
|
94
|
+
expect(() => keyDeriver.revealCounterpartySecret('self')).toThrow()
|
|
95
|
+
expect(() => keyDeriver.revealCounterpartySecret(rootPublicKey)).toThrow()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('should reveal the specific key association', () => {
|
|
99
|
+
const specificSecret = keyDeriver.revealSpecificSecret(counterpartyPublicKey, protocolID, keyID)
|
|
100
|
+
expect(specificSecret).toBeInstanceOf(Array)
|
|
101
|
+
expect(specificSecret.length).toBeGreaterThan(0)
|
|
102
|
+
const sharedSecret = rootPrivateKey.deriveSharedSecret(counterpartyPublicKey)
|
|
103
|
+
const invoiceNumberBin = Utils.toArray((keyDeriver as any).computeInvoiceNumber(protocolID, keyID), 'utf8')
|
|
104
|
+
expect(specificSecret).toEqual(Hash.sha256hmac(sharedSecret.encode(true), invoiceNumberBin))
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('should throw an error for invalid protocol names', () => {
|
|
108
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber(protocolID, 'long' + 'a'.repeat(800))).toThrow()
|
|
109
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber(protocolID, '')).toThrow()
|
|
110
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([-3, 'otherwise valid'], keyID)).toThrow()
|
|
111
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([2, 'double space'], keyID)).toThrow()
|
|
112
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([0, ''], keyID)).toThrow()
|
|
113
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([0, ' a'], keyID)).toThrow()
|
|
114
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([0, 'long' + 'a'.repeat(400)], keyID)).toThrow()
|
|
115
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([2, 'redundant protocol protocol'], keyID)).toThrow()
|
|
116
|
+
expect(() => (keyDeriver as any).computeInvoiceNumber([2, 'üñî√é®sål ©0på'], keyID)).toThrow()
|
|
117
|
+
})
|
|
118
|
+
})
|