@taquito/sapling 24.2.0 → 24.3.0-beta.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/lib/errors.js +10 -10
- package/dist/lib/sapling-forger/sapling-forger.js +3 -3
- package/dist/lib/sapling-keys/helpers.js +3 -3
- package/dist/lib/sapling-keys/in-memory-proving-key.js +16 -30
- package/dist/lib/sapling-keys/in-memory-spending-key.js +48 -68
- package/dist/lib/sapling-keys/in-memory-viewing-key.js +20 -37
- package/dist/lib/sapling-module-wrapper.js +33 -57
- package/dist/lib/sapling-state/sapling-state.js +83 -106
- package/dist/lib/sapling-state/utils.js +7 -18
- package/dist/lib/sapling-tx-builder/sapling-transactions-builder.js +194 -216
- package/dist/lib/sapling-tx-viewer/sapling-transaction-viewer.js +98 -135
- package/dist/lib/taquito-sapling.js +84 -119
- package/dist/lib/version.js +2 -2
- package/dist/taquito-sapling.es6.js +605 -727
- package/dist/taquito-sapling.es6.js.map +1 -1
- package/dist/taquito-sapling.umd.js +604 -726
- package/dist/taquito-sapling.umd.js.map +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/errors.d.ts +11 -8
- package/dist/types/sapling-forger/sapling-forger.d.ts +5 -5
- package/dist/types/sapling-keys/helpers.d.ts +1 -1
- package/dist/types/sapling-keys/in-memory-proving-key.d.ts +3 -4
- package/dist/types/sapling-keys/in-memory-spending-key.d.ts +4 -5
- package/dist/types/sapling-keys/in-memory-viewing-key.d.ts +9 -9
- package/dist/types/sapling-module-wrapper.d.ts +11 -11
- package/dist/types/sapling-state/sapling-state.d.ts +1 -1
- package/dist/types/sapling-state/utils.d.ts +1 -1
- package/dist/types/sapling-tx-builder/sapling-transactions-builder.d.ts +3 -3
- package/dist/types/sapling-tx-viewer/helpers.d.ts +1 -1
- package/dist/types/sapling-tx-viewer/sapling-transaction-viewer.d.ts +4 -4
- package/dist/types/taquito-sapling.d.ts +5 -5
- package/package.json +26 -13
- package/LICENSE +0 -202
|
@@ -41,28 +41,6 @@
|
|
|
41
41
|
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
function __rest(s, e) {
|
|
45
|
-
var t = {};
|
|
46
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
47
|
-
t[p] = s[p];
|
|
48
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
49
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
50
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
51
|
-
t[p[i]] = s[p[i]];
|
|
52
|
-
}
|
|
53
|
-
return t;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
57
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
58
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
59
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
60
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
61
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
62
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
44
|
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
67
45
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
68
46
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
@@ -83,20 +61,20 @@
|
|
|
83
61
|
|
|
84
62
|
/**
|
|
85
63
|
* @category Error
|
|
86
|
-
*
|
|
64
|
+
* Error indicates the spending key is invalid
|
|
87
65
|
*/
|
|
88
66
|
class InvalidSpendingKey extends core.ParameterValidationError {
|
|
89
|
-
constructor(
|
|
67
|
+
constructor(a, b) {
|
|
68
|
+
const errorDetail = arguments.length >= 2 ? b : a;
|
|
90
69
|
super();
|
|
91
|
-
this.sk = sk;
|
|
92
|
-
this.errorDetail = errorDetail;
|
|
93
70
|
this.name = 'InvalidSpendingKey';
|
|
94
|
-
this.
|
|
71
|
+
this.errorDetail = errorDetail;
|
|
72
|
+
this.message = `Invalid spending key: ${errorDetail}.`;
|
|
95
73
|
}
|
|
96
74
|
}
|
|
97
75
|
/**
|
|
98
76
|
* @category Error
|
|
99
|
-
*
|
|
77
|
+
* Error indicates an invalid Merkle tree being passed
|
|
100
78
|
*/
|
|
101
79
|
class InvalidMerkleTreeError extends core.ParameterValidationError {
|
|
102
80
|
constructor(root) {
|
|
@@ -108,7 +86,7 @@
|
|
|
108
86
|
}
|
|
109
87
|
/**
|
|
110
88
|
* @category Error
|
|
111
|
-
*
|
|
89
|
+
* Error indicates a failure when trying to construct the Merkle tree
|
|
112
90
|
*/
|
|
113
91
|
class TreeConstructionFailure extends core.TaquitoError {
|
|
114
92
|
constructor(message) {
|
|
@@ -119,7 +97,7 @@
|
|
|
119
97
|
}
|
|
120
98
|
/**
|
|
121
99
|
* @category Error
|
|
122
|
-
*
|
|
100
|
+
* Error indicates the memo is invalid
|
|
123
101
|
*/
|
|
124
102
|
class InvalidMemo extends core.ParameterValidationError {
|
|
125
103
|
constructor(memo, errorDetails) {
|
|
@@ -132,7 +110,7 @@
|
|
|
132
110
|
}
|
|
133
111
|
/**
|
|
134
112
|
* @category Error
|
|
135
|
-
*
|
|
113
|
+
* Error indicates not enough balance to prepare the sapling transaction
|
|
136
114
|
*/
|
|
137
115
|
class InsufficientBalance extends core.TaquitoError {
|
|
138
116
|
constructor(realBalance, amountToSpend) {
|
|
@@ -145,7 +123,7 @@
|
|
|
145
123
|
}
|
|
146
124
|
/**
|
|
147
125
|
* @category Error
|
|
148
|
-
*
|
|
126
|
+
* Error indicates SaplingTransactionViewer failure
|
|
149
127
|
*/
|
|
150
128
|
class SaplingTransactionViewerError extends core.TaquitoError {
|
|
151
129
|
constructor(message) {
|
|
@@ -179,7 +157,7 @@
|
|
|
179
157
|
|
|
180
158
|
class SaplingForger {
|
|
181
159
|
/**
|
|
182
|
-
*
|
|
160
|
+
* Forge sapling transactions
|
|
183
161
|
* @param spendDescriptions the list of spend descriptions
|
|
184
162
|
* @param outputDescriptions the list of output descriptions
|
|
185
163
|
* @param signature signature hash
|
|
@@ -204,7 +182,7 @@
|
|
|
204
182
|
]);
|
|
205
183
|
}
|
|
206
184
|
/**
|
|
207
|
-
*
|
|
185
|
+
* Forge list of spend descriptions
|
|
208
186
|
* @param spendDescriptions list of spend descriptions
|
|
209
187
|
* @returns concatenated forged bytes of type Buffer
|
|
210
188
|
*/
|
|
@@ -226,7 +204,7 @@
|
|
|
226
204
|
]);
|
|
227
205
|
}
|
|
228
206
|
/**
|
|
229
|
-
*
|
|
207
|
+
* Forge list of output descriptions
|
|
230
208
|
* @param outputDescriptions list of output descriptions
|
|
231
209
|
* @returns concatenated forged bytes of type Buffer
|
|
232
210
|
*/
|
|
@@ -279,7 +257,7 @@
|
|
|
279
257
|
|
|
280
258
|
var _SaplingTransactionViewer_viewingKeyProvider, _SaplingTransactionViewer_readProvider, _SaplingTransactionViewer_saplingContractId;
|
|
281
259
|
/**
|
|
282
|
-
*
|
|
260
|
+
* Allows to retrieve and decrypt sapling transactions using on a viewing key
|
|
283
261
|
*
|
|
284
262
|
* @param inMemoryViewingKey Holds the sapling viewing key
|
|
285
263
|
* @param saplingContractId Address of the sapling contract or sapling id if the smart contract contains multiple sapling states
|
|
@@ -295,102 +273,120 @@
|
|
|
295
273
|
__classPrivateFieldSet(this, _SaplingTransactionViewer_readProvider, readProvider, "f");
|
|
296
274
|
}
|
|
297
275
|
/**
|
|
298
|
-
*
|
|
276
|
+
* Retrieve the unspent balance associated with the configured viewing key and sapling state
|
|
299
277
|
*
|
|
300
278
|
* @returns the balance in mutez represented as a BigNumber
|
|
301
279
|
*
|
|
302
280
|
*/
|
|
303
|
-
getBalance() {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
balance = balance.plus(valueBigNumber);
|
|
314
|
-
}
|
|
281
|
+
async getBalance() {
|
|
282
|
+
let balance = new BigNumber(0);
|
|
283
|
+
const { commitments_and_ciphertexts, nullifiers } = await this.getSaplingDiff();
|
|
284
|
+
for (let i = 0; i < commitments_and_ciphertexts.length; i++) {
|
|
285
|
+
const decrypted = await this.decryptCiphertextAsReceiver(commitments_and_ciphertexts[i]);
|
|
286
|
+
if (decrypted) {
|
|
287
|
+
const valueBigNumber = convertValueToBigNumber(decrypted.value);
|
|
288
|
+
const isSpent = await this.isSpent(decrypted.paymentAddress, valueBigNumber.toString(), decrypted.randomCommitmentTrapdoor, i, nullifiers);
|
|
289
|
+
if (!isSpent) {
|
|
290
|
+
balance = balance.plus(valueBigNumber);
|
|
315
291
|
}
|
|
316
292
|
}
|
|
317
|
-
|
|
318
|
-
|
|
293
|
+
}
|
|
294
|
+
return balance;
|
|
319
295
|
}
|
|
320
296
|
/**
|
|
321
|
-
*
|
|
297
|
+
* Retrieve all the incoming and outgoing transactions associated with the configured viewing key.
|
|
322
298
|
* The response properties are in Uint8Array format; use the getIncomingAndOutgoingTransactions method for readable properties
|
|
323
299
|
*
|
|
324
300
|
*/
|
|
325
|
-
getIncomingAndOutgoingTransactionsRaw() {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
incoming.push(Object.assign(Object.assign({}, decryptedAsReceiver), { isSpent, position: i }));
|
|
337
|
-
}
|
|
338
|
-
if (decryptedAsSender) {
|
|
339
|
-
outgoing.push(decryptedAsSender);
|
|
340
|
-
}
|
|
301
|
+
async getIncomingAndOutgoingTransactionsRaw() {
|
|
302
|
+
const incoming = [];
|
|
303
|
+
const outgoing = [];
|
|
304
|
+
const { commitments_and_ciphertexts, nullifiers } = await this.getSaplingDiff();
|
|
305
|
+
for (let i = 0; i < commitments_and_ciphertexts.length; i++) {
|
|
306
|
+
const decryptedAsReceiver = await this.decryptCiphertextAsReceiver(commitments_and_ciphertexts[i]);
|
|
307
|
+
const decryptedAsSender = await this.decryptCiphertextAsSender(commitments_and_ciphertexts[i]);
|
|
308
|
+
if (decryptedAsReceiver) {
|
|
309
|
+
const balance = convertValueToBigNumber(decryptedAsReceiver.value);
|
|
310
|
+
const isSpent = await this.isSpent(decryptedAsReceiver.paymentAddress, balance.toString(), decryptedAsReceiver.randomCommitmentTrapdoor, i, nullifiers);
|
|
311
|
+
incoming.push({ ...decryptedAsReceiver, isSpent, position: i });
|
|
341
312
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
313
|
+
if (decryptedAsSender) {
|
|
314
|
+
outgoing.push(decryptedAsSender);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return {
|
|
318
|
+
incoming,
|
|
319
|
+
outgoing,
|
|
320
|
+
};
|
|
347
321
|
}
|
|
348
322
|
/**
|
|
349
|
-
*
|
|
323
|
+
* Retrieve all the incoming and outgoing decoded transactions associated with the configured viewing key
|
|
350
324
|
*
|
|
351
325
|
*/
|
|
352
|
-
getIncomingAndOutgoingTransactions() {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
var { isSpent } = _a, rest = __rest(_a, ["isSpent"]);
|
|
357
|
-
return Object.assign(Object.assign({}, readableFormat(rest)), { isSpent });
|
|
358
|
-
});
|
|
359
|
-
const outgoing = tx.outgoing.map((outgoingTx) => {
|
|
360
|
-
return readableFormat(outgoingTx);
|
|
361
|
-
});
|
|
362
|
-
return { incoming, outgoing };
|
|
326
|
+
async getIncomingAndOutgoingTransactions() {
|
|
327
|
+
const tx = await this.getIncomingAndOutgoingTransactionsRaw();
|
|
328
|
+
const incoming = tx.incoming.map(({ isSpent, ...rest }) => {
|
|
329
|
+
return { ...readableFormat(rest), isSpent };
|
|
363
330
|
});
|
|
331
|
+
const outgoing = tx.outgoing.map((outgoingTx) => {
|
|
332
|
+
return readableFormat(outgoingTx);
|
|
333
|
+
});
|
|
334
|
+
return { incoming, outgoing };
|
|
364
335
|
}
|
|
365
|
-
getSaplingDiff() {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
336
|
+
async getSaplingDiff() {
|
|
337
|
+
let saplingDiffResponse;
|
|
338
|
+
if (__classPrivateFieldGet(this, _SaplingTransactionViewer_saplingContractId, "f").saplingId) {
|
|
339
|
+
saplingDiffResponse = await __classPrivateFieldGet(this, _SaplingTransactionViewer_readProvider, "f").getSaplingDiffById({ id: __classPrivateFieldGet(this, _SaplingTransactionViewer_saplingContractId, "f").saplingId }, 'head');
|
|
340
|
+
}
|
|
341
|
+
else if (__classPrivateFieldGet(this, _SaplingTransactionViewer_saplingContractId, "f").contractAddress) {
|
|
342
|
+
saplingDiffResponse = await __classPrivateFieldGet(this, _SaplingTransactionViewer_readProvider, "f").getSaplingDiffByContract(__classPrivateFieldGet(this, _SaplingTransactionViewer_saplingContractId, "f").contractAddress, 'head');
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
throw new SaplingTransactionViewerError('A contract address or a sapling id was expected in the SaplingTransactionViewer constructor.');
|
|
346
|
+
}
|
|
347
|
+
return saplingDiffResponse;
|
|
348
|
+
}
|
|
349
|
+
async decryptCiphertextAsReceiver(commitmentsAndCiphertexts) {
|
|
350
|
+
const commitment = commitmentsAndCiphertexts[0];
|
|
351
|
+
const { epk, payload_enc, nonce_enc } = commitmentsAndCiphertexts[1];
|
|
352
|
+
const incomingViewingKey = await __classPrivateFieldGet(this, _SaplingTransactionViewer_viewingKeyProvider, "f").getIncomingViewingKey();
|
|
353
|
+
const keyAgreement = await sapling__namespace.keyAgreement(epk, incomingViewingKey);
|
|
354
|
+
const keyAgreementHash = blake.blake2b(keyAgreement, Buffer.from(KDF_KEY), 32);
|
|
355
|
+
const decrypted = await this.decryptCiphertext(keyAgreementHash, utils.hex2buf(nonce_enc), utils.hex2buf(payload_enc));
|
|
356
|
+
if (decrypted) {
|
|
357
|
+
const { diversifier, value, randomCommitmentTrapdoor: rcm, memo, } = this.extractTransactionProperties(decrypted);
|
|
358
|
+
const paymentAddress = bufToUint8Array(await sapling__namespace.getRawPaymentAddressFromIncomingViewingKey(incomingViewingKey, diversifier));
|
|
359
|
+
try {
|
|
360
|
+
const valid = await sapling__namespace.verifyCommitment(commitment, paymentAddress, convertValueToBigNumber(value).toString(), rcm);
|
|
361
|
+
if (valid) {
|
|
362
|
+
return { value, memo, paymentAddress, randomCommitmentTrapdoor: rcm };
|
|
363
|
+
}
|
|
373
364
|
}
|
|
374
|
-
|
|
375
|
-
|
|
365
|
+
catch (ex) {
|
|
366
|
+
if (!/invalid value/.test(ex)) {
|
|
367
|
+
throw ex;
|
|
368
|
+
}
|
|
376
369
|
}
|
|
377
|
-
|
|
378
|
-
});
|
|
370
|
+
}
|
|
379
371
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
372
|
+
async decryptCiphertextAsSender(commitmentsAndCiphertexts) {
|
|
373
|
+
const commitment = commitmentsAndCiphertexts[0];
|
|
374
|
+
const { epk, payload_enc, nonce_enc, payload_out, nonce_out, cv } = commitmentsAndCiphertexts[1];
|
|
375
|
+
const outgoingViewingKey = await __classPrivateFieldGet(this, _SaplingTransactionViewer_viewingKeyProvider, "f").getOutgoingViewingKey();
|
|
376
|
+
const concat = cv.concat(commitment, epk, outgoingViewingKey.toString('hex'));
|
|
377
|
+
const outgoingCipherKey = blake.blake2b(Buffer.from(concat, 'hex'), Buffer.from(OCK_KEY), 32);
|
|
378
|
+
const decryptedOut = await this.decryptCiphertext(outgoingCipherKey, utils.hex2buf(nonce_out), utils.hex2buf(payload_out));
|
|
379
|
+
if (decryptedOut) {
|
|
380
|
+
const { recipientDiversifiedTransmissionKey: pkd, ephemeralPrivateKey: esk } = this.extractPkdAndEsk(decryptedOut);
|
|
381
|
+
const keyAgreement = await sapling__namespace.keyAgreement(pkd, esk);
|
|
386
382
|
const keyAgreementHash = blake.blake2b(keyAgreement, Buffer.from(KDF_KEY), 32);
|
|
387
|
-
const
|
|
388
|
-
if (
|
|
389
|
-
const { diversifier, value, randomCommitmentTrapdoor: rcm, memo, } = this.extractTransactionProperties(
|
|
390
|
-
const paymentAddress =
|
|
383
|
+
const decryptedEnc = await this.decryptCiphertext(keyAgreementHash, utils.hex2buf(nonce_enc), utils.hex2buf(payload_enc));
|
|
384
|
+
if (decryptedEnc) {
|
|
385
|
+
const { diversifier, value, randomCommitmentTrapdoor: rcm, memo, } = this.extractTransactionProperties(decryptedEnc);
|
|
386
|
+
const paymentAddress = utils.mergebuf(diversifier, pkd);
|
|
391
387
|
try {
|
|
392
|
-
const
|
|
393
|
-
if (
|
|
388
|
+
const isValid = await sapling__namespace.verifyCommitment(commitment, paymentAddress, convertValueToBigNumber(value).toString(), rcm);
|
|
389
|
+
if (isValid) {
|
|
394
390
|
return { value, memo, paymentAddress, randomCommitmentTrapdoor: rcm };
|
|
395
391
|
}
|
|
396
392
|
}
|
|
@@ -400,43 +396,10 @@
|
|
|
400
396
|
}
|
|
401
397
|
}
|
|
402
398
|
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
decryptCiphertextAsSender(commitmentsAndCiphertexts) {
|
|
406
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
407
|
-
const commitment = commitmentsAndCiphertexts[0];
|
|
408
|
-
const { epk, payload_enc, nonce_enc, payload_out, nonce_out, cv } = commitmentsAndCiphertexts[1];
|
|
409
|
-
const outgoingViewingKey = yield __classPrivateFieldGet(this, _SaplingTransactionViewer_viewingKeyProvider, "f").getOutgoingViewingKey();
|
|
410
|
-
const concat = cv.concat(commitment, epk, outgoingViewingKey.toString('hex'));
|
|
411
|
-
const outgoingCipherKey = blake.blake2b(Buffer.from(concat, 'hex'), Buffer.from(OCK_KEY), 32);
|
|
412
|
-
const decryptedOut = yield this.decryptCiphertext(outgoingCipherKey, utils.hex2buf(nonce_out), utils.hex2buf(payload_out));
|
|
413
|
-
if (decryptedOut) {
|
|
414
|
-
const { recipientDiversifiedTransmissionKey: pkd, ephemeralPrivateKey: esk } = this.extractPkdAndEsk(decryptedOut);
|
|
415
|
-
const keyAgreement = yield sapling__namespace.keyAgreement(pkd, esk);
|
|
416
|
-
const keyAgreementHash = blake.blake2b(keyAgreement, Buffer.from(KDF_KEY), 32);
|
|
417
|
-
const decryptedEnc = yield this.decryptCiphertext(keyAgreementHash, utils.hex2buf(nonce_enc), utils.hex2buf(payload_enc));
|
|
418
|
-
if (decryptedEnc) {
|
|
419
|
-
const { diversifier, value, randomCommitmentTrapdoor: rcm, memo, } = this.extractTransactionProperties(decryptedEnc);
|
|
420
|
-
const paymentAddress = utils.mergebuf(diversifier, pkd);
|
|
421
|
-
try {
|
|
422
|
-
const isValid = yield sapling__namespace.verifyCommitment(commitment, paymentAddress, convertValueToBigNumber(value).toString(), rcm);
|
|
423
|
-
if (isValid) {
|
|
424
|
-
return { value, memo, paymentAddress, randomCommitmentTrapdoor: rcm };
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
catch (ex) {
|
|
428
|
-
if (!/invalid value/.test(ex)) {
|
|
429
|
-
throw ex;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
});
|
|
399
|
+
}
|
|
435
400
|
}
|
|
436
|
-
decryptCiphertext(keyAgreementHash, nonce, payload) {
|
|
437
|
-
return
|
|
438
|
-
return nacl.openSecretBox(keyAgreementHash, nonce, payload);
|
|
439
|
-
});
|
|
401
|
+
async decryptCiphertext(keyAgreementHash, nonce, payload) {
|
|
402
|
+
return nacl.openSecretBox(keyAgreementHash, nonce, payload);
|
|
440
403
|
}
|
|
441
404
|
extractTransactionProperties(decrypted) {
|
|
442
405
|
return {
|
|
@@ -453,11 +416,9 @@
|
|
|
453
416
|
ephemeralPrivateKey: decrypted.slice(32),
|
|
454
417
|
};
|
|
455
418
|
}
|
|
456
|
-
isSpent(address, value, randomCommitmentTrapdoor, position, nullifiers) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
return nullifiers.includes(computedNullifier.toString('hex'));
|
|
460
|
-
});
|
|
419
|
+
async isSpent(address, value, randomCommitmentTrapdoor, position, nullifiers) {
|
|
420
|
+
const computedNullifier = await sapling__namespace.computeNullifier(__classPrivateFieldGet(this, _SaplingTransactionViewer_viewingKeyProvider, "f").getFullViewingKey(), address, value, randomCommitmentTrapdoor, position);
|
|
421
|
+
return nullifiers.includes(computedNullifier.toString('hex'));
|
|
461
422
|
}
|
|
462
423
|
}
|
|
463
424
|
_SaplingTransactionViewer_viewingKeyProvider = new WeakMap(), _SaplingTransactionViewer_readProvider = new WeakMap(), _SaplingTransactionViewer_saplingContractId = new WeakMap();
|
|
@@ -475,7 +436,7 @@
|
|
|
475
436
|
return pairs;
|
|
476
437
|
}
|
|
477
438
|
/**
|
|
478
|
-
*
|
|
439
|
+
* helper function to assist in Lazy initializing an object
|
|
479
440
|
*/
|
|
480
441
|
class Lazy {
|
|
481
442
|
constructor(init) {
|
|
@@ -484,14 +445,12 @@
|
|
|
484
445
|
this.value = undefined;
|
|
485
446
|
}
|
|
486
447
|
// initializes the lazily initiated object
|
|
487
|
-
get() {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return this.value;
|
|
494
|
-
});
|
|
448
|
+
async get() {
|
|
449
|
+
if (!this.isInitialized) {
|
|
450
|
+
this.value = await this.init();
|
|
451
|
+
this.isInitialized = true;
|
|
452
|
+
}
|
|
453
|
+
return this.value;
|
|
495
454
|
}
|
|
496
455
|
}
|
|
497
456
|
/**
|
|
@@ -513,7 +472,7 @@
|
|
|
513
472
|
*
|
|
514
473
|
*/
|
|
515
474
|
/**
|
|
516
|
-
*
|
|
475
|
+
* The SaplingState class's main purpose is to provide a Merkle path for the forger and the transaction builder, so that it may verify that the Sapling transaction is valid
|
|
517
476
|
*
|
|
518
477
|
*/
|
|
519
478
|
class SaplingState {
|
|
@@ -522,25 +481,23 @@
|
|
|
522
481
|
this.uncommittedMerkleHash = '0100000000000000000000000000000000000000000000000000000000000000';
|
|
523
482
|
this.uncommittedMerkleHashes = new Lazy(() => this.createUncommittedMerkleHashes());
|
|
524
483
|
}
|
|
525
|
-
getStateTree(
|
|
526
|
-
|
|
527
|
-
if (this.stateTree !== undefined && this.stateTree.root === stateDiff.root) {
|
|
528
|
-
return this.stateTree;
|
|
529
|
-
}
|
|
530
|
-
const commitments = stateDiff.commitments_and_ciphertexts.map(([commitment, _]) => commitment);
|
|
531
|
-
let merkleTree;
|
|
532
|
-
if (constructTree) {
|
|
533
|
-
merkleTree = yield this.constructMerkleTree(commitments, 0);
|
|
534
|
-
yield this.validateMerkleTree(merkleTree, stateDiff.root);
|
|
535
|
-
}
|
|
536
|
-
this.stateTree = {
|
|
537
|
-
height: this.height,
|
|
538
|
-
size: commitments.length,
|
|
539
|
-
root: stateDiff.root,
|
|
540
|
-
tree: merkleTree,
|
|
541
|
-
};
|
|
484
|
+
async getStateTree(stateDiff, constructTree = true) {
|
|
485
|
+
if (this.stateTree !== undefined && this.stateTree.root === stateDiff.root) {
|
|
542
486
|
return this.stateTree;
|
|
543
|
-
}
|
|
487
|
+
}
|
|
488
|
+
const commitments = stateDiff.commitments_and_ciphertexts.map(([commitment, _]) => commitment);
|
|
489
|
+
let merkleTree;
|
|
490
|
+
if (constructTree) {
|
|
491
|
+
merkleTree = await this.constructMerkleTree(commitments, 0);
|
|
492
|
+
await this.validateMerkleTree(merkleTree, stateDiff.root);
|
|
493
|
+
}
|
|
494
|
+
this.stateTree = {
|
|
495
|
+
height: this.height,
|
|
496
|
+
size: commitments.length,
|
|
497
|
+
root: stateDiff.root,
|
|
498
|
+
tree: merkleTree,
|
|
499
|
+
};
|
|
500
|
+
return this.stateTree;
|
|
544
501
|
}
|
|
545
502
|
/**
|
|
546
503
|
*
|
|
@@ -548,17 +505,15 @@
|
|
|
548
505
|
* @param position position of the hash in the Merkle tree
|
|
549
506
|
* @returns a promise of a string that serves as the Merkle path that can be passed on to the Sapling forger or the transaction builder
|
|
550
507
|
*/
|
|
551
|
-
getWitness(stateTree, position) {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
return Buffer.concat([heightBuffer, witness, posBuffer]).toString('hex');
|
|
561
|
-
});
|
|
508
|
+
async getWitness(stateTree, position) {
|
|
509
|
+
const heightBuffer = utils.hex2Bytes(changeEndianness(utils.num2PaddedHex(stateTree.height)));
|
|
510
|
+
const posBuffer = utils.hex2Bytes(changeEndianness(utils.num2PaddedHex(position, 64)));
|
|
511
|
+
const neighbouringHashes = await this.getNeighbouringHashes([], stateTree.height, position, stateTree.tree);
|
|
512
|
+
const witness = neighbouringHashes
|
|
513
|
+
.map((hash) => Buffer.concat([utils.hex2Bytes(changeEndianness(utils.num2PaddedHex(hash.length))), hash]))
|
|
514
|
+
.reverse()
|
|
515
|
+
.reduce((acc, next) => Buffer.concat([acc, next]));
|
|
516
|
+
return Buffer.concat([heightBuffer, witness, posBuffer]).toString('hex');
|
|
562
517
|
}
|
|
563
518
|
/**
|
|
564
519
|
*
|
|
@@ -566,51 +521,45 @@
|
|
|
566
521
|
* @param height height of the desired Merkle tree
|
|
567
522
|
* @returns a promise of MerkleTree type object
|
|
568
523
|
*/
|
|
569
|
-
constructMerkleTree(leaves, height) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
else {
|
|
596
|
-
return Buffer.from(tree[0], 'hex');
|
|
597
|
-
}
|
|
598
|
-
});
|
|
524
|
+
async constructMerkleTree(leaves, height) {
|
|
525
|
+
if (height === this.height && leaves.length === 1) {
|
|
526
|
+
return leaves[0];
|
|
527
|
+
}
|
|
528
|
+
if (height === this.height || leaves.length > Math.pow(2, this.height - 1 - height)) {
|
|
529
|
+
throw new TreeConstructionFailure('Children length exceeds maximum number of nodes in a merkle tree');
|
|
530
|
+
}
|
|
531
|
+
const pairedLeaves = pairNodes(leaves);
|
|
532
|
+
const updatedLeaves = await Promise.all(pairedLeaves.map(async (chunk) => {
|
|
533
|
+
const left = await this.getMerkleHash(chunk[0], height);
|
|
534
|
+
const right = await this.getMerkleHash(chunk[1], height);
|
|
535
|
+
const parentHash = await sapling.merkleHash(height, left, right);
|
|
536
|
+
return [parentHash.toString('hex'), chunk[0], chunk[1]];
|
|
537
|
+
}));
|
|
538
|
+
return this.constructMerkleTree(updatedLeaves, height + 1);
|
|
539
|
+
}
|
|
540
|
+
async getMerkleHash(tree, height) {
|
|
541
|
+
if (tree === undefined) {
|
|
542
|
+
return (await this.uncommittedMerkleHashes.get())[height];
|
|
543
|
+
}
|
|
544
|
+
else if (typeof tree === 'string') {
|
|
545
|
+
return Buffer.from(tree, 'hex');
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
return Buffer.from(tree[0], 'hex');
|
|
549
|
+
}
|
|
599
550
|
}
|
|
600
551
|
/**
|
|
601
552
|
*
|
|
602
553
|
* @returns hashes of empty or null values to fill in the Merkle tree
|
|
603
554
|
*/
|
|
604
|
-
createUncommittedMerkleHashes() {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
return res;
|
|
613
|
-
});
|
|
555
|
+
async createUncommittedMerkleHashes() {
|
|
556
|
+
const res = new Array(this.height);
|
|
557
|
+
res[0] = Buffer.from(this.uncommittedMerkleHash, 'hex');
|
|
558
|
+
for (let i = 0; i < this.height; i++) {
|
|
559
|
+
const hash = res[i];
|
|
560
|
+
res[i + 1] = await sapling.merkleHash(i, hash, hash);
|
|
561
|
+
}
|
|
562
|
+
return res;
|
|
614
563
|
}
|
|
615
564
|
/**
|
|
616
565
|
*
|
|
@@ -618,13 +567,11 @@
|
|
|
618
567
|
* @param expectedRoot the expected merkle root to validate against
|
|
619
568
|
* @throws {@link InvalidMerkleTreeError}
|
|
620
569
|
*/
|
|
621
|
-
validateMerkleTree(tree, expectedRoot) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
});
|
|
570
|
+
async validateMerkleTree(tree, expectedRoot) {
|
|
571
|
+
const root = await this.getMerkleHash(tree, this.height - 1);
|
|
572
|
+
if (root.toString('hex') !== expectedRoot) {
|
|
573
|
+
throw new InvalidMerkleTreeError(root.toString('hex'));
|
|
574
|
+
}
|
|
628
575
|
}
|
|
629
576
|
/**
|
|
630
577
|
*
|
|
@@ -634,30 +581,28 @@
|
|
|
634
581
|
* @param tree the Merkle tree that we want to traverse
|
|
635
582
|
* @returns the accumulated Buffer array of neighbouring hashes
|
|
636
583
|
*/
|
|
637
|
-
getNeighbouringHashes(acc, height, position, tree) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
584
|
+
async getNeighbouringHashes(acc, height, position, tree) {
|
|
585
|
+
if (typeof tree === 'undefined') {
|
|
586
|
+
throw new Error();
|
|
587
|
+
}
|
|
588
|
+
else if (typeof tree === 'string') {
|
|
589
|
+
return acc;
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
let nextPos, nextTree, otherTree;
|
|
593
|
+
const fullTree = new BigNumber(2).pow(height - 1);
|
|
594
|
+
if (position.lt(fullTree)) {
|
|
595
|
+
nextPos = position;
|
|
596
|
+
nextTree = tree[1];
|
|
597
|
+
otherTree = tree[2];
|
|
644
598
|
}
|
|
645
599
|
else {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
nextPos = position;
|
|
650
|
-
nextTree = tree[1];
|
|
651
|
-
otherTree = tree[2];
|
|
652
|
-
}
|
|
653
|
-
else {
|
|
654
|
-
nextPos = position.minus(fullTree);
|
|
655
|
-
nextTree = tree[2];
|
|
656
|
-
otherTree = tree[1];
|
|
657
|
-
}
|
|
658
|
-
return this.getNeighbouringHashes([yield this.getMerkleHash(otherTree, height - 1), ...acc], height - 1, nextPos, nextTree);
|
|
600
|
+
nextPos = position.minus(fullTree);
|
|
601
|
+
nextTree = tree[2];
|
|
602
|
+
otherTree = tree[1];
|
|
659
603
|
}
|
|
660
|
-
|
|
604
|
+
return this.getNeighbouringHashes([await this.getMerkleHash(otherTree, height - 1), ...acc], height - 1, nextPos, nextTree);
|
|
605
|
+
}
|
|
661
606
|
}
|
|
662
607
|
}
|
|
663
608
|
|
|
@@ -665,67 +610,52 @@
|
|
|
665
610
|
const saplingOutputParams = require('../saplingOutputParams');
|
|
666
611
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
667
612
|
const saplingSpendParams = require('../saplingSpendParams');
|
|
613
|
+
let cachedParams;
|
|
668
614
|
class SaplingWrapper {
|
|
669
|
-
withProvingContext(action) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return sapling__namespace.withProvingContext(action);
|
|
673
|
-
});
|
|
615
|
+
async withProvingContext(action) {
|
|
616
|
+
await this.initSaplingParameters();
|
|
617
|
+
return sapling__namespace.withProvingContext(action);
|
|
674
618
|
}
|
|
675
619
|
getRandomBytes(length) {
|
|
676
620
|
return random.randomBytes(length);
|
|
677
621
|
}
|
|
678
|
-
randR() {
|
|
679
|
-
return
|
|
680
|
-
return sapling__namespace.randR();
|
|
681
|
-
});
|
|
622
|
+
async randR() {
|
|
623
|
+
return sapling__namespace.randR();
|
|
682
624
|
}
|
|
683
|
-
getOutgoingViewingKey(vk) {
|
|
684
|
-
return
|
|
685
|
-
return sapling__namespace.getOutgoingViewingKey(vk);
|
|
686
|
-
});
|
|
625
|
+
async getOutgoingViewingKey(vk) {
|
|
626
|
+
return sapling__namespace.getOutgoingViewingKey(vk);
|
|
687
627
|
}
|
|
688
|
-
preparePartialOutputDescription(parametersOutputProof) {
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
};
|
|
696
|
-
});
|
|
628
|
+
async preparePartialOutputDescription(parametersOutputProof) {
|
|
629
|
+
const partialOutputDesc = await sapling__namespace.preparePartialOutputDescription(parametersOutputProof.saplingContext, parametersOutputProof.address, parametersOutputProof.randomCommitmentTrapdoor, parametersOutputProof.ephemeralPrivateKey, parametersOutputProof.amount);
|
|
630
|
+
return {
|
|
631
|
+
commitmentValue: partialOutputDesc.cv,
|
|
632
|
+
commitment: partialOutputDesc.cm,
|
|
633
|
+
proof: partialOutputDesc.proof,
|
|
634
|
+
};
|
|
697
635
|
}
|
|
698
|
-
getDiversifiedFromRawPaymentAddress(decodedDestination) {
|
|
699
|
-
return
|
|
700
|
-
return sapling__namespace.getDiversifiedFromRawPaymentAddress(decodedDestination);
|
|
701
|
-
});
|
|
636
|
+
async getDiversifiedFromRawPaymentAddress(decodedDestination) {
|
|
637
|
+
return sapling__namespace.getDiversifiedFromRawPaymentAddress(decodedDestination);
|
|
702
638
|
}
|
|
703
|
-
deriveEphemeralPublicKey(diversifier, esk) {
|
|
704
|
-
return
|
|
705
|
-
return sapling__namespace.deriveEphemeralPublicKey(diversifier, esk);
|
|
706
|
-
});
|
|
639
|
+
async deriveEphemeralPublicKey(diversifier, esk) {
|
|
640
|
+
return sapling__namespace.deriveEphemeralPublicKey(diversifier, esk);
|
|
707
641
|
}
|
|
708
|
-
getPkdFromRawPaymentAddress(destination) {
|
|
709
|
-
return
|
|
710
|
-
return sapling__namespace.getPkdFromRawPaymentAddress(destination);
|
|
711
|
-
});
|
|
642
|
+
async getPkdFromRawPaymentAddress(destination) {
|
|
643
|
+
return sapling__namespace.getPkdFromRawPaymentAddress(destination);
|
|
712
644
|
}
|
|
713
|
-
keyAgreement(p, sk) {
|
|
714
|
-
return
|
|
715
|
-
return sapling__namespace.keyAgreement(p, sk);
|
|
716
|
-
});
|
|
645
|
+
async keyAgreement(p, sk) {
|
|
646
|
+
return sapling__namespace.keyAgreement(p, sk);
|
|
717
647
|
}
|
|
718
|
-
createBindingSignature(saplingContext, balance, transactionSigHash) {
|
|
719
|
-
return
|
|
720
|
-
return sapling__namespace.createBindingSignature(saplingContext, balance, transactionSigHash);
|
|
721
|
-
});
|
|
648
|
+
async createBindingSignature(saplingContext, balance, transactionSigHash) {
|
|
649
|
+
return sapling__namespace.createBindingSignature(saplingContext, balance, transactionSigHash);
|
|
722
650
|
}
|
|
723
|
-
initSaplingParameters() {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
651
|
+
async initSaplingParameters() {
|
|
652
|
+
if (!cachedParams) {
|
|
653
|
+
cachedParams = {
|
|
654
|
+
spend: Buffer.from(saplingSpendParams.saplingSpendParams, 'base64'),
|
|
655
|
+
output: Buffer.from(saplingOutputParams.saplingOutputParams, 'base64'),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
return sapling__namespace.initParameters(cachedParams.spend, cachedParams.output);
|
|
729
659
|
}
|
|
730
660
|
}
|
|
731
661
|
|
|
@@ -752,241 +682,228 @@
|
|
|
752
682
|
__classPrivateFieldSet(this, _SaplingTransactionBuilder_saplingWrapper, saplingWrapper, "f");
|
|
753
683
|
__classPrivateFieldSet(this, _SaplingTransactionBuilder_readProvider, readProvider, "f");
|
|
754
684
|
}
|
|
755
|
-
createShieldedTx(saplingTransactionParams, txTotalAmount, boundData) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
outputs.push(yield this.prepareSaplingOutputDescription({
|
|
767
|
-
saplingContext,
|
|
768
|
-
address,
|
|
769
|
-
amount: saplingTransactionParams[i].amount,
|
|
770
|
-
memo: saplingTransactionParams[i].memo,
|
|
771
|
-
randomCommitmentTrapdoor: rcm,
|
|
772
|
-
}));
|
|
773
|
-
}
|
|
774
|
-
const signature = yield this.createBindingSignature({
|
|
685
|
+
async createShieldedTx(saplingTransactionParams, txTotalAmount, boundData) {
|
|
686
|
+
const rcm = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").randR();
|
|
687
|
+
const balance = this.calculateTransactionBalance('0', txTotalAmount.toString());
|
|
688
|
+
const { signature, inputs, outputs } = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").withProvingContext(async (saplingContext) => {
|
|
689
|
+
const outputs = [];
|
|
690
|
+
const inputs = [];
|
|
691
|
+
for (const i in saplingTransactionParams) {
|
|
692
|
+
const [address] = utils.b58DecodeAndCheckPrefix(saplingTransactionParams[i].to, [
|
|
693
|
+
utils.PrefixV2.SaplingAddress,
|
|
694
|
+
]);
|
|
695
|
+
outputs.push(await this.prepareSaplingOutputDescription({
|
|
775
696
|
saplingContext,
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
697
|
+
address,
|
|
698
|
+
amount: saplingTransactionParams[i].amount,
|
|
699
|
+
memo: saplingTransactionParams[i].memo,
|
|
700
|
+
randomCommitmentTrapdoor: rcm,
|
|
701
|
+
}));
|
|
702
|
+
}
|
|
703
|
+
const signature = await this.createBindingSignature({
|
|
704
|
+
saplingContext,
|
|
784
705
|
inputs,
|
|
785
706
|
outputs,
|
|
786
|
-
signature,
|
|
787
707
|
balance,
|
|
788
|
-
|
|
708
|
+
boundData,
|
|
709
|
+
});
|
|
710
|
+
return { signature, inputs, outputs };
|
|
789
711
|
});
|
|
712
|
+
return {
|
|
713
|
+
inputs,
|
|
714
|
+
outputs,
|
|
715
|
+
signature,
|
|
716
|
+
balance,
|
|
717
|
+
};
|
|
790
718
|
}
|
|
791
|
-
createSaplingTx(saplingTransactionParams, txTotalAmount, boundData, chosenInputs) {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
outputs.push(yield this.prepareSaplingOutputDescription({
|
|
807
|
-
saplingContext,
|
|
808
|
-
address,
|
|
809
|
-
amount: saplingTransactionParams[i].amount,
|
|
810
|
-
memo: saplingTransactionParams[i].memo,
|
|
811
|
-
randomCommitmentTrapdoor,
|
|
812
|
-
outgoingViewingKey,
|
|
813
|
-
}));
|
|
814
|
-
}
|
|
815
|
-
if (chosenInputs.sumSelectedInputs.isGreaterThan(sumAmountOutput)) {
|
|
816
|
-
const payBackAddress = (yield saplingViewer.getAddress()).address;
|
|
817
|
-
const [address] = utils.b58DecodeAndCheckPrefix(payBackAddress, [utils.PrefixV2.SaplingAddress]);
|
|
818
|
-
const { payBackOutput, payBackAmount } = yield this.createPaybackOutput({
|
|
819
|
-
saplingContext,
|
|
820
|
-
address,
|
|
821
|
-
amount: txTotalAmount.toString(),
|
|
822
|
-
memo: DEFAULT_MEMO,
|
|
823
|
-
randomCommitmentTrapdoor: randomCommitmentTrapdoor,
|
|
824
|
-
outgoingViewingKey: outgoingViewingKey,
|
|
825
|
-
}, chosenInputs.sumSelectedInputs);
|
|
826
|
-
sumAmountOutput = sumAmountOutput.plus(new BigNumber(payBackAmount));
|
|
827
|
-
outputs.push(payBackOutput);
|
|
828
|
-
}
|
|
829
|
-
const balance = this.calculateTransactionBalance(chosenInputs.sumSelectedInputs.toString(), sumAmountOutput.toString());
|
|
830
|
-
const signature = yield this.createBindingSignature({
|
|
719
|
+
async createSaplingTx(saplingTransactionParams, txTotalAmount, boundData, chosenInputs) {
|
|
720
|
+
const randomCommitmentTrapdoor = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").randR();
|
|
721
|
+
const saplingViewer = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemorySpendingKey, "f").getSaplingViewingKeyProvider();
|
|
722
|
+
const outgoingViewingKey = await saplingViewer.getOutgoingViewingKey();
|
|
723
|
+
const { signature, balance, inputs, outputs } = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").withProvingContext(async (saplingContext) => {
|
|
724
|
+
const outputs = [];
|
|
725
|
+
const inputs = [];
|
|
726
|
+
inputs.push(...(await this.prepareSaplingSpendDescription(saplingContext, chosenInputs.inputsToSpend)));
|
|
727
|
+
let sumAmountOutput = new BigNumber(0);
|
|
728
|
+
for (const i in saplingTransactionParams) {
|
|
729
|
+
sumAmountOutput = sumAmountOutput.plus(new BigNumber(saplingTransactionParams[i].amount));
|
|
730
|
+
const [address] = utils.b58DecodeAndCheckPrefix(saplingTransactionParams[i].to, [
|
|
731
|
+
utils.PrefixV2.SaplingAddress,
|
|
732
|
+
]);
|
|
733
|
+
outputs.push(await this.prepareSaplingOutputDescription({
|
|
831
734
|
saplingContext,
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
|
|
735
|
+
address,
|
|
736
|
+
amount: saplingTransactionParams[i].amount,
|
|
737
|
+
memo: saplingTransactionParams[i].memo,
|
|
738
|
+
randomCommitmentTrapdoor,
|
|
739
|
+
outgoingViewingKey,
|
|
740
|
+
}));
|
|
741
|
+
}
|
|
742
|
+
if (chosenInputs.sumSelectedInputs.isGreaterThan(sumAmountOutput)) {
|
|
743
|
+
const payBackAddress = (await saplingViewer.getAddress()).address;
|
|
744
|
+
const [address] = utils.b58DecodeAndCheckPrefix(payBackAddress, [utils.PrefixV2.SaplingAddress]);
|
|
745
|
+
const { payBackOutput, payBackAmount } = await this.createPaybackOutput({
|
|
746
|
+
saplingContext,
|
|
747
|
+
address,
|
|
748
|
+
amount: txTotalAmount.toString(),
|
|
749
|
+
memo: DEFAULT_MEMO,
|
|
750
|
+
randomCommitmentTrapdoor: randomCommitmentTrapdoor,
|
|
751
|
+
outgoingViewingKey: outgoingViewingKey,
|
|
752
|
+
}, chosenInputs.sumSelectedInputs);
|
|
753
|
+
sumAmountOutput = sumAmountOutput.plus(new BigNumber(payBackAmount));
|
|
754
|
+
outputs.push(payBackOutput);
|
|
755
|
+
}
|
|
756
|
+
const balance = this.calculateTransactionBalance(chosenInputs.sumSelectedInputs.toString(), sumAmountOutput.toString());
|
|
757
|
+
const signature = await this.createBindingSignature({
|
|
758
|
+
saplingContext,
|
|
840
759
|
inputs,
|
|
841
760
|
outputs,
|
|
842
|
-
signature,
|
|
843
761
|
balance,
|
|
844
|
-
|
|
762
|
+
boundData,
|
|
763
|
+
});
|
|
764
|
+
return { signature, balance, inputs, outputs };
|
|
845
765
|
});
|
|
766
|
+
return {
|
|
767
|
+
inputs,
|
|
768
|
+
outputs,
|
|
769
|
+
signature,
|
|
770
|
+
balance,
|
|
771
|
+
};
|
|
846
772
|
}
|
|
847
773
|
// sum of values of inputs minus sums of values of output equals balance
|
|
848
774
|
calculateTransactionBalance(inputTotal, outputTotal) {
|
|
849
775
|
return new BigNumber(inputTotal).minus(new BigNumber(outputTotal));
|
|
850
776
|
}
|
|
851
|
-
prepareSaplingOutputDescription(parametersOutputDescription) {
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
commitmentValue,
|
|
866
|
-
commitment,
|
|
867
|
-
ephemeralPublicKey,
|
|
868
|
-
parametersOutputDescription.outgoingViewingKey,
|
|
869
|
-
]), Buffer.from(OCK_KEY), 32)
|
|
870
|
-
: __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(32);
|
|
871
|
-
const ciphertext = yield this.encryptCiphertext({
|
|
872
|
-
address: parametersOutputDescription.address,
|
|
873
|
-
ephemeralPrivateKey,
|
|
874
|
-
diversifier,
|
|
875
|
-
outgoingCipherKey,
|
|
876
|
-
amount: parametersOutputDescription.amount,
|
|
877
|
-
randomCommitmentTrapdoor: parametersOutputDescription.randomCommitmentTrapdoor,
|
|
878
|
-
memo: parametersOutputDescription.memo,
|
|
879
|
-
});
|
|
880
|
-
return {
|
|
777
|
+
async prepareSaplingOutputDescription(parametersOutputDescription) {
|
|
778
|
+
const ephemeralPrivateKey = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").randR();
|
|
779
|
+
const { commitmentValue, commitment, proof } = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").preparePartialOutputDescription({
|
|
780
|
+
saplingContext: parametersOutputDescription.saplingContext,
|
|
781
|
+
address: parametersOutputDescription.address,
|
|
782
|
+
randomCommitmentTrapdoor: parametersOutputDescription.randomCommitmentTrapdoor,
|
|
783
|
+
ephemeralPrivateKey,
|
|
784
|
+
amount: parametersOutputDescription.amount,
|
|
785
|
+
});
|
|
786
|
+
const diversifier = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getDiversifiedFromRawPaymentAddress(parametersOutputDescription.address);
|
|
787
|
+
const ephemeralPublicKey = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").deriveEphemeralPublicKey(diversifier, ephemeralPrivateKey);
|
|
788
|
+
const outgoingCipherKey = parametersOutputDescription.outgoingViewingKey
|
|
789
|
+
? blake.blake2b(Buffer.concat([
|
|
790
|
+
commitmentValue,
|
|
881
791
|
commitment,
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
792
|
+
ephemeralPublicKey,
|
|
793
|
+
parametersOutputDescription.outgoingViewingKey,
|
|
794
|
+
]), Buffer.from(OCK_KEY), 32)
|
|
795
|
+
: __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(32);
|
|
796
|
+
const ciphertext = await this.encryptCiphertext({
|
|
797
|
+
address: parametersOutputDescription.address,
|
|
798
|
+
ephemeralPrivateKey,
|
|
799
|
+
diversifier,
|
|
800
|
+
outgoingCipherKey,
|
|
801
|
+
amount: parametersOutputDescription.amount,
|
|
802
|
+
randomCommitmentTrapdoor: parametersOutputDescription.randomCommitmentTrapdoor,
|
|
803
|
+
memo: parametersOutputDescription.memo,
|
|
886
804
|
});
|
|
805
|
+
return {
|
|
806
|
+
commitment,
|
|
807
|
+
proof,
|
|
808
|
+
ciphertext: {
|
|
809
|
+
...ciphertext,
|
|
810
|
+
commitmentValue,
|
|
811
|
+
ephemeralPublicKey,
|
|
812
|
+
},
|
|
813
|
+
};
|
|
887
814
|
}
|
|
888
|
-
prepareSaplingSpendDescription(saplingContext, inputsToSpend) {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
randomCommitmentTrapdoor: inputsToSpend[i].randomCommitmentTrapdoor,
|
|
917
|
-
publicKeyReRandomization,
|
|
918
|
-
amount,
|
|
919
|
-
root: stateDiff.root,
|
|
920
|
-
witness,
|
|
921
|
-
});
|
|
922
|
-
const unsignedSpendDescriptionBytes = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeUnsignedTxInput(unsignedSpendDescription);
|
|
923
|
-
const hash = blake.blake2b(unsignedSpendDescriptionBytes, yield this.getAntiReplay(), 32);
|
|
924
|
-
const spendDescription = yield __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemorySpendingKey, "f").signSpendDescription({
|
|
815
|
+
async prepareSaplingSpendDescription(saplingContext, inputsToSpend) {
|
|
816
|
+
const publicKeyReRandomization = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").randR();
|
|
817
|
+
let stateDiff;
|
|
818
|
+
if (__classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingId, "f")) {
|
|
819
|
+
stateDiff = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_readProvider, "f").getSaplingDiffById({ id: __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingId, "f") }, 'head');
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
stateDiff = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_readProvider, "f").getSaplingDiffByContract(__classPrivateFieldGet(this, _SaplingTransactionBuilder_contractAddress, "f"), 'head');
|
|
823
|
+
}
|
|
824
|
+
const stateTree = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingState, "f").getStateTree(stateDiff, true);
|
|
825
|
+
const saplingSpendDescriptions = [];
|
|
826
|
+
for (let i = 0; i < inputsToSpend.length; i++) {
|
|
827
|
+
const amount = convertValueToBigNumber(inputsToSpend[i].value).toString();
|
|
828
|
+
const witness = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingState, "f").getWitness(stateTree, new BigNumber(inputsToSpend[i].position));
|
|
829
|
+
const unsignedSpendDescription = __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemoryProvingKey, "f")
|
|
830
|
+
? await __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemoryProvingKey, "f").prepareSpendDescription({
|
|
831
|
+
saplingContext,
|
|
832
|
+
address: inputsToSpend[i].paymentAddress,
|
|
833
|
+
randomCommitmentTrapdoor: inputsToSpend[i].randomCommitmentTrapdoor,
|
|
834
|
+
publicKeyReRandomization,
|
|
835
|
+
amount,
|
|
836
|
+
root: stateDiff.root,
|
|
837
|
+
witness,
|
|
838
|
+
})
|
|
839
|
+
: await __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemorySpendingKey, "f").prepareSpendDescription({
|
|
840
|
+
saplingContext,
|
|
841
|
+
address: inputsToSpend[i].paymentAddress,
|
|
842
|
+
randomCommitmentTrapdoor: inputsToSpend[i].randomCommitmentTrapdoor,
|
|
925
843
|
publicKeyReRandomization,
|
|
926
|
-
|
|
927
|
-
|
|
844
|
+
amount,
|
|
845
|
+
root: stateDiff.root,
|
|
846
|
+
witness,
|
|
928
847
|
});
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
encryptCiphertext(parametersCiphertext) {
|
|
938
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
939
|
-
const recipientDiversifiedTransmissionKey = yield __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getPkdFromRawPaymentAddress(parametersCiphertext.address);
|
|
940
|
-
const keyAgreement = yield __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").keyAgreement(recipientDiversifiedTransmissionKey, parametersCiphertext.ephemeralPrivateKey);
|
|
941
|
-
const keyAgreementHash = blake.blake2b(keyAgreement, Buffer.from(KDF_KEY), 32);
|
|
942
|
-
const nonceEnc = Buffer.from(__classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(24));
|
|
943
|
-
const transactionPlaintext = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeTransactionPlaintext({
|
|
944
|
-
diversifier: parametersCiphertext.diversifier,
|
|
945
|
-
amount: parametersCiphertext.amount,
|
|
946
|
-
randomCommitmentTrapdoor: parametersCiphertext.randomCommitmentTrapdoor,
|
|
947
|
-
memoSize: __classPrivateFieldGet(this, _SaplingTransactionBuilder_memoSize, "f") * 2,
|
|
948
|
-
memo: parametersCiphertext.memo,
|
|
949
|
-
});
|
|
950
|
-
const nonceOut = Buffer.from(__classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(24));
|
|
951
|
-
const payloadEnc = Buffer.from(nacl.secretBox(keyAgreementHash, nonceEnc, transactionPlaintext));
|
|
952
|
-
const payloadOut = Buffer.from(nacl.secretBox(parametersCiphertext.outgoingCipherKey, nonceOut, Buffer.concat([
|
|
953
|
-
recipientDiversifiedTransmissionKey,
|
|
954
|
-
parametersCiphertext.ephemeralPrivateKey,
|
|
955
|
-
])));
|
|
956
|
-
return { payloadEnc, nonceEnc, payloadOut, nonceOut };
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
createPaybackOutput(params, sumSelectedInputs) {
|
|
960
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
961
|
-
const payBackAmount = sumSelectedInputs.minus(params.amount).toString();
|
|
962
|
-
const payBackOutput = yield this.prepareSaplingOutputDescription({
|
|
963
|
-
saplingContext: params.saplingContext,
|
|
964
|
-
address: params.address,
|
|
965
|
-
amount: payBackAmount,
|
|
966
|
-
memo: params.memo,
|
|
967
|
-
randomCommitmentTrapdoor: params.randomCommitmentTrapdoor,
|
|
968
|
-
outgoingViewingKey: params.outgoingViewingKey,
|
|
848
|
+
const unsignedSpendDescriptionBytes = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeUnsignedTxInput(unsignedSpendDescription);
|
|
849
|
+
const hash = blake.blake2b(unsignedSpendDescriptionBytes, await this.getAntiReplay(), 32);
|
|
850
|
+
const spendDescription = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_inMemorySpendingKey, "f").signSpendDescription({
|
|
851
|
+
publicKeyReRandomization,
|
|
852
|
+
unsignedSpendDescription,
|
|
853
|
+
hash,
|
|
969
854
|
});
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
}
|
|
973
|
-
createBindingSignature(parametersBindingSig) {
|
|
974
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
975
|
-
const outputs = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeOutputDescriptions(parametersBindingSig.outputs);
|
|
976
|
-
const inputs = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeSpendDescriptions(parametersBindingSig.inputs);
|
|
977
|
-
const transactionSigHash = blake.blake2b(Buffer.concat([inputs, outputs, parametersBindingSig.boundData]), yield this.getAntiReplay(), 32);
|
|
978
|
-
return __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").createBindingSignature(parametersBindingSig.saplingContext, parametersBindingSig.balance.toFixed(), transactionSigHash);
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
getAntiReplay() {
|
|
982
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
983
|
-
let chainId = __classPrivateFieldGet(this, _SaplingTransactionBuilder_chainId, "f");
|
|
984
|
-
if (!chainId) {
|
|
985
|
-
chainId = yield __classPrivateFieldGet(this, _SaplingTransactionBuilder_readProvider, "f").getChainId();
|
|
986
|
-
__classPrivateFieldSet(this, _SaplingTransactionBuilder_chainId, chainId, "f");
|
|
855
|
+
if (spendDescription.signature === undefined) {
|
|
856
|
+
throw new Error('Spend signing failed');
|
|
987
857
|
}
|
|
988
|
-
|
|
858
|
+
saplingSpendDescriptions.push(spendDescription);
|
|
859
|
+
}
|
|
860
|
+
return saplingSpendDescriptions;
|
|
861
|
+
}
|
|
862
|
+
async encryptCiphertext(parametersCiphertext) {
|
|
863
|
+
const recipientDiversifiedTransmissionKey = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getPkdFromRawPaymentAddress(parametersCiphertext.address);
|
|
864
|
+
const keyAgreement = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").keyAgreement(recipientDiversifiedTransmissionKey, parametersCiphertext.ephemeralPrivateKey);
|
|
865
|
+
const keyAgreementHash = blake.blake2b(keyAgreement, Buffer.from(KDF_KEY), 32);
|
|
866
|
+
const nonceEnc = Buffer.from(__classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(24));
|
|
867
|
+
const transactionPlaintext = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeTransactionPlaintext({
|
|
868
|
+
diversifier: parametersCiphertext.diversifier,
|
|
869
|
+
amount: parametersCiphertext.amount,
|
|
870
|
+
randomCommitmentTrapdoor: parametersCiphertext.randomCommitmentTrapdoor,
|
|
871
|
+
memoSize: __classPrivateFieldGet(this, _SaplingTransactionBuilder_memoSize, "f") * 2,
|
|
872
|
+
memo: parametersCiphertext.memo,
|
|
989
873
|
});
|
|
874
|
+
const nonceOut = Buffer.from(__classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").getRandomBytes(24));
|
|
875
|
+
const payloadEnc = Buffer.from(nacl.secretBox(keyAgreementHash, nonceEnc, transactionPlaintext));
|
|
876
|
+
const payloadOut = Buffer.from(nacl.secretBox(parametersCiphertext.outgoingCipherKey, nonceOut, Buffer.concat([
|
|
877
|
+
recipientDiversifiedTransmissionKey,
|
|
878
|
+
parametersCiphertext.ephemeralPrivateKey,
|
|
879
|
+
])));
|
|
880
|
+
return { payloadEnc, nonceEnc, payloadOut, nonceOut };
|
|
881
|
+
}
|
|
882
|
+
async createPaybackOutput(params, sumSelectedInputs) {
|
|
883
|
+
const payBackAmount = sumSelectedInputs.minus(params.amount).toString();
|
|
884
|
+
const payBackOutput = await this.prepareSaplingOutputDescription({
|
|
885
|
+
saplingContext: params.saplingContext,
|
|
886
|
+
address: params.address,
|
|
887
|
+
amount: payBackAmount,
|
|
888
|
+
memo: params.memo,
|
|
889
|
+
randomCommitmentTrapdoor: params.randomCommitmentTrapdoor,
|
|
890
|
+
outgoingViewingKey: params.outgoingViewingKey,
|
|
891
|
+
});
|
|
892
|
+
return { payBackOutput, payBackAmount };
|
|
893
|
+
}
|
|
894
|
+
async createBindingSignature(parametersBindingSig) {
|
|
895
|
+
const outputs = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeOutputDescriptions(parametersBindingSig.outputs);
|
|
896
|
+
const inputs = __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingForger, "f").forgeSpendDescriptions(parametersBindingSig.inputs);
|
|
897
|
+
const transactionSigHash = blake.blake2b(Buffer.concat([inputs, outputs, parametersBindingSig.boundData]), await this.getAntiReplay(), 32);
|
|
898
|
+
return __classPrivateFieldGet(this, _SaplingTransactionBuilder_saplingWrapper, "f").createBindingSignature(parametersBindingSig.saplingContext, parametersBindingSig.balance.toFixed(), transactionSigHash);
|
|
899
|
+
}
|
|
900
|
+
async getAntiReplay() {
|
|
901
|
+
let chainId = __classPrivateFieldGet(this, _SaplingTransactionBuilder_chainId, "f");
|
|
902
|
+
if (!chainId) {
|
|
903
|
+
chainId = await __classPrivateFieldGet(this, _SaplingTransactionBuilder_readProvider, "f").getChainId();
|
|
904
|
+
__classPrivateFieldSet(this, _SaplingTransactionBuilder_chainId, chainId, "f");
|
|
905
|
+
}
|
|
906
|
+
return Buffer.from(`${__classPrivateFieldGet(this, _SaplingTransactionBuilder_contractAddress, "f")}${chainId}`);
|
|
990
907
|
}
|
|
991
908
|
}
|
|
992
909
|
_SaplingTransactionBuilder_inMemorySpendingKey = new WeakMap(), _SaplingTransactionBuilder_inMemoryProvingKey = new WeakMap(), _SaplingTransactionBuilder_saplingForger = new WeakMap(), _SaplingTransactionBuilder_contractAddress = new WeakMap(), _SaplingTransactionBuilder_saplingId = new WeakMap(), _SaplingTransactionBuilder_memoSize = new WeakMap(), _SaplingTransactionBuilder_readProvider = new WeakMap(), _SaplingTransactionBuilder_saplingWrapper = new WeakMap(), _SaplingTransactionBuilder_chainId = new WeakMap(), _SaplingTransactionBuilder_saplingState = new WeakMap();
|
|
@@ -1001,7 +918,7 @@
|
|
|
1001
918
|
}
|
|
1002
919
|
catch (err) {
|
|
1003
920
|
if (err instanceof core.ParameterValidationError) {
|
|
1004
|
-
throw new InvalidSpendingKey(
|
|
921
|
+
throw new InvalidSpendingKey('invalid spending key');
|
|
1005
922
|
}
|
|
1006
923
|
else {
|
|
1007
924
|
throw err;
|
|
@@ -1010,7 +927,7 @@
|
|
|
1010
927
|
})();
|
|
1011
928
|
if (pre === utils.PrefixV2.EncryptedSaplingSpendingKey) {
|
|
1012
929
|
if (!password) {
|
|
1013
|
-
throw new InvalidSpendingKey(
|
|
930
|
+
throw new InvalidSpendingKey('no password provided to decrypt');
|
|
1014
931
|
}
|
|
1015
932
|
const salt = toBuffer(keyArr.slice(0, 8));
|
|
1016
933
|
const encryptedSk = toBuffer(keyArr.slice(8));
|
|
@@ -1020,7 +937,7 @@
|
|
|
1020
937
|
const decrypted = nacl.openSecretBox(new Uint8Array(encryptionKey), new Uint8Array(24), // zero nonce - uniqueness provided by per-encryption derived key
|
|
1021
938
|
new Uint8Array(encryptedSk));
|
|
1022
939
|
if (!decrypted) {
|
|
1023
|
-
throw new InvalidSpendingKey(
|
|
940
|
+
throw new InvalidSpendingKey('incorrect password or unable to decrypt');
|
|
1024
941
|
}
|
|
1025
942
|
return toBuffer(decrypted);
|
|
1026
943
|
}
|
|
@@ -1031,7 +948,7 @@
|
|
|
1031
948
|
|
|
1032
949
|
var _InMemoryViewingKey_fullViewingKey;
|
|
1033
950
|
/**
|
|
1034
|
-
*
|
|
951
|
+
* Holds the viewing key
|
|
1035
952
|
*/
|
|
1036
953
|
class InMemoryViewingKey {
|
|
1037
954
|
constructor(fullViewingKey) {
|
|
@@ -1039,7 +956,7 @@
|
|
|
1039
956
|
__classPrivateFieldSet(this, _InMemoryViewingKey_fullViewingKey, Buffer.from(fullViewingKey, 'hex'), "f");
|
|
1040
957
|
}
|
|
1041
958
|
/**
|
|
1042
|
-
*
|
|
959
|
+
* Allows to instantiate the InMemoryViewingKey from an encrypted/unencrypted spending key
|
|
1043
960
|
*
|
|
1044
961
|
* @param spendingKey Base58Check-encoded spending key
|
|
1045
962
|
* @param password Optional password to decrypt the spending key
|
|
@@ -1049,15 +966,13 @@
|
|
|
1049
966
|
* ```
|
|
1050
967
|
*
|
|
1051
968
|
*/
|
|
1052
|
-
static fromSpendingKey(spendingKey, password) {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
return new InMemoryViewingKey(viewingKey.toString('hex'));
|
|
1057
|
-
});
|
|
969
|
+
static async fromSpendingKey(spendingKey, password) {
|
|
970
|
+
const spendingKeyBuf = decryptKey(spendingKey, password);
|
|
971
|
+
const viewingKey = await sapling__namespace.getExtendedFullViewingKeyFromSpendingKey(spendingKeyBuf);
|
|
972
|
+
return new InMemoryViewingKey(viewingKey.toString('hex'));
|
|
1058
973
|
}
|
|
1059
974
|
/**
|
|
1060
|
-
*
|
|
975
|
+
* Retrieve the full viewing key
|
|
1061
976
|
* @returns Buffer representing the full viewing key
|
|
1062
977
|
*
|
|
1063
978
|
*/
|
|
@@ -1065,46 +980,40 @@
|
|
|
1065
980
|
return __classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f");
|
|
1066
981
|
}
|
|
1067
982
|
/**
|
|
1068
|
-
*
|
|
983
|
+
* Retrieve the outgoing viewing key
|
|
1069
984
|
* @returns Buffer representing the outgoing viewing key
|
|
1070
985
|
*
|
|
1071
986
|
*/
|
|
1072
|
-
getOutgoingViewingKey() {
|
|
1073
|
-
return
|
|
1074
|
-
return sapling__namespace.getOutgoingViewingKey(__classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f"));
|
|
1075
|
-
});
|
|
987
|
+
async getOutgoingViewingKey() {
|
|
988
|
+
return sapling__namespace.getOutgoingViewingKey(__classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f"));
|
|
1076
989
|
}
|
|
1077
990
|
/**
|
|
1078
|
-
*
|
|
991
|
+
* Retrieve the incoming viewing key
|
|
1079
992
|
* @returns Buffer representing the incoming viewing key
|
|
1080
993
|
*
|
|
1081
994
|
*/
|
|
1082
|
-
getIncomingViewingKey() {
|
|
1083
|
-
return
|
|
1084
|
-
return sapling__namespace.getIncomingViewingKey(__classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f"));
|
|
1085
|
-
});
|
|
995
|
+
async getIncomingViewingKey() {
|
|
996
|
+
return sapling__namespace.getIncomingViewingKey(__classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f"));
|
|
1086
997
|
}
|
|
1087
998
|
/**
|
|
1088
|
-
*
|
|
999
|
+
* Retrieve a payment address
|
|
1089
1000
|
* @param addressIndex used to determine which diversifier should be used to derive the address, default is 0
|
|
1090
1001
|
* @returns Base58Check-encoded address and its index
|
|
1091
1002
|
*
|
|
1092
1003
|
*/
|
|
1093
|
-
getAddress(addressIndex) {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
};
|
|
1100
|
-
});
|
|
1004
|
+
async getAddress(addressIndex) {
|
|
1005
|
+
const { index, raw } = await sapling__namespace.getPaymentAddressFromViewingKey(__classPrivateFieldGet(this, _InMemoryViewingKey_fullViewingKey, "f"), addressIndex);
|
|
1006
|
+
return {
|
|
1007
|
+
address: utils.b58Encode(raw, utils.PrefixV2.SaplingAddress),
|
|
1008
|
+
addressIndex: index.readInt32LE(),
|
|
1009
|
+
};
|
|
1101
1010
|
}
|
|
1102
1011
|
}
|
|
1103
1012
|
_InMemoryViewingKey_fullViewingKey = new WeakMap();
|
|
1104
1013
|
|
|
1105
1014
|
var _InMemorySpendingKey_spendingKeyBuf, _InMemorySpendingKey_saplingViewingKey;
|
|
1106
1015
|
/**
|
|
1107
|
-
*
|
|
1016
|
+
* holds the spending key, create proof and signature for spend descriptions
|
|
1108
1017
|
* can instantiate from mnemonic word list or decrypt a encrypted spending key
|
|
1109
1018
|
* with access to instantiate a InMemoryViewingKey
|
|
1110
1019
|
*/
|
|
@@ -1125,35 +1034,31 @@
|
|
|
1125
1034
|
* @param derivationPath tezos current standard 'm/'
|
|
1126
1035
|
* @returns InMemorySpendingKey class instantiated
|
|
1127
1036
|
*/
|
|
1128
|
-
static fromMnemonic(
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
return new InMemorySpendingKey(spendingKey);
|
|
1139
|
-
});
|
|
1037
|
+
static async fromMnemonic(mnemonic, derivationPath = 'm/') {
|
|
1038
|
+
// no password passed here. password provided only changes from sask -> MMXj
|
|
1039
|
+
const fullSeed = await bip39__namespace.mnemonicToSeed(mnemonic);
|
|
1040
|
+
const first32 = fullSeed.slice(0, 32);
|
|
1041
|
+
const second32 = fullSeed.slice(32);
|
|
1042
|
+
// reduce seed bytes must be 32 bytes reflecting both halves
|
|
1043
|
+
const seed = Buffer.from(first32.map((byte, index) => byte ^ second32[index]));
|
|
1044
|
+
const spendingKeyArr = new Uint8Array(await sapling__namespace.getExtendedSpendingKey(seed, derivationPath));
|
|
1045
|
+
const spendingKey = utils.b58Encode(spendingKeyArr, utils.PrefixV2.SaplingSpendingKey);
|
|
1046
|
+
return new InMemorySpendingKey(spendingKey);
|
|
1140
1047
|
}
|
|
1141
1048
|
/**
|
|
1142
1049
|
*
|
|
1143
1050
|
* @returns InMemoryViewingKey instantiated class
|
|
1144
1051
|
*/
|
|
1145
|
-
getSaplingViewingKeyProvider() {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
return __classPrivateFieldGet(this, _InMemorySpendingKey_saplingViewingKey, "f");
|
|
1153
|
-
});
|
|
1052
|
+
async getSaplingViewingKeyProvider() {
|
|
1053
|
+
let viewingKey;
|
|
1054
|
+
if (!__classPrivateFieldGet(this, _InMemorySpendingKey_saplingViewingKey, "f")) {
|
|
1055
|
+
viewingKey = await sapling__namespace.getExtendedFullViewingKeyFromSpendingKey(__classPrivateFieldGet(this, _InMemorySpendingKey_spendingKeyBuf, "f"));
|
|
1056
|
+
__classPrivateFieldSet(this, _InMemorySpendingKey_saplingViewingKey, new InMemoryViewingKey(viewingKey.toString('hex')), "f");
|
|
1057
|
+
}
|
|
1058
|
+
return __classPrivateFieldGet(this, _InMemorySpendingKey_saplingViewingKey, "f");
|
|
1154
1059
|
}
|
|
1155
1060
|
/**
|
|
1156
|
-
*
|
|
1061
|
+
* Prepare an unsigned sapling spend description using the spending key
|
|
1157
1062
|
* @param parametersSpendProof.saplingContext The sapling proving context
|
|
1158
1063
|
* @param parametersSpendProof.address The address of the input
|
|
1159
1064
|
* @param parametersSpendProof.randomCommitmentTrapdoor The randomness of the commitment
|
|
@@ -1161,61 +1066,54 @@
|
|
|
1161
1066
|
* @param parametersSpendProof.amount The value of the input
|
|
1162
1067
|
* @param parametersSpendProof.root The root of the merkle tree
|
|
1163
1068
|
* @param parametersSpendProof.witness The path of the commitment in the tree
|
|
1164
|
-
* @param derivationPath tezos current standard 'm/'
|
|
1165
1069
|
* @returns The unsigned spend description
|
|
1166
1070
|
*/
|
|
1167
|
-
prepareSpendDescription(parametersSpendProof) {
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
};
|
|
1177
|
-
});
|
|
1071
|
+
async prepareSpendDescription(parametersSpendProof) {
|
|
1072
|
+
const spendDescription = await sapling__namespace.prepareSpendDescriptionWithSpendingKey(parametersSpendProof.saplingContext, __classPrivateFieldGet(this, _InMemorySpendingKey_spendingKeyBuf, "f"), parametersSpendProof.address, parametersSpendProof.randomCommitmentTrapdoor, parametersSpendProof.publicKeyReRandomization, parametersSpendProof.amount, parametersSpendProof.root, parametersSpendProof.witness);
|
|
1073
|
+
return {
|
|
1074
|
+
commitmentValue: spendDescription.cv,
|
|
1075
|
+
nullifier: spendDescription.nf,
|
|
1076
|
+
publicKeyReRandomization: spendDescription.rk,
|
|
1077
|
+
rtAnchor: spendDescription.rt,
|
|
1078
|
+
proof: spendDescription.proof,
|
|
1079
|
+
};
|
|
1178
1080
|
}
|
|
1179
1081
|
/**
|
|
1180
|
-
*
|
|
1082
|
+
* Sign a sapling spend description
|
|
1181
1083
|
* @param parametersSpendSig.publicKeyReRandomization The re-randomization of the public key
|
|
1182
1084
|
* @param parametersSpendSig.unsignedSpendDescription The unsigned Spend description
|
|
1183
1085
|
* @param parametersSpendSig.hash The data to be signed
|
|
1184
1086
|
* @returns The signed spend description
|
|
1185
1087
|
*/
|
|
1186
|
-
signSpendDescription(parametersSpendSig) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
};
|
|
1202
|
-
});
|
|
1088
|
+
async signSpendDescription(parametersSpendSig) {
|
|
1089
|
+
const signedSpendDescription = await sapling__namespace.signSpendDescription({
|
|
1090
|
+
cv: parametersSpendSig.unsignedSpendDescription.commitmentValue,
|
|
1091
|
+
rt: parametersSpendSig.unsignedSpendDescription.rtAnchor,
|
|
1092
|
+
nf: parametersSpendSig.unsignedSpendDescription.nullifier,
|
|
1093
|
+
rk: parametersSpendSig.unsignedSpendDescription.publicKeyReRandomization,
|
|
1094
|
+
proof: parametersSpendSig.unsignedSpendDescription.proof,
|
|
1095
|
+
}, __classPrivateFieldGet(this, _InMemorySpendingKey_spendingKeyBuf, "f"), parametersSpendSig.publicKeyReRandomization, parametersSpendSig.hash);
|
|
1096
|
+
return {
|
|
1097
|
+
commitmentValue: signedSpendDescription.cv,
|
|
1098
|
+
nullifier: signedSpendDescription.nf,
|
|
1099
|
+
publicKeyReRandomization: signedSpendDescription.rk,
|
|
1100
|
+
proof: signedSpendDescription.proof,
|
|
1101
|
+
signature: signedSpendDescription.spendAuthSig,
|
|
1102
|
+
};
|
|
1203
1103
|
}
|
|
1204
1104
|
/**
|
|
1205
|
-
*
|
|
1105
|
+
* Return a proof authorizing key from the configured spending key
|
|
1206
1106
|
*/
|
|
1207
|
-
getProvingKey() {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
return provingKey.toString('hex');
|
|
1211
|
-
});
|
|
1107
|
+
async getProvingKey() {
|
|
1108
|
+
const provingKey = await sapling__namespace.getProofAuthorizingKey(__classPrivateFieldGet(this, _InMemorySpendingKey_spendingKeyBuf, "f"));
|
|
1109
|
+
return provingKey.toString('hex');
|
|
1212
1110
|
}
|
|
1213
1111
|
}
|
|
1214
1112
|
_InMemorySpendingKey_spendingKeyBuf = new WeakMap(), _InMemorySpendingKey_saplingViewingKey = new WeakMap();
|
|
1215
1113
|
|
|
1216
1114
|
var _InMemoryProvingKey_provingKey;
|
|
1217
1115
|
/**
|
|
1218
|
-
*
|
|
1116
|
+
* holds the proving key, create proof for spend descriptions
|
|
1219
1117
|
* The class can be instantiated from a proving key or a spending key
|
|
1220
1118
|
*/
|
|
1221
1119
|
class InMemoryProvingKey {
|
|
@@ -1224,7 +1122,7 @@
|
|
|
1224
1122
|
__classPrivateFieldSet(this, _InMemoryProvingKey_provingKey, Buffer.from(provingKey, 'hex'), "f");
|
|
1225
1123
|
}
|
|
1226
1124
|
/**
|
|
1227
|
-
*
|
|
1125
|
+
* Allows to instantiate the InMemoryProvingKey from an encrypted/unencrypted spending key
|
|
1228
1126
|
*
|
|
1229
1127
|
* @param spendingKey Base58Check-encoded spending key
|
|
1230
1128
|
* @param password Optional password to decrypt the spending key
|
|
@@ -1234,15 +1132,13 @@
|
|
|
1234
1132
|
* ```
|
|
1235
1133
|
*
|
|
1236
1134
|
*/
|
|
1237
|
-
static fromSpendingKey(spendingKey, password) {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
return new InMemoryProvingKey(provingKey.toString('hex'));
|
|
1242
|
-
});
|
|
1135
|
+
static async fromSpendingKey(spendingKey, password) {
|
|
1136
|
+
const decodedSpendingKey = decryptKey(spendingKey, password);
|
|
1137
|
+
const provingKey = await sapling__namespace.getProofAuthorizingKey(decodedSpendingKey);
|
|
1138
|
+
return new InMemoryProvingKey(provingKey.toString('hex'));
|
|
1243
1139
|
}
|
|
1244
1140
|
/**
|
|
1245
|
-
*
|
|
1141
|
+
* Prepare an unsigned sapling spend description using the proving key
|
|
1246
1142
|
*
|
|
1247
1143
|
* @param parametersSpendProof.saplingContext The sapling proving context
|
|
1248
1144
|
* @param parametersSpendProof.address The address of the input
|
|
@@ -1251,20 +1147,17 @@
|
|
|
1251
1147
|
* @param parametersSpendProof.amount The value of the input
|
|
1252
1148
|
* @param parametersSpendProof.root The root of the merkle tree
|
|
1253
1149
|
* @param parametersSpendProof.witness The path of the commitment in the tree
|
|
1254
|
-
* @param derivationPath tezos current standard 'm/'
|
|
1255
1150
|
* @returns The unsinged spend description
|
|
1256
1151
|
*/
|
|
1257
|
-
prepareSpendDescription(parametersSpendProof) {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
};
|
|
1267
|
-
});
|
|
1152
|
+
async prepareSpendDescription(parametersSpendProof) {
|
|
1153
|
+
const spendDescription = await sapling__namespace.prepareSpendDescriptionWithAuthorizingKey(parametersSpendProof.saplingContext, __classPrivateFieldGet(this, _InMemoryProvingKey_provingKey, "f"), parametersSpendProof.address, parametersSpendProof.randomCommitmentTrapdoor, parametersSpendProof.publicKeyReRandomization, parametersSpendProof.amount, parametersSpendProof.root, parametersSpendProof.witness);
|
|
1154
|
+
return {
|
|
1155
|
+
commitmentValue: spendDescription.cv,
|
|
1156
|
+
nullifier: spendDescription.nf,
|
|
1157
|
+
publicKeyReRandomization: spendDescription.rk,
|
|
1158
|
+
rtAnchor: spendDescription.rt,
|
|
1159
|
+
proof: spendDescription.proof,
|
|
1160
|
+
};
|
|
1268
1161
|
}
|
|
1269
1162
|
}
|
|
1270
1163
|
_InMemoryProvingKey_provingKey = new WeakMap();
|
|
@@ -1275,7 +1168,7 @@
|
|
|
1275
1168
|
*/
|
|
1276
1169
|
var _SaplingToolkit_inMemorySpendingKey, _SaplingToolkit_saplingId, _SaplingToolkit_contractAddress, _SaplingToolkit_memoSize, _SaplingToolkit_readProvider, _SaplingToolkit_packer, _SaplingToolkit_saplingForger, _SaplingToolkit_saplingTxBuilder, _SaplingToolkit_saplingTransactionViewer;
|
|
1277
1170
|
/**
|
|
1278
|
-
*
|
|
1171
|
+
* Class that surfaces all of the sapling capability allowing to read from a sapling state and prepare transactions
|
|
1279
1172
|
*
|
|
1280
1173
|
* @param keys.saplingSigner Holds the sapling spending key
|
|
1281
1174
|
* @param keys.saplingProver (Optional) Allows to generate the proofs with the proving key rather than the spending key
|
|
@@ -1317,104 +1210,95 @@
|
|
|
1317
1210
|
__classPrivateFieldSet(this, _SaplingToolkit_saplingTxBuilder, saplingTxBuilder, "f");
|
|
1318
1211
|
}
|
|
1319
1212
|
/**
|
|
1320
|
-
*
|
|
1213
|
+
* Get an instance of `SaplingTransactionViewer` which allows to retrieve and decrypt sapling transactions and calculate the unspent balance.
|
|
1321
1214
|
*/
|
|
1322
|
-
getSaplingTransactionViewer() {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
return __classPrivateFieldGet(this, _SaplingToolkit_saplingTransactionViewer, "f");
|
|
1331
|
-
});
|
|
1215
|
+
async getSaplingTransactionViewer() {
|
|
1216
|
+
let saplingTransactionViewer;
|
|
1217
|
+
if (!__classPrivateFieldGet(this, _SaplingToolkit_saplingTransactionViewer, "f")) {
|
|
1218
|
+
const saplingViewingKey = await __classPrivateFieldGet(this, _SaplingToolkit_inMemorySpendingKey, "f").getSaplingViewingKeyProvider();
|
|
1219
|
+
saplingTransactionViewer = new SaplingTransactionViewer(saplingViewingKey, this.getSaplingContractId(), __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f"));
|
|
1220
|
+
__classPrivateFieldSet(this, _SaplingToolkit_saplingTransactionViewer, saplingTransactionViewer, "f");
|
|
1221
|
+
}
|
|
1222
|
+
return __classPrivateFieldGet(this, _SaplingToolkit_saplingTransactionViewer, "f");
|
|
1332
1223
|
}
|
|
1333
1224
|
/**
|
|
1334
|
-
*
|
|
1225
|
+
* Prepare a shielded transaction
|
|
1335
1226
|
* @param shieldedTxParams `to` is the payment address that will receive the shielded tokens (zet).
|
|
1336
1227
|
* `amount` is the amount of shielded tokens in tez by default.
|
|
1337
1228
|
* `mutez` needs to be set to true if the amount of shielded tokens is in mutez.
|
|
1338
1229
|
* `memo` is an empty string by default.
|
|
1339
1230
|
* @returns a string representing the sapling transaction
|
|
1340
1231
|
*/
|
|
1341
|
-
prepareShieldedTransaction(shieldedTxParams) {
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
signature,
|
|
1353
|
-
});
|
|
1354
|
-
return forgedSaplingTx.toString('hex');
|
|
1232
|
+
async prepareShieldedTransaction(shieldedTxParams) {
|
|
1233
|
+
const { formatedParams, totalAmount } = this.formatTransactionParams(shieldedTxParams, this.validateDestinationSaplingAddress);
|
|
1234
|
+
const root = await this.getRoot();
|
|
1235
|
+
const { inputs, outputs, signature, balance } = await __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createShieldedTx(formatedParams, totalAmount, DEFAULT_BOUND_DATA);
|
|
1236
|
+
const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
|
|
1237
|
+
inputs,
|
|
1238
|
+
outputs,
|
|
1239
|
+
balance,
|
|
1240
|
+
root,
|
|
1241
|
+
boundData: DEFAULT_BOUND_DATA,
|
|
1242
|
+
signature,
|
|
1355
1243
|
});
|
|
1244
|
+
return forgedSaplingTx.toString('hex');
|
|
1356
1245
|
}
|
|
1357
1246
|
/**
|
|
1358
|
-
*
|
|
1247
|
+
* Prepare an unshielded transaction
|
|
1359
1248
|
* @param unshieldedTxParams `to` is the Tezos address that will receive the unshielded tokens (tz1, tz2 or tz3).
|
|
1360
1249
|
* `amount` is the amount of unshielded tokens in tez by default.
|
|
1361
1250
|
* `mutez` needs to be set to true if the amount of unshielded tokens is in mutez.
|
|
1362
1251
|
* @returns a string representing the sapling transaction.
|
|
1363
1252
|
*/
|
|
1364
|
-
prepareUnshieldedTransaction(unshieldedTxParams) {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
signature,
|
|
1378
|
-
});
|
|
1379
|
-
return forgedSaplingTx.toString('hex');
|
|
1253
|
+
async prepareUnshieldedTransaction(unshieldedTxParams) {
|
|
1254
|
+
const { formatedParams, totalAmount } = this.formatTransactionParams([unshieldedTxParams], this.validateDestinationImplicitAddress);
|
|
1255
|
+
const boundData = await this.createBoundData(formatedParams[0].to);
|
|
1256
|
+
const root = await this.getRoot();
|
|
1257
|
+
const chosenInputs = await this.selectInputsToSpend(new BigNumber(formatedParams[0].amount));
|
|
1258
|
+
const { inputs, outputs, signature, balance } = await __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createSaplingTx([], totalAmount, boundData, chosenInputs);
|
|
1259
|
+
const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
|
|
1260
|
+
inputs,
|
|
1261
|
+
outputs,
|
|
1262
|
+
balance,
|
|
1263
|
+
root,
|
|
1264
|
+
boundData,
|
|
1265
|
+
signature,
|
|
1380
1266
|
});
|
|
1267
|
+
return forgedSaplingTx.toString('hex');
|
|
1381
1268
|
}
|
|
1382
1269
|
/**
|
|
1383
|
-
*
|
|
1270
|
+
* Prepare a sapling transaction (zet to zet)
|
|
1384
1271
|
* @param saplingTxParams `to` is the payment address that will receive the shielded tokens (zet).
|
|
1385
1272
|
* `amount` is the amount of unshielded tokens in tez by default.
|
|
1386
1273
|
* `mutez` needs to be set to true if the amount of unshielded tokens is in mutez.
|
|
1387
1274
|
* `memo` is an empty string by default.
|
|
1388
1275
|
* @returns a string representing the sapling transaction.
|
|
1389
1276
|
*/
|
|
1390
|
-
prepareSaplingTransaction(saplingTxParams) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
signature,
|
|
1403
|
-
});
|
|
1404
|
-
return forgedSaplingTx.toString('hex');
|
|
1277
|
+
async prepareSaplingTransaction(saplingTxParams) {
|
|
1278
|
+
const { formatedParams, totalAmount } = this.formatTransactionParams(saplingTxParams, this.validateDestinationSaplingAddress);
|
|
1279
|
+
const root = await this.getRoot();
|
|
1280
|
+
const chosenInputs = await this.selectInputsToSpend(totalAmount);
|
|
1281
|
+
const { inputs, outputs, signature, balance } = await __classPrivateFieldGet(this, _SaplingToolkit_saplingTxBuilder, "f").createSaplingTx(formatedParams, totalAmount, DEFAULT_BOUND_DATA, chosenInputs);
|
|
1282
|
+
const forgedSaplingTx = __classPrivateFieldGet(this, _SaplingToolkit_saplingForger, "f").forgeSaplingTransaction({
|
|
1283
|
+
inputs,
|
|
1284
|
+
outputs,
|
|
1285
|
+
balance,
|
|
1286
|
+
root,
|
|
1287
|
+
boundData: DEFAULT_BOUND_DATA,
|
|
1288
|
+
signature,
|
|
1405
1289
|
});
|
|
1290
|
+
return forgedSaplingTx.toString('hex');
|
|
1406
1291
|
}
|
|
1407
1292
|
formatTransactionParams(txParams, validateDestination) {
|
|
1408
1293
|
const formatedParams = [];
|
|
1409
1294
|
let totalAmount = new BigNumber(0);
|
|
1410
1295
|
txParams.forEach((param) => {
|
|
1411
|
-
var _a;
|
|
1412
1296
|
validateDestination(param.to);
|
|
1413
1297
|
const amountMutez = param.mutez
|
|
1414
1298
|
? param.amount.toString()
|
|
1415
1299
|
: utils.format('tz', 'mutez', param.amount).toString();
|
|
1416
1300
|
totalAmount = totalAmount.plus(new BigNumber(amountMutez));
|
|
1417
|
-
const memo =
|
|
1301
|
+
const memo = param.memo ?? DEFAULT_MEMO;
|
|
1418
1302
|
if (memo.length > __classPrivateFieldGet(this, _SaplingToolkit_memoSize, "f")) {
|
|
1419
1303
|
throw new InvalidMemo(memo, `expecting length to be less than ${__classPrivateFieldGet(this, _SaplingToolkit_memoSize, "f")}`);
|
|
1420
1304
|
}
|
|
@@ -1422,27 +1306,23 @@
|
|
|
1422
1306
|
});
|
|
1423
1307
|
return { formatedParams, totalAmount };
|
|
1424
1308
|
}
|
|
1425
|
-
getRoot() {
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
}
|
|
1435
|
-
});
|
|
1309
|
+
async getRoot() {
|
|
1310
|
+
if (__classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f")) {
|
|
1311
|
+
const { root } = await __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f").getSaplingDiffById({ id: __classPrivateFieldGet(this, _SaplingToolkit_saplingId, "f") }, 'head');
|
|
1312
|
+
return root;
|
|
1313
|
+
}
|
|
1314
|
+
else {
|
|
1315
|
+
const { root } = await __classPrivateFieldGet(this, _SaplingToolkit_readProvider, "f").getSaplingDiffByContract(__classPrivateFieldGet(this, _SaplingToolkit_contractAddress, "f"), 'head');
|
|
1316
|
+
return root;
|
|
1317
|
+
}
|
|
1436
1318
|
}
|
|
1437
|
-
createBoundData(destination) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
type: { prim: 'bytes' },
|
|
1443
|
-
});
|
|
1444
|
-
return Buffer.from(packedDestination.packed, 'hex');
|
|
1319
|
+
async createBoundData(destination) {
|
|
1320
|
+
const bytes = utils.b58DecodePublicKeyHash(destination, 'hex');
|
|
1321
|
+
const packedDestination = await __classPrivateFieldGet(this, _SaplingToolkit_packer, "f").packData({
|
|
1322
|
+
data: { bytes },
|
|
1323
|
+
type: { prim: 'bytes' },
|
|
1445
1324
|
});
|
|
1325
|
+
return Buffer.from(packedDestination.packed, 'hex');
|
|
1446
1326
|
}
|
|
1447
1327
|
validateDestinationImplicitAddress(to) {
|
|
1448
1328
|
const toValidation = utils.validateKeyHash(to);
|
|
@@ -1454,7 +1334,7 @@
|
|
|
1454
1334
|
try {
|
|
1455
1335
|
utils.b58DecodeAndCheckPrefix(to, [utils.PrefixV2.SaplingAddress]);
|
|
1456
1336
|
}
|
|
1457
|
-
catch
|
|
1337
|
+
catch {
|
|
1458
1338
|
throw new core.InvalidAddressError(to, `expecting prefix ${utils.PrefixV2.SaplingAddress}.`);
|
|
1459
1339
|
}
|
|
1460
1340
|
}
|
|
@@ -1468,25 +1348,23 @@
|
|
|
1468
1348
|
}
|
|
1469
1349
|
return saplingContractId;
|
|
1470
1350
|
}
|
|
1471
|
-
selectInputsToSpend(amountMutez) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
inputsToSpend.push(rest);
|
|
1483
|
-
}
|
|
1484
|
-
});
|
|
1485
|
-
if (sumSelectedInputs.isLessThan(new BigNumber(amountMutez))) {
|
|
1486
|
-
throw new InsufficientBalance(sumSelectedInputs.toString(), amountMutez.toString());
|
|
1351
|
+
async selectInputsToSpend(amountMutez) {
|
|
1352
|
+
const saplingTxViewer = await this.getSaplingTransactionViewer();
|
|
1353
|
+
const { incoming } = await saplingTxViewer.getIncomingAndOutgoingTransactionsRaw();
|
|
1354
|
+
const inputsToSpend = [];
|
|
1355
|
+
let sumSelectedInputs = new BigNumber(0);
|
|
1356
|
+
incoming.forEach((input) => {
|
|
1357
|
+
if (!input.isSpent && sumSelectedInputs.isLessThan(amountMutez)) {
|
|
1358
|
+
const txAmount = convertValueToBigNumber(input.value);
|
|
1359
|
+
sumSelectedInputs = sumSelectedInputs.plus(txAmount);
|
|
1360
|
+
const { isSpent: _isSpent, ...rest } = input;
|
|
1361
|
+
inputsToSpend.push(rest);
|
|
1487
1362
|
}
|
|
1488
|
-
return { inputsToSpend, sumSelectedInputs };
|
|
1489
1363
|
});
|
|
1364
|
+
if (sumSelectedInputs.isLessThan(new BigNumber(amountMutez))) {
|
|
1365
|
+
throw new InsufficientBalance(sumSelectedInputs.toString(), amountMutez.toString());
|
|
1366
|
+
}
|
|
1367
|
+
return { inputsToSpend, sumSelectedInputs };
|
|
1490
1368
|
}
|
|
1491
1369
|
}
|
|
1492
1370
|
_SaplingToolkit_inMemorySpendingKey = new WeakMap(), _SaplingToolkit_saplingId = new WeakMap(), _SaplingToolkit_contractAddress = new WeakMap(), _SaplingToolkit_memoSize = new WeakMap(), _SaplingToolkit_readProvider = new WeakMap(), _SaplingToolkit_packer = new WeakMap(), _SaplingToolkit_saplingForger = new WeakMap(), _SaplingToolkit_saplingTxBuilder = new WeakMap(), _SaplingToolkit_saplingTransactionViewer = new WeakMap();
|