@sip-protocol/sdk 0.1.0 → 0.1.4
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/index.d.mts +3236 -1554
- package/dist/index.d.ts +3236 -1554
- package/dist/index.js +9185 -3521
- package/dist/index.mjs +8995 -3376
- package/package.json +5 -2
- package/src/adapters/near-intents.ts +48 -35
- package/src/adapters/oneclick-client.ts +9 -1
- package/src/compliance/compliance-manager.ts +1035 -0
- package/src/compliance/index.ts +43 -0
- package/src/index.ts +129 -2
- package/src/payment/index.ts +54 -0
- package/src/payment/payment.ts +623 -0
- package/src/payment/stablecoins.ts +306 -0
- package/src/privacy.ts +127 -94
- package/src/proofs/circuits/fulfillment_proof.json +1 -0
- package/src/proofs/circuits/funding_proof.json +1 -0
- package/src/proofs/circuits/validity_proof.json +1 -0
- package/src/proofs/interface.ts +13 -1
- package/src/proofs/noir.ts +967 -97
- package/src/secure-memory.ts +147 -0
- package/src/sip.ts +399 -37
- package/src/stealth.ts +116 -84
- package/src/treasury/index.ts +43 -0
- package/src/treasury/treasury.ts +911 -0
- package/src/wallet/hardware/index.ts +87 -0
- package/src/wallet/hardware/ledger.ts +628 -0
- package/src/wallet/hardware/mock.ts +667 -0
- package/src/wallet/hardware/trezor.ts +657 -0
- package/src/wallet/hardware/types.ts +317 -0
- package/src/wallet/index.ts +40 -0
- package/src/zcash/shielded-service.ts +59 -1
package/src/stealth.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
isValidCompressedPublicKey,
|
|
30
30
|
isValidPrivateKey,
|
|
31
31
|
} from './validation'
|
|
32
|
+
import { secureWipe, secureWipeAll } from './secure-memory'
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Generate a new stealth meta-address keypair
|
|
@@ -58,19 +59,28 @@ export function generateStealthMetaAddress(
|
|
|
58
59
|
const spendingPrivateKey = randomBytes(32)
|
|
59
60
|
const viewingPrivateKey = randomBytes(32)
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
try {
|
|
63
|
+
// Derive public keys
|
|
64
|
+
const spendingKey = secp256k1.getPublicKey(spendingPrivateKey, true)
|
|
65
|
+
const viewingKey = secp256k1.getPublicKey(viewingPrivateKey, true)
|
|
66
|
+
|
|
67
|
+
// Convert to hex strings before wiping buffers
|
|
68
|
+
const result = {
|
|
69
|
+
metaAddress: {
|
|
70
|
+
spendingKey: `0x${bytesToHex(spendingKey)}` as HexString,
|
|
71
|
+
viewingKey: `0x${bytesToHex(viewingKey)}` as HexString,
|
|
72
|
+
chain,
|
|
73
|
+
label,
|
|
74
|
+
},
|
|
75
|
+
spendingPrivateKey: `0x${bytesToHex(spendingPrivateKey)}` as HexString,
|
|
76
|
+
viewingPrivateKey: `0x${bytesToHex(viewingPrivateKey)}` as HexString,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return result
|
|
80
|
+
} finally {
|
|
81
|
+
// Securely wipe private key buffers
|
|
82
|
+
// Note: The hex strings returned to caller must be handled securely by them
|
|
83
|
+
secureWipeAll(spendingPrivateKey, viewingPrivateKey)
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
|
|
@@ -128,41 +138,47 @@ export function generateStealthAddress(
|
|
|
128
138
|
|
|
129
139
|
// Generate ephemeral keypair
|
|
130
140
|
const ephemeralPrivateKey = randomBytes(32)
|
|
131
|
-
const ephemeralPublicKey = secp256k1.getPublicKey(ephemeralPrivateKey, true)
|
|
132
|
-
|
|
133
|
-
// Parse recipient's keys (remove 0x prefix)
|
|
134
|
-
const spendingKeyBytes = hexToBytes(recipientMetaAddress.spendingKey.slice(2))
|
|
135
|
-
const viewingKeyBytes = hexToBytes(recipientMetaAddress.viewingKey.slice(2))
|
|
136
|
-
|
|
137
|
-
// Compute shared secret: S = r * P (ephemeral private * spending public)
|
|
138
|
-
const sharedSecretPoint = secp256k1.getSharedSecret(
|
|
139
|
-
ephemeralPrivateKey,
|
|
140
|
-
spendingKeyBytes,
|
|
141
|
-
)
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
try {
|
|
143
|
+
const ephemeralPublicKey = secp256k1.getPublicKey(ephemeralPrivateKey, true)
|
|
145
144
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
// Parse recipient's keys (remove 0x prefix)
|
|
146
|
+
const spendingKeyBytes = hexToBytes(recipientMetaAddress.spendingKey.slice(2))
|
|
147
|
+
const viewingKeyBytes = hexToBytes(recipientMetaAddress.viewingKey.slice(2))
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// Compute view tag (first byte of hash for efficient scanning)
|
|
157
|
-
const viewTag = sharedSecretHash[0]
|
|
149
|
+
// Compute shared secret: S = r * P (ephemeral private * spending public)
|
|
150
|
+
const sharedSecretPoint = secp256k1.getSharedSecret(
|
|
151
|
+
ephemeralPrivateKey,
|
|
152
|
+
spendingKeyBytes,
|
|
153
|
+
)
|
|
158
154
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
// Hash the shared secret for use as a scalar
|
|
156
|
+
const sharedSecretHash = sha256(sharedSecretPoint)
|
|
157
|
+
|
|
158
|
+
// Compute stealth address: A = Q + hash(S)*G
|
|
159
|
+
// First get hash(S)*G
|
|
160
|
+
const hashTimesG = secp256k1.getPublicKey(sharedSecretHash, true)
|
|
161
|
+
|
|
162
|
+
// Then add to viewing key Q
|
|
163
|
+
const viewingKeyPoint = secp256k1.ProjectivePoint.fromHex(viewingKeyBytes)
|
|
164
|
+
const hashTimesGPoint = secp256k1.ProjectivePoint.fromHex(hashTimesG)
|
|
165
|
+
const stealthPoint = viewingKeyPoint.add(hashTimesGPoint)
|
|
166
|
+
const stealthAddressBytes = stealthPoint.toRawBytes(true)
|
|
167
|
+
|
|
168
|
+
// Compute view tag (first byte of hash for efficient scanning)
|
|
169
|
+
const viewTag = sharedSecretHash[0]
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
stealthAddress: {
|
|
173
|
+
address: `0x${bytesToHex(stealthAddressBytes)}` as HexString,
|
|
174
|
+
ephemeralPublicKey: `0x${bytesToHex(ephemeralPublicKey)}` as HexString,
|
|
175
|
+
viewTag,
|
|
176
|
+
},
|
|
177
|
+
sharedSecret: `0x${bytesToHex(sharedSecretHash)}` as HexString,
|
|
178
|
+
}
|
|
179
|
+
} finally {
|
|
180
|
+
// Securely wipe ephemeral private key
|
|
181
|
+
secureWipe(ephemeralPrivateKey)
|
|
166
182
|
}
|
|
167
183
|
}
|
|
168
184
|
|
|
@@ -242,28 +258,38 @@ export function deriveStealthPrivateKey(
|
|
|
242
258
|
const viewingPrivBytes = hexToBytes(viewingPrivateKey.slice(2))
|
|
243
259
|
const ephemeralPubBytes = hexToBytes(stealthAddress.ephemeralPublicKey.slice(2))
|
|
244
260
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
261
|
+
try {
|
|
262
|
+
// Compute shared secret: S = p * R (spending private * ephemeral public)
|
|
263
|
+
const sharedSecretPoint = secp256k1.getSharedSecret(
|
|
264
|
+
spendingPrivBytes,
|
|
265
|
+
ephemeralPubBytes,
|
|
266
|
+
)
|
|
250
267
|
|
|
251
|
-
|
|
252
|
-
|
|
268
|
+
// Hash the shared secret
|
|
269
|
+
const sharedSecretHash = sha256(sharedSecretPoint)
|
|
253
270
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
271
|
+
// Derive stealth private key: q + hash(S) mod n
|
|
272
|
+
// Where q is the viewing private key
|
|
273
|
+
const viewingScalar = bytesToBigInt(viewingPrivBytes)
|
|
274
|
+
const hashScalar = bytesToBigInt(sharedSecretHash)
|
|
275
|
+
const stealthPrivateScalar = (viewingScalar + hashScalar) % secp256k1.CURVE.n
|
|
259
276
|
|
|
260
|
-
|
|
261
|
-
|
|
277
|
+
// Convert back to bytes
|
|
278
|
+
const stealthPrivateKey = bigIntToBytes(stealthPrivateScalar, 32)
|
|
262
279
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
280
|
+
const result = {
|
|
281
|
+
stealthAddress: stealthAddress.address,
|
|
282
|
+
ephemeralPublicKey: stealthAddress.ephemeralPublicKey,
|
|
283
|
+
privateKey: `0x${bytesToHex(stealthPrivateKey)}` as HexString,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Wipe derived key buffer after converting to hex
|
|
287
|
+
secureWipe(stealthPrivateKey)
|
|
288
|
+
|
|
289
|
+
return result
|
|
290
|
+
} finally {
|
|
291
|
+
// Securely wipe input private key buffers
|
|
292
|
+
secureWipeAll(spendingPrivBytes, viewingPrivBytes)
|
|
267
293
|
}
|
|
268
294
|
}
|
|
269
295
|
|
|
@@ -305,33 +331,39 @@ export function checkStealthAddress(
|
|
|
305
331
|
const viewingPrivBytes = hexToBytes(viewingPrivateKey.slice(2))
|
|
306
332
|
const ephemeralPubBytes = hexToBytes(stealthAddress.ephemeralPublicKey.slice(2))
|
|
307
333
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
334
|
+
try {
|
|
335
|
+
// Quick check: compute shared secret and verify view tag first
|
|
336
|
+
const sharedSecretPoint = secp256k1.getSharedSecret(
|
|
337
|
+
spendingPrivBytes,
|
|
338
|
+
ephemeralPubBytes,
|
|
339
|
+
)
|
|
340
|
+
const sharedSecretHash = sha256(sharedSecretPoint)
|
|
314
341
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
342
|
+
// View tag check (optimization - reject quickly if doesn't match)
|
|
343
|
+
if (sharedSecretHash[0] !== stealthAddress.viewTag) {
|
|
344
|
+
return false
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Full check: derive the expected stealth address
|
|
348
|
+
const viewingScalar = bytesToBigInt(viewingPrivBytes)
|
|
349
|
+
const hashScalar = bytesToBigInt(sharedSecretHash)
|
|
350
|
+
const stealthPrivateScalar = (viewingScalar + hashScalar) % secp256k1.CURVE.n
|
|
319
351
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const stealthPrivateScalar = (viewingScalar + hashScalar) % secp256k1.CURVE.n
|
|
352
|
+
// Compute expected public key from derived private key
|
|
353
|
+
const derivedKeyBytes = bigIntToBytes(stealthPrivateScalar, 32)
|
|
354
|
+
const expectedPubKey = secp256k1.getPublicKey(derivedKeyBytes, true)
|
|
324
355
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
bigIntToBytes(stealthPrivateScalar, 32),
|
|
328
|
-
true,
|
|
329
|
-
)
|
|
356
|
+
// Wipe derived key immediately after use
|
|
357
|
+
secureWipe(derivedKeyBytes)
|
|
330
358
|
|
|
331
|
-
|
|
332
|
-
|
|
359
|
+
// Compare with provided stealth address
|
|
360
|
+
const providedAddress = hexToBytes(stealthAddress.address.slice(2))
|
|
333
361
|
|
|
334
|
-
|
|
362
|
+
return bytesToHex(expectedPubKey) === bytesToHex(providedAddress)
|
|
363
|
+
} finally {
|
|
364
|
+
// Securely wipe input private key buffers
|
|
365
|
+
secureWipeAll(spendingPrivBytes, viewingPrivBytes)
|
|
366
|
+
}
|
|
335
367
|
}
|
|
336
368
|
|
|
337
369
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAO Treasury Module for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* Provides privacy-preserving treasury management with multi-sig support
|
|
5
|
+
* for DAOs and organizations.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { Treasury, getStablecoin } from '@sip-protocol/sdk'
|
|
10
|
+
*
|
|
11
|
+
* // Create a 2-of-3 multi-sig treasury
|
|
12
|
+
* const treasury = await Treasury.create({
|
|
13
|
+
* name: 'DAO Treasury',
|
|
14
|
+
* chain: 'ethereum',
|
|
15
|
+
* members: [
|
|
16
|
+
* { address: alice, publicKey: alicePub, role: 'owner', name: 'Alice' },
|
|
17
|
+
* { address: bob, publicKey: bobPub, role: 'signer', name: 'Bob' },
|
|
18
|
+
* { address: carol, publicKey: carolPub, role: 'signer', name: 'Carol' },
|
|
19
|
+
* ],
|
|
20
|
+
* signingThreshold: 2,
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // Create a batch payment proposal for payroll
|
|
24
|
+
* const proposal = await treasury.createBatchProposal({
|
|
25
|
+
* title: 'November Payroll',
|
|
26
|
+
* token: getStablecoin('USDC', 'ethereum')!,
|
|
27
|
+
* recipients: [
|
|
28
|
+
* { address: emp1MetaAddr, amount: 5000_000000n, purpose: 'salary' },
|
|
29
|
+
* { address: emp2MetaAddr, amount: 4500_000000n, purpose: 'salary' },
|
|
30
|
+
* { address: emp3MetaAddr, amount: 6000_000000n, purpose: 'salary' },
|
|
31
|
+
* ],
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* // Collect signatures
|
|
35
|
+
* await treasury.signProposal(proposal.proposalId, alice, alicePrivKey)
|
|
36
|
+
* await treasury.signProposal(proposal.proposalId, bob, bobPrivKey)
|
|
37
|
+
*
|
|
38
|
+
* // Execute when approved
|
|
39
|
+
* const payments = await treasury.executeProposal(proposal.proposalId)
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
export { Treasury } from './treasury'
|