@bsv/sdk 1.1.33 → 1.2.0
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 +551 -0
- package/docs/script.md +135 -0
- package/docs/totp.md +119 -0
- package/docs/wallet-substrates.md +10 -0
- package/docs/wallet.md +4182 -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,5 @@
|
|
|
1
|
+
export * from './LookupResolver.js'
|
|
2
|
+
export * from './SHIPBroadcaster.js'
|
|
3
|
+
export { default as OverlayAdminTokenTemplate } from './OverlayAdminTokenTemplate.js'
|
|
4
|
+
export { default as LookupResolver } from './LookupResolver.js'
|
|
5
|
+
export { default as SHIPBroadcaster } from './SHIPBroadcaster.js'
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { ScriptTemplate, LockingScript, UnlockingScript, OP } from '../index.js'
|
|
2
|
+
import { Utils, Hash, TransactionSignature, Signature, PublicKey } from '../../primitives/index.js'
|
|
3
|
+
import { Wallet } from '../../wallet/Wallet.interfaces.js'
|
|
4
|
+
import { Transaction } from '../../transaction/index.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* For a given piece of data to push onto the stack in script, creates the correct minimally-encoded script chunk,
|
|
8
|
+
* including the correct push operation.
|
|
9
|
+
*
|
|
10
|
+
* TODO: This should be made into a TS-SDK util (distinct from the `minimallyEncode` util)
|
|
11
|
+
*/
|
|
12
|
+
const createMinimallyEncodedScriptChunk = (data: number[]): { op: number, data?: number[] } => {
|
|
13
|
+
if (data.length === 0) {
|
|
14
|
+
// Could have used OP_0.
|
|
15
|
+
return { op: 0 }
|
|
16
|
+
}
|
|
17
|
+
if (data.length === 1 && data[0] === 0) {
|
|
18
|
+
// Could have used OP_0.
|
|
19
|
+
return { op: 0 }
|
|
20
|
+
}
|
|
21
|
+
if (data.length === 1 && data[0] > 0 && data[0] <= 16) {
|
|
22
|
+
// Could have used OP_0 .. OP_16.
|
|
23
|
+
return { op: 0x50 + data[0] }
|
|
24
|
+
}
|
|
25
|
+
if (data.length === 1 && data[0] === 0x81) {
|
|
26
|
+
// Could have used OP_1NEGATE.
|
|
27
|
+
return { op: 0x4f }
|
|
28
|
+
}
|
|
29
|
+
if (data.length <= 75) {
|
|
30
|
+
// Could have used a direct push (opcode indicating number of bytes
|
|
31
|
+
// pushed + those bytes).
|
|
32
|
+
return { op: data.length, data }
|
|
33
|
+
}
|
|
34
|
+
if (data.length <= 255) {
|
|
35
|
+
// Could have used OP_PUSHDATA.
|
|
36
|
+
return { op: 0x4c, data }
|
|
37
|
+
}
|
|
38
|
+
if (data.length <= 65535) {
|
|
39
|
+
// Could have used OP_PUSHDATA2.
|
|
40
|
+
return { op: 0x4d, data }
|
|
41
|
+
}
|
|
42
|
+
return { op: 0x4e, data }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default class PushDrop implements ScriptTemplate {
|
|
46
|
+
wallet: Wallet
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Decodes a PushDrop script back into its token fields and the locking public key. If a signature was present, it will be the last field returned.
|
|
50
|
+
* Warning: Only works with a P2PK lock at the beginning of the script.
|
|
51
|
+
* @param script PushDrop script to decode back into token fields
|
|
52
|
+
* @returns An object containing PushDrop token fields and the locking public key. If a signature was included, it will be the last field.
|
|
53
|
+
*/
|
|
54
|
+
static decode(script: LockingScript): { lockingPublicKey: PublicKey, fields: number[][] } {
|
|
55
|
+
const lockingPublicKey = PublicKey.fromString(Utils.toHex(script.chunks[0].data))
|
|
56
|
+
const fields = []
|
|
57
|
+
for (let i = 2; i < script.chunks.length; i++) {
|
|
58
|
+
const nextOpcode = script.chunks[i + 1].op
|
|
59
|
+
let chunk = script.chunks[i].data
|
|
60
|
+
if (!chunk) {
|
|
61
|
+
if (script.chunks[i].op >= 80 && script.chunks[i].op <= 95) {
|
|
62
|
+
chunk = [script.chunks[i].op - 80]
|
|
63
|
+
} else if (script.chunks[i].op === 0) {
|
|
64
|
+
chunk = [0]
|
|
65
|
+
} else if (script.chunks[i].op === 0x4f) {
|
|
66
|
+
chunk = [0x81]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
fields.push(chunk)
|
|
70
|
+
|
|
71
|
+
// If the next value is DROP or 2DROP then this is the final field
|
|
72
|
+
if (nextOpcode === OP.OP_DROP || nextOpcode === OP.OP_2DROP) {
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
fields,
|
|
79
|
+
lockingPublicKey
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Constructs a new instance of the PushDrop class.
|
|
85
|
+
*
|
|
86
|
+
* @param {Wallet} wallet - The wallet interface used for creating signatures and accessing public keys.
|
|
87
|
+
*/
|
|
88
|
+
constructor(wallet: Wallet) {
|
|
89
|
+
this.wallet = wallet
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a PushDrop locking script with arbitrary data fields and a public key lock.
|
|
94
|
+
*
|
|
95
|
+
* @param {number[][]} fields - The token fields to include in the locking script.
|
|
96
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID to use.
|
|
97
|
+
* @param {string} keyID - The key ID to use.
|
|
98
|
+
* @param {string} counterparty - The counterparty involved in the transaction, "self" or "anyone".
|
|
99
|
+
* @param {boolean} [forSelf=false] - Flag indicating if the lock is for the creator (default no).
|
|
100
|
+
* @param {boolean} [includeSignature=true] - Flag indicating if a signature should be included in the script (default yes).
|
|
101
|
+
* @returns {Promise<LockingScript>} The generated PushDrop locking script.
|
|
102
|
+
*/
|
|
103
|
+
async lock(fields: number[][], protocolID: [0 | 1 | 2, string], keyID: string, counterparty: string, forSelf = false, includeSignature = true, lockPosition: 'before' | 'after' = 'before'): Promise<LockingScript> {
|
|
104
|
+
const { publicKey } = await this.wallet.getPublicKey({
|
|
105
|
+
protocolID,
|
|
106
|
+
keyID,
|
|
107
|
+
counterparty,
|
|
108
|
+
forSelf
|
|
109
|
+
})
|
|
110
|
+
const lockChunks: Array<{ op: number, data?: number[] }> = []
|
|
111
|
+
const pushDropChunks: Array<{ op: number, data?: number[] }> = []
|
|
112
|
+
lockChunks.push({ op: publicKey.length / 2, data: Utils.toArray(publicKey, 'hex') })
|
|
113
|
+
lockChunks.push({ op: OP.OP_CHECKSIG })
|
|
114
|
+
if (includeSignature) {
|
|
115
|
+
const dataToSign = fields.reduce((a, e) => [...a, ...e], [])
|
|
116
|
+
const { signature } = await this.wallet.createSignature({
|
|
117
|
+
data: dataToSign,
|
|
118
|
+
protocolID,
|
|
119
|
+
keyID,
|
|
120
|
+
counterparty
|
|
121
|
+
})
|
|
122
|
+
fields.push(signature)
|
|
123
|
+
}
|
|
124
|
+
for (const field of fields) {
|
|
125
|
+
pushDropChunks.push(createMinimallyEncodedScriptChunk(field))
|
|
126
|
+
}
|
|
127
|
+
let notYetDropped = fields.length
|
|
128
|
+
while (notYetDropped > 1) {
|
|
129
|
+
pushDropChunks.push({ op: OP.OP_2DROP })
|
|
130
|
+
notYetDropped -= 2
|
|
131
|
+
}
|
|
132
|
+
if (notYetDropped) {
|
|
133
|
+
pushDropChunks.push({ op: OP.OP_DROP })
|
|
134
|
+
}
|
|
135
|
+
if (lockPosition === 'before') {
|
|
136
|
+
return new LockingScript([
|
|
137
|
+
...lockChunks,
|
|
138
|
+
...pushDropChunks
|
|
139
|
+
])
|
|
140
|
+
} else {
|
|
141
|
+
return new LockingScript([
|
|
142
|
+
...pushDropChunks,
|
|
143
|
+
...lockChunks
|
|
144
|
+
])
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Creates an unlocking script for spending a PushDrop token output.
|
|
150
|
+
*
|
|
151
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID to use.
|
|
152
|
+
* @param {string} keyID - The key ID to use.
|
|
153
|
+
* @param {string} counterparty - The counterparty involved in the transaction, "self" or "anyone".
|
|
154
|
+
* @param {string} [sourceTXID] - The TXID of the source transaction.
|
|
155
|
+
* @param {number} [sourceSatoshis] - The number of satoshis in the source output.
|
|
156
|
+
* @param {LockingScript} [lockingScript] - The locking script of the source output.
|
|
157
|
+
* @param {'all' | 'none' | 'single'} [signOutputs='all'] - Specifies which outputs to sign.
|
|
158
|
+
* @param {boolean} [anyoneCanPay=false] - Specifies if the anyone-can-pay flag is set.
|
|
159
|
+
* @returns {Object} An object containing functions to sign the transaction and estimate the script length.
|
|
160
|
+
*/
|
|
161
|
+
unlock(
|
|
162
|
+
protocolID: [0 | 1 | 2, string],
|
|
163
|
+
keyID: string,
|
|
164
|
+
counterparty: string,
|
|
165
|
+
signOutputs: 'all' | 'none' | 'single' = 'all',
|
|
166
|
+
anyoneCanPay = false,
|
|
167
|
+
sourceSatoshis?: number,
|
|
168
|
+
lockingScript?: LockingScript
|
|
169
|
+
): {
|
|
170
|
+
sign: (tx: Transaction, inputIndex: number) => Promise<UnlockingScript>
|
|
171
|
+
estimateLength: () => Promise<73>
|
|
172
|
+
} {
|
|
173
|
+
return {
|
|
174
|
+
sign: async (tx: Transaction, inputIndex: number): Promise<UnlockingScript> => {
|
|
175
|
+
let signatureScope = TransactionSignature.SIGHASH_FORKID
|
|
176
|
+
if (signOutputs === 'all') {
|
|
177
|
+
signatureScope |= TransactionSignature.SIGHASH_ALL
|
|
178
|
+
}
|
|
179
|
+
if (signOutputs === 'none') {
|
|
180
|
+
signatureScope |= TransactionSignature.SIGHASH_NONE
|
|
181
|
+
}
|
|
182
|
+
if (signOutputs === 'single') {
|
|
183
|
+
signatureScope |= TransactionSignature.SIGHASH_SINGLE
|
|
184
|
+
}
|
|
185
|
+
if (anyoneCanPay) {
|
|
186
|
+
signatureScope |= TransactionSignature.SIGHASH_ANYONECANPAY
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const input = tx.inputs[inputIndex]
|
|
190
|
+
|
|
191
|
+
const otherInputs = tx.inputs.filter((_, index) => index !== inputIndex)
|
|
192
|
+
|
|
193
|
+
const sourceTXID = input.sourceTXID ? input.sourceTXID : input.sourceTransaction?.id('hex')
|
|
194
|
+
if (!sourceTXID) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
'The input sourceTXID or sourceTransaction is required for transaction signing.'
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
sourceSatoshis ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].satoshis
|
|
200
|
+
if (!sourceSatoshis) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
'The sourceSatoshis or input sourceTransaction is required for transaction signing.'
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
lockingScript ||= input.sourceTransaction?.outputs[input.sourceOutputIndex].lockingScript
|
|
206
|
+
if (!lockingScript) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
'The lockingScript or input sourceTransaction is required for transaction signing.'
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const preimage = TransactionSignature.format({
|
|
213
|
+
sourceTXID,
|
|
214
|
+
sourceOutputIndex: input.sourceOutputIndex,
|
|
215
|
+
sourceSatoshis,
|
|
216
|
+
transactionVersion: tx.version,
|
|
217
|
+
otherInputs,
|
|
218
|
+
inputIndex,
|
|
219
|
+
outputs: tx.outputs,
|
|
220
|
+
inputSequence: input.sequence,
|
|
221
|
+
subscript: lockingScript,
|
|
222
|
+
lockTime: tx.lockTime,
|
|
223
|
+
scope: signatureScope
|
|
224
|
+
})
|
|
225
|
+
const preimageHash = Hash.sha256(preimage)
|
|
226
|
+
const { signature: bareSignature } = await this.wallet.createSignature({
|
|
227
|
+
data: preimageHash,
|
|
228
|
+
protocolID,
|
|
229
|
+
keyID,
|
|
230
|
+
counterparty
|
|
231
|
+
})
|
|
232
|
+
const signature = Signature.fromDER([...bareSignature])
|
|
233
|
+
const txSignature = new TransactionSignature(
|
|
234
|
+
signature.r,
|
|
235
|
+
signature.s,
|
|
236
|
+
signatureScope
|
|
237
|
+
)
|
|
238
|
+
const sigForScript = txSignature.toChecksigFormat()
|
|
239
|
+
return new UnlockingScript([
|
|
240
|
+
{ op: sigForScript.length, data: sigForScript }
|
|
241
|
+
])
|
|
242
|
+
},
|
|
243
|
+
estimateLength: async () => 73
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/* eslint-env jest */
|
|
2
|
+
import PushDrop from '../../../../dist/cjs/src/script/templates/PushDrop.js'
|
|
3
|
+
import ProtoWallet from '../../../../dist/cjs/src/wallet/ProtoWallet.js'
|
|
4
|
+
import { PrivateKey, Utils } from '../../../../dist/cjs/src/primitives/index.js'
|
|
5
|
+
import { Script, Spend } from '../../../../dist/cjs/src/script/index.js'
|
|
6
|
+
import { Transaction } from '../../../../dist/cjs/src/transaction/index.js'
|
|
7
|
+
|
|
8
|
+
describe('PushDrop', () => {
|
|
9
|
+
let privateKey: typeof PrivateKey
|
|
10
|
+
let wallet
|
|
11
|
+
let pushDrop: PushDrop
|
|
12
|
+
|
|
13
|
+
const createDecodeRedeem = async (fields: number[][] = [], protocolID: [0 | 1 | 2, string] = [0, 'tests'], keyID: string = 'test-key', counterparty: string = 'self', signOutputs: 'all' | 'none' | 'single' = 'all', anyoneCanPay: boolean = false) => {
|
|
14
|
+
const lockingScript = await pushDrop.lock(fields, protocolID, keyID, counterparty)
|
|
15
|
+
expect(lockingScript).toBeInstanceOf(Script)
|
|
16
|
+
const decoded = await PushDrop.decode(lockingScript)
|
|
17
|
+
expect(decoded.fields).toEqual(fields)
|
|
18
|
+
const expectedPublicKey = (await wallet.getPublicKey({ protocolID, keyID, counterparty })).publicKey
|
|
19
|
+
expect(decoded.lockingPublicKey.toString()).toEqual(expectedPublicKey)
|
|
20
|
+
const satoshis = 1
|
|
21
|
+
const unlockingTemplate = await pushDrop.unlock(protocolID, keyID, counterparty, signOutputs, anyoneCanPay)
|
|
22
|
+
const sourceTx = new Transaction(1, [], [{
|
|
23
|
+
lockingScript,
|
|
24
|
+
satoshis
|
|
25
|
+
}], 0)
|
|
26
|
+
const spendTx = new Transaction(1, [{
|
|
27
|
+
sourceTransaction: sourceTx,
|
|
28
|
+
sourceOutputIndex: 0,
|
|
29
|
+
sequence: 0xffffffff
|
|
30
|
+
}], [], 0)
|
|
31
|
+
const unlockingScript = await unlockingTemplate.sign(spendTx, 0)
|
|
32
|
+
expect(await unlockingTemplate.estimateLength()).toEqual(73)
|
|
33
|
+
const spend = new Spend({
|
|
34
|
+
sourceTXID: sourceTx.id('hex'),
|
|
35
|
+
sourceOutputIndex: 0,
|
|
36
|
+
sourceSatoshis: satoshis,
|
|
37
|
+
lockingScript,
|
|
38
|
+
transactionVersion: 1,
|
|
39
|
+
otherInputs: [],
|
|
40
|
+
inputIndex: 0,
|
|
41
|
+
unlockingScript,
|
|
42
|
+
outputs: [],
|
|
43
|
+
inputSequence: 0xffffffff,
|
|
44
|
+
lockTime: 0
|
|
45
|
+
})
|
|
46
|
+
const valid = spend.validate()
|
|
47
|
+
expect(valid).toBe(true)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
privateKey = PrivateKey.fromRandom()
|
|
52
|
+
wallet = new ProtoWallet(privateKey)
|
|
53
|
+
pushDrop = new PushDrop(wallet)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('Passes various test vectors', async () => {
|
|
57
|
+
await createDecodeRedeem()
|
|
58
|
+
await createDecodeRedeem([[0]])
|
|
59
|
+
await createDecodeRedeem([[1]])
|
|
60
|
+
await createDecodeRedeem([[0x81]])
|
|
61
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]])
|
|
62
|
+
await createDecodeRedeem([new Array(200).fill(0xff)])
|
|
63
|
+
await createDecodeRedeem([new Array(400).fill(0xff)])
|
|
64
|
+
await createDecodeRedeem([new Array(70000).fill(0xff)])
|
|
65
|
+
await createDecodeRedeem([[0], [1], [2]])
|
|
66
|
+
await createDecodeRedeem([[0], [1], [2], [3]])
|
|
67
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]], undefined, undefined, undefined, 'none', false)
|
|
68
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]], undefined, undefined, undefined, 'single', false)
|
|
69
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]], undefined, undefined, undefined, 'all', true)
|
|
70
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]], undefined, undefined, undefined, 'none', true)
|
|
71
|
+
await createDecodeRedeem([[3, 1, 4, 1, 5, 9]], undefined, undefined, undefined, 'single', true)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('lock', () => {
|
|
75
|
+
it('creates a correct locking script', async () => {
|
|
76
|
+
const fields = [
|
|
77
|
+
Utils.toArray('hello world', 'utf8'),
|
|
78
|
+
Utils.toArray('This is a field', 'utf8'),
|
|
79
|
+
[0xde, 0xad, 0xbe, 0xef]
|
|
80
|
+
]
|
|
81
|
+
const protocolID: [0 | 1 | 2, string] = [0, 'tests']
|
|
82
|
+
const keyID = 'test-key'
|
|
83
|
+
const counterparty = 'self'
|
|
84
|
+
const lockingScript = await pushDrop.lock(fields, protocolID, keyID, counterparty)
|
|
85
|
+
|
|
86
|
+
// Check that the locking script is not null
|
|
87
|
+
expect(lockingScript).toBeInstanceOf(Script)
|
|
88
|
+
|
|
89
|
+
// Decode the locking script and check the fields and locking public key
|
|
90
|
+
const decoded = await PushDrop.decode(lockingScript)
|
|
91
|
+
expect(decoded.fields).toEqual(fields)
|
|
92
|
+
const expectedPublicKey = (await wallet.getPublicKey({ protocolID, keyID, counterparty })).publicKey
|
|
93
|
+
expect(decoded.lockingPublicKey.toString()).toEqual(expectedPublicKey)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe('unlock', () => {
|
|
98
|
+
it('creates a correct unlocking script', async () => {
|
|
99
|
+
const fields = [
|
|
100
|
+
Utils.toArray('hello world', 'utf8'),
|
|
101
|
+
Utils.toArray('This is a field', 'utf8'),
|
|
102
|
+
[0xde, 0xad, 0xbe, 0xef]
|
|
103
|
+
]
|
|
104
|
+
const protocolID: [0 | 1 | 2, string] = [0, 'tests']
|
|
105
|
+
const keyID = 'test-key'
|
|
106
|
+
const counterparty = 'self'
|
|
107
|
+
const lockingScript = await pushDrop.lock(fields, protocolID, keyID, counterparty)
|
|
108
|
+
const satoshis = 1
|
|
109
|
+
const unlockingTemplate = await pushDrop.unlock(protocolID, keyID, counterparty)
|
|
110
|
+
const sourceTx = new Transaction(1, [], [{
|
|
111
|
+
lockingScript,
|
|
112
|
+
satoshis
|
|
113
|
+
}], 0)
|
|
114
|
+
const spendTx = new Transaction(1, [{
|
|
115
|
+
sourceTransaction: sourceTx,
|
|
116
|
+
sourceOutputIndex: 0,
|
|
117
|
+
sequence: 0xffffffff
|
|
118
|
+
}], [], 0)
|
|
119
|
+
const unlockingScript = await unlockingTemplate.sign(spendTx, 0)
|
|
120
|
+
expect(await unlockingTemplate.estimateLength()).toEqual(73)
|
|
121
|
+
const spend = new Spend({
|
|
122
|
+
sourceTXID: sourceTx.id('hex'),
|
|
123
|
+
sourceOutputIndex: 0,
|
|
124
|
+
sourceSatoshis: satoshis,
|
|
125
|
+
lockingScript,
|
|
126
|
+
transactionVersion: 1,
|
|
127
|
+
otherInputs: [],
|
|
128
|
+
inputIndex: 0,
|
|
129
|
+
unlockingScript,
|
|
130
|
+
outputs: [],
|
|
131
|
+
inputSequence: 0xffffffff,
|
|
132
|
+
lockTime: 0
|
|
133
|
+
})
|
|
134
|
+
const valid = spend.validate()
|
|
135
|
+
expect(valid).toBe(true)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe('decode', () => {
|
|
140
|
+
it('decodes the locking script correctly', async () => {
|
|
141
|
+
const fields = [
|
|
142
|
+
Utils.toArray('hello world', 'utf8'),
|
|
143
|
+
Utils.toArray('This is a field', 'utf8'),
|
|
144
|
+
[0xde, 0xad, 0xbe, 0xef]
|
|
145
|
+
]
|
|
146
|
+
const protocolID: [0 | 1 | 2, string] = [0, 'tests']
|
|
147
|
+
const keyID = 'test-key'
|
|
148
|
+
const counterparty = 'self'
|
|
149
|
+
|
|
150
|
+
const lockingScript = await pushDrop.lock(fields, protocolID, keyID, counterparty)
|
|
151
|
+
|
|
152
|
+
const decoded = await PushDrop.decode(lockingScript)
|
|
153
|
+
expect(decoded.fields).toEqual(fields)
|
|
154
|
+
const expectedPublicKey = (await wallet.getPublicKey({ protocolID, keyID, counterparty })).publicKey
|
|
155
|
+
expect(decoded.lockingPublicKey.toString()).toEqual(expectedPublicKey)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
})
|
|
@@ -16,7 +16,7 @@ export function defaultHttpClient (): HttpClient {
|
|
|
16
16
|
|
|
17
17
|
if (typeof window !== 'undefined' && typeof window.fetch === 'function') {
|
|
18
18
|
// Use fetch in a browser environment
|
|
19
|
-
return new FetchHttpClient(window.fetch)
|
|
19
|
+
return new FetchHttpClient(window.fetch.bind(window))
|
|
20
20
|
} else if (typeof require !== 'undefined') {
|
|
21
21
|
// Use Node.js https module
|
|
22
22
|
// eslint-disable-next-line
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { PrivateKey, PublicKey, SymmetricKey } from '../primitives/index.js'
|
|
2
|
+
import KeyDeriver from './KeyDeriver.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A cached version of KeyDeriver that caches the results of key derivation methods.
|
|
6
|
+
* This is useful for optimizing performance when the same keys are derived multiple times.
|
|
7
|
+
* It supports configurable cache size with sane defaults and maintains cache entries using LRU (Least Recently Used) eviction policy.
|
|
8
|
+
*/
|
|
9
|
+
export default class CachedKeyDeriver {
|
|
10
|
+
private readonly keyDeriver: KeyDeriver
|
|
11
|
+
private readonly cache: Map<string, any>
|
|
12
|
+
private readonly maxCacheSize: number
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initializes the CachedKeyDeriver instance with a root private key and optional cache settings.
|
|
16
|
+
* @param {PrivateKey | 'anyone'} rootKey - The root private key or the string 'anyone'.
|
|
17
|
+
* @param {Object} [options] - Optional settings for the cache.
|
|
18
|
+
* @param {number} [options.maxCacheSize=1000] - The maximum number of entries to store in the cache.
|
|
19
|
+
*/
|
|
20
|
+
constructor(rootKey: PrivateKey | 'anyone', options?: { maxCacheSize?: number }) {
|
|
21
|
+
this.keyDeriver = new KeyDeriver(rootKey)
|
|
22
|
+
this.cache = new Map<string, any>()
|
|
23
|
+
this.maxCacheSize = options?.maxCacheSize || 1000
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Derives a public key based on protocol ID, key ID, and counterparty.
|
|
28
|
+
* Caches the result for future calls with the same parameters.
|
|
29
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID including a security level and protocol name.
|
|
30
|
+
* @param {string} keyID - The key identifier.
|
|
31
|
+
* @param {PublicKey | string | 'self' | 'anyone'} counterparty - The counterparty's public key or a predefined value ('self' or 'anyone').
|
|
32
|
+
* @param {boolean} [forSelf=false] - Whether deriving for self.
|
|
33
|
+
* @returns {PublicKey} - The derived public key.
|
|
34
|
+
*/
|
|
35
|
+
derivePublicKey(
|
|
36
|
+
protocolID: [0 | 1 | 2, string],
|
|
37
|
+
keyID: string,
|
|
38
|
+
counterparty: PublicKey | string | 'self' | 'anyone',
|
|
39
|
+
forSelf: boolean = false
|
|
40
|
+
): PublicKey {
|
|
41
|
+
const cacheKey = this.generateCacheKey('derivePublicKey', protocolID, keyID, counterparty, forSelf)
|
|
42
|
+
if (this.cache.has(cacheKey)) {
|
|
43
|
+
return this.cacheGet(cacheKey)
|
|
44
|
+
} else {
|
|
45
|
+
const result = this.keyDeriver.derivePublicKey(protocolID, keyID, counterparty, forSelf)
|
|
46
|
+
this.cacheSet(cacheKey, result)
|
|
47
|
+
return result
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Derives a private key based on protocol ID, key ID, and counterparty.
|
|
53
|
+
* Caches the result for future calls with the same parameters.
|
|
54
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID including a security level and protocol name.
|
|
55
|
+
* @param {string} keyID - The key identifier.
|
|
56
|
+
* @param {PublicKey | string | 'self' | 'anyone'} counterparty - The counterparty's public key or a predefined value ('self' or 'anyone').
|
|
57
|
+
* @returns {PrivateKey} - The derived private key.
|
|
58
|
+
*/
|
|
59
|
+
derivePrivateKey(
|
|
60
|
+
protocolID: [0 | 1 | 2, string],
|
|
61
|
+
keyID: string,
|
|
62
|
+
counterparty: PublicKey | string | 'self' | 'anyone'
|
|
63
|
+
): PrivateKey {
|
|
64
|
+
const cacheKey = this.generateCacheKey('derivePrivateKey', protocolID, keyID, counterparty)
|
|
65
|
+
if (this.cache.has(cacheKey)) {
|
|
66
|
+
return this.cacheGet(cacheKey)
|
|
67
|
+
} else {
|
|
68
|
+
const result = this.keyDeriver.derivePrivateKey(protocolID, keyID, counterparty)
|
|
69
|
+
this.cacheSet(cacheKey, result)
|
|
70
|
+
return result
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Derives a symmetric key based on protocol ID, key ID, and counterparty.
|
|
76
|
+
* Caches the result for future calls with the same parameters.
|
|
77
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID including a security level and protocol name.
|
|
78
|
+
* @param {string} keyID - The key identifier.
|
|
79
|
+
* @param {PublicKey | string | 'self' | 'anyone'} counterparty - The counterparty's public key or a predefined value ('self' or 'anyone').
|
|
80
|
+
* @returns {SymmetricKey} - The derived symmetric key.
|
|
81
|
+
* @throws {Error} - Throws an error if attempting to derive a symmetric key for 'anyone'.
|
|
82
|
+
*/
|
|
83
|
+
deriveSymmetricKey(
|
|
84
|
+
protocolID: [0 | 1 | 2, string],
|
|
85
|
+
keyID: string,
|
|
86
|
+
counterparty: PublicKey | string | 'self' | 'anyone'
|
|
87
|
+
): SymmetricKey {
|
|
88
|
+
const cacheKey = this.generateCacheKey('deriveSymmetricKey', protocolID, keyID, counterparty)
|
|
89
|
+
if (this.cache.has(cacheKey)) {
|
|
90
|
+
return this.cacheGet(cacheKey)
|
|
91
|
+
} else {
|
|
92
|
+
const result = this.keyDeriver.deriveSymmetricKey(protocolID, keyID, counterparty)
|
|
93
|
+
this.cacheSet(cacheKey, result)
|
|
94
|
+
return result
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Reveals the shared secret between the root key and the counterparty.
|
|
100
|
+
* Caches the result for future calls with the same parameters.
|
|
101
|
+
* @param {PublicKey | string | 'self' | 'anyone'} counterparty - The counterparty's public key or a predefined value ('self' or 'anyone').
|
|
102
|
+
* @returns {number[]} - The shared secret as a number array.
|
|
103
|
+
* @throws {Error} - Throws an error if attempting to reveal a shared secret for 'self'.
|
|
104
|
+
*/
|
|
105
|
+
revealCounterpartySecret(counterparty: PublicKey | string | 'self' | 'anyone'): number[] {
|
|
106
|
+
const cacheKey = this.generateCacheKey('revealCounterpartySecret', counterparty)
|
|
107
|
+
if (this.cache.has(cacheKey)) {
|
|
108
|
+
return this.cacheGet(cacheKey)
|
|
109
|
+
} else {
|
|
110
|
+
const result = this.keyDeriver.revealCounterpartySecret(counterparty)
|
|
111
|
+
this.cacheSet(cacheKey, result)
|
|
112
|
+
return result
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Reveals the specific key association for a given protocol ID, key ID, and counterparty.
|
|
118
|
+
* Caches the result for future calls with the same parameters.
|
|
119
|
+
* @param {PublicKey | string | 'self' | 'anyone'} counterparty - The counterparty's public key or a predefined value ('self' or 'anyone').
|
|
120
|
+
* @param {[0 | 1 | 2, string]} protocolID - The protocol ID including a security level and protocol name.
|
|
121
|
+
* @param {string} keyID - The key identifier.
|
|
122
|
+
* @returns {number[]} - The specific key association as a number array.
|
|
123
|
+
*/
|
|
124
|
+
revealSpecificSecret(
|
|
125
|
+
counterparty: PublicKey | string | 'self' | 'anyone',
|
|
126
|
+
protocolID: [0 | 1 | 2, string],
|
|
127
|
+
keyID: string
|
|
128
|
+
): number[] {
|
|
129
|
+
const cacheKey = this.generateCacheKey('revealSpecificSecret', counterparty, protocolID, keyID)
|
|
130
|
+
if (this.cache.has(cacheKey)) {
|
|
131
|
+
return this.cacheGet(cacheKey)
|
|
132
|
+
} else {
|
|
133
|
+
const result = this.keyDeriver.revealSpecificSecret(counterparty, protocolID, keyID)
|
|
134
|
+
this.cacheSet(cacheKey, result)
|
|
135
|
+
return result
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generates a unique cache key based on the method name and input parameters.
|
|
141
|
+
* @param {string} methodName - The name of the method.
|
|
142
|
+
* @param {...any} args - The arguments passed to the method.
|
|
143
|
+
* @returns {string} - The generated cache key.
|
|
144
|
+
*/
|
|
145
|
+
private generateCacheKey(methodName: string, ...args: any[]): string {
|
|
146
|
+
const serializedArgs = args.map((arg) => this.serializeArgument(arg)).join('|')
|
|
147
|
+
return `${methodName}|${serializedArgs}`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Serializes an argument to a string for use in a cache key.
|
|
152
|
+
* @param {any} arg - The argument to serialize.
|
|
153
|
+
* @returns {string} - The serialized argument.
|
|
154
|
+
*/
|
|
155
|
+
private serializeArgument(arg: any): string {
|
|
156
|
+
if (arg instanceof PublicKey || arg instanceof PrivateKey) {
|
|
157
|
+
return arg.toString()
|
|
158
|
+
} else if (Array.isArray(arg)) {
|
|
159
|
+
return arg.map((item) => this.serializeArgument(item)).join(',')
|
|
160
|
+
} else if (typeof arg === 'object' && arg !== null) {
|
|
161
|
+
return JSON.stringify(arg)
|
|
162
|
+
} else {
|
|
163
|
+
return String(arg)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Retrieves an item from the cache and updates its position to reflect recent use.
|
|
169
|
+
* @param {string} cacheKey - The key of the cached item.
|
|
170
|
+
* @returns {any} - The cached value.
|
|
171
|
+
*/
|
|
172
|
+
private cacheGet(cacheKey: string): any {
|
|
173
|
+
const value = this.cache.get(cacheKey)
|
|
174
|
+
// Update the entry to reflect recent use
|
|
175
|
+
this.cache.delete(cacheKey)
|
|
176
|
+
this.cache.set(cacheKey, value)
|
|
177
|
+
return value
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Adds an item to the cache and evicts the least recently used item if necessary.
|
|
182
|
+
* @param {string} cacheKey - The key of the item to cache.
|
|
183
|
+
* @param {any} value - The value to cache.
|
|
184
|
+
*/
|
|
185
|
+
private cacheSet(cacheKey: string, value: any): void {
|
|
186
|
+
if (this.cache.size >= this.maxCacheSize) {
|
|
187
|
+
// Evict the least recently used item (first item in Map)
|
|
188
|
+
const firstKey = this.cache.keys().next().value
|
|
189
|
+
this.cache.delete(firstKey)
|
|
190
|
+
}
|
|
191
|
+
this.cache.set(cacheKey, value)
|
|
192
|
+
}
|
|
193
|
+
}
|