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