xrpl 5.0.0 → 5.1.0-batch.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/build/xrpl-latest-min.js +1 -1
- package/build/xrpl-latest-min.js.map +1 -1
- package/build/xrpl-latest.js +68 -17
- package/build/xrpl-latest.js.map +1 -1
- package/dist/npm/Wallet/batchSigner.d.ts.map +1 -1
- package/dist/npm/Wallet/batchSigner.js +42 -15
- package/dist/npm/Wallet/batchSigner.js.map +1 -1
- package/package.json +2 -2
- package/src/Wallet/batchSigner.ts +75 -21
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchSigner.d.ts","sourceRoot":"","sources":["../../../src/Wallet/batchSigner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAyB,MAAM,WAAW,CAAA;AAMxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"batchSigner.d.ts","sourceRoot":"","sources":["../../../src/Wallet/batchSigner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAyB,MAAM,WAAW,CAAA;AAMxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAgErC,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,KAAK,EAClB,IAAI,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAAO,GACjE,IAAI,CA0DN;AAYD,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,GAClC,MAAM,CAmCR"}
|
|
@@ -37,6 +37,14 @@ function constructBatchSignerObject(batchAccount, wallet, signature, multisignAd
|
|
|
37
37
|
}
|
|
38
38
|
return batchSigner;
|
|
39
39
|
}
|
|
40
|
+
function getBatchSeqValue(transaction) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const sequence = (_a = transaction.Sequence) !== null && _a !== void 0 ? _a : 0;
|
|
43
|
+
if (sequence !== 0) {
|
|
44
|
+
return sequence;
|
|
45
|
+
}
|
|
46
|
+
return (_b = transaction.TicketSequence) !== null && _b !== void 0 ? _b : 0;
|
|
47
|
+
}
|
|
40
48
|
function signMultiBatch(wallet, transaction, opts = {}) {
|
|
41
49
|
var _a;
|
|
42
50
|
const batchAccount = (_a = opts.batchAccount) !== null && _a !== void 0 ? _a : wallet.classicAddress;
|
|
@@ -51,14 +59,19 @@ function signMultiBatch(wallet, transaction, opts = {}) {
|
|
|
51
59
|
throw new errors_1.ValidationError('Must be a Batch transaction.');
|
|
52
60
|
}
|
|
53
61
|
(0, models_1.validate)(transaction);
|
|
54
|
-
const involvedAccounts = new Set(
|
|
62
|
+
const involvedAccounts = new Set();
|
|
63
|
+
transaction.RawTransactions.forEach((raw) => {
|
|
64
|
+
involvedAccounts.add(raw.RawTransaction.Account);
|
|
65
|
+
const counterparty = raw.RawTransaction
|
|
66
|
+
.Counterparty;
|
|
67
|
+
if (typeof counterparty === 'string') {
|
|
68
|
+
involvedAccounts.add(counterparty);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
55
71
|
if (!involvedAccounts.has(batchAccount)) {
|
|
56
72
|
throw new errors_1.ValidationError('Must be signing for an address submitting a transaction in the Batch.');
|
|
57
73
|
}
|
|
58
|
-
const fieldsToSign = {
|
|
59
|
-
flags: transaction.Flags,
|
|
60
|
-
txIDs: transaction.RawTransactions.map((rawTx) => (0, hashes_1.hashSignedTx)(rawTx.RawTransaction)),
|
|
61
|
-
};
|
|
74
|
+
const fieldsToSign = Object.assign({ account: transaction.Account, sequence: getBatchSeqValue(transaction), flags: transaction.Flags, txIDs: transaction.RawTransactions.map((rawTx) => (0, hashes_1.hashSignedTx)(rawTx.RawTransaction)), batchAccount }, (multisignAddress ? { signerAccount: multisignAddress } : {}));
|
|
62
75
|
const signature = (0, ripple_keypairs_1.sign)((0, ripple_binary_codec_1.encodeForSigningBatch)(fieldsToSign), wallet.privateKey);
|
|
63
76
|
transaction.BatchSigners = [
|
|
64
77
|
constructBatchSignerObject(batchAccount, wallet, signature, multisignAddress),
|
|
@@ -89,23 +102,37 @@ function combineBatchSigners(transactions) {
|
|
|
89
102
|
return (0, ripple_binary_codec_1.encode)(getTransactionWithAllBatchSigners(batchTransactions));
|
|
90
103
|
}
|
|
91
104
|
exports.combineBatchSigners = combineBatchSigners;
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
});
|
|
97
|
-
if (transactions.slice(1).some((tx) => JSON.stringify({
|
|
105
|
+
function getBatchEquivalenceKey(tx) {
|
|
106
|
+
return JSON.stringify({
|
|
107
|
+
account: tx.Account,
|
|
108
|
+
sequence: getBatchSeqValue(tx),
|
|
98
109
|
flags: tx.Flags,
|
|
99
110
|
transactionIDs: tx.RawTransactions.map((rawTx) => (0, hashes_1.hashSignedTx)(rawTx.RawTransaction)),
|
|
100
|
-
})
|
|
101
|
-
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function validateBatchTransactionEquivalence(transactions) {
|
|
114
|
+
const exampleTransaction = getBatchEquivalenceKey(transactions[0]);
|
|
115
|
+
if (transactions
|
|
116
|
+
.slice(1)
|
|
117
|
+
.some((tx) => getBatchEquivalenceKey(tx) !== exampleTransaction)) {
|
|
118
|
+
throw new errors_1.ValidationError('Account, sequence, flags, and transaction hashes must be the same for all provided transactions.');
|
|
102
119
|
}
|
|
103
120
|
}
|
|
104
121
|
function getTransactionWithAllBatchSigners(transactions) {
|
|
122
|
+
const outerAccount = transactions[0].Account;
|
|
105
123
|
const sortedSigners = transactions
|
|
106
124
|
.flatMap((tx) => { var _a; return (_a = tx.BatchSigners) !== null && _a !== void 0 ? _a : []; })
|
|
107
|
-
.filter((signer) => signer.BatchSigner.Account !==
|
|
125
|
+
.filter((signer) => signer.BatchSigner.Account !== outerAccount)
|
|
108
126
|
.sort((signer1, signer2) => (0, utils_1.compareSigners)(signer1.BatchSigner, signer2.BatchSigner));
|
|
109
|
-
|
|
127
|
+
const dedupedSigners = [];
|
|
128
|
+
let lastAccount = '';
|
|
129
|
+
for (const signer of sortedSigners) {
|
|
130
|
+
const account = signer.BatchSigner.Account;
|
|
131
|
+
if (account !== lastAccount) {
|
|
132
|
+
dedupedSigners.push(signer);
|
|
133
|
+
lastAccount = account;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return Object.assign(Object.assign({}, transactions[0]), { BatchSigners: dedupedSigners });
|
|
110
137
|
}
|
|
111
138
|
//# sourceMappingURL=batchSigner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchSigner.js","sourceRoot":"","sources":["../../../src/Wallet/batchSigner.ts"],"names":[],"mappings":";;;AAAA,6DAAmE;AACnE,qDAAsC;AAEtC,sCAA2C;AAC3C,sCAAwD;AACxD,wDAAyE;AACzE,4CAA8C;AAE9C,mCAA+D;AAK/D,SAAS,0BAA0B,CACjC,YAAoB,EACpB,MAAc,EACd,SAAiB,EACjB,mBAAmC,KAAK;IAExC,IAAI,WAAwB,CAAA;IAC5B,IAAI,gBAAgB,EAAE;QACpB,WAAW,GAAG;YACZ,WAAW,EAAE;gBACX,OAAO,EAAE,YAAY;gBACrB,OAAO,EAAE;oBACP;wBACE,MAAM,EAAE;4BACN,OAAO,EAAE,gBAAgB;4BACzB,aAAa,EAAE,MAAM,CAAC,SAAS;4BAC/B,YAAY,EAAE,SAAS;yBACxB;qBACF;iBACF;aACF;SACF,CAAA;KACF;SAAM;QACL,WAAW,GAAG;YACZ,WAAW,EAAE;gBACX,OAAO,EAAE,YAAY;gBACrB,aAAa,EAAE,MAAM,CAAC,SAAS;gBAC/B,YAAY,EAAE,SAAS;aACxB;SACF,CAAA;KACF;IACD,OAAO,WAAW,CAAA;AACpB,CAAC;
|
|
1
|
+
{"version":3,"file":"batchSigner.js","sourceRoot":"","sources":["../../../src/Wallet/batchSigner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xrpl",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.1.0-batch.1",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
|
6
6
|
"files": [
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"eventemitter3": "^5.0.1",
|
|
31
31
|
"fast-json-stable-stringify": "^2.1.0",
|
|
32
32
|
"ripple-address-codec": "^5.0.1",
|
|
33
|
-
"ripple-binary-codec": "^2.
|
|
33
|
+
"ripple-binary-codec": "^2.9.0-batch.1",
|
|
34
34
|
"ripple-keypairs": "^3.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
@@ -45,6 +45,21 @@ function constructBatchSignerObject(
|
|
|
45
45
|
return batchSigner
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the sequence value bound into a Batch signature: the `Sequence` when
|
|
50
|
+
* non-zero, otherwise the `TicketSequence` value (or 0).
|
|
51
|
+
*
|
|
52
|
+
* @param transaction - The Batch transaction being signed.
|
|
53
|
+
* @returns The sequence value to bind into the signature.
|
|
54
|
+
*/
|
|
55
|
+
function getBatchSeqValue(transaction: Batch): number {
|
|
56
|
+
const sequence = transaction.Sequence ?? 0
|
|
57
|
+
if (sequence !== 0) {
|
|
58
|
+
return sequence
|
|
59
|
+
}
|
|
60
|
+
return transaction.TicketSequence ?? 0
|
|
61
|
+
}
|
|
62
|
+
|
|
48
63
|
/**
|
|
49
64
|
* Sign a multi-account Batch transaction.
|
|
50
65
|
*
|
|
@@ -56,6 +71,7 @@ function constructBatchSignerObject(
|
|
|
56
71
|
* The actual address is only needed in the case of regular key usage.
|
|
57
72
|
* @throws ValidationError if the transaction is malformed.
|
|
58
73
|
*/
|
|
74
|
+
// eslint-disable-next-line max-lines-per-function -- cohesive signing routine
|
|
59
75
|
export function signMultiBatch(
|
|
60
76
|
wallet: Wallet,
|
|
61
77
|
transaction: Batch,
|
|
@@ -79,19 +95,33 @@ export function signMultiBatch(
|
|
|
79
95
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- validate does not accept Transaction type
|
|
80
96
|
validate(transaction as unknown as Record<string, unknown>)
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
98
|
+
// An account must sign the Batch if it submits an inner transaction or is the
|
|
99
|
+
// `Counterparty` of one.
|
|
100
|
+
const involvedAccounts = new Set<string>()
|
|
101
|
+
transaction.RawTransactions.forEach((raw) => {
|
|
102
|
+
involvedAccounts.add(raw.RawTransaction.Account)
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Counterparty only exists on some inner tx types
|
|
104
|
+
const counterparty = (raw.RawTransaction as Record<string, unknown>)
|
|
105
|
+
.Counterparty
|
|
106
|
+
if (typeof counterparty === 'string') {
|
|
107
|
+
involvedAccounts.add(counterparty)
|
|
108
|
+
}
|
|
109
|
+
})
|
|
85
110
|
if (!involvedAccounts.has(batchAccount)) {
|
|
86
111
|
throw new ValidationError(
|
|
87
112
|
'Must be signing for an address submitting a transaction in the Batch.',
|
|
88
113
|
)
|
|
89
114
|
}
|
|
90
115
|
const fieldsToSign = {
|
|
116
|
+
account: transaction.Account,
|
|
117
|
+
sequence: getBatchSeqValue(transaction),
|
|
91
118
|
flags: transaction.Flags,
|
|
92
119
|
txIDs: transaction.RawTransactions.map((rawTx) =>
|
|
93
120
|
hashSignedTx(rawTx.RawTransaction),
|
|
94
121
|
),
|
|
122
|
+
batchAccount,
|
|
123
|
+
// Multi-signed batch signers also bind the inner signer account.
|
|
124
|
+
...(multisignAddress ? { signerAccount: multisignAddress } : {}),
|
|
95
125
|
}
|
|
96
126
|
const signature = sign(encodeForSigningBatch(fieldsToSign), wallet.privateKey)
|
|
97
127
|
|
|
@@ -155,6 +185,26 @@ export function combineBatchSigners(
|
|
|
155
185
|
return encode(getTransactionWithAllBatchSigners(batchTransactions))
|
|
156
186
|
}
|
|
157
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Builds a comparison key over every field bound into a Batch signature
|
|
190
|
+
* (XLS-56 V1_1): the outer account, sequence value, flags, and inner
|
|
191
|
+
* transaction IDs. Fragments that disagree on any of these were signed over
|
|
192
|
+
* different payloads and cannot be combined.
|
|
193
|
+
*
|
|
194
|
+
* @param tx - The Batch transaction to derive the key from.
|
|
195
|
+
* @returns A stable string key for equivalence comparison.
|
|
196
|
+
*/
|
|
197
|
+
function getBatchEquivalenceKey(tx: Batch): string {
|
|
198
|
+
return JSON.stringify({
|
|
199
|
+
account: tx.Account,
|
|
200
|
+
sequence: getBatchSeqValue(tx),
|
|
201
|
+
flags: tx.Flags,
|
|
202
|
+
transactionIDs: tx.RawTransactions.map((rawTx) =>
|
|
203
|
+
hashSignedTx(rawTx.RawTransaction),
|
|
204
|
+
),
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
158
208
|
/**
|
|
159
209
|
* The transactions should all be equal except for the 'Signers' field.
|
|
160
210
|
*
|
|
@@ -162,37 +212,41 @@ export function combineBatchSigners(
|
|
|
162
212
|
* @throws ValidationError if the transactions are not equal in any field other than 'Signers'.
|
|
163
213
|
*/
|
|
164
214
|
function validateBatchTransactionEquivalence(transactions: Batch[]): void {
|
|
165
|
-
const exampleTransaction =
|
|
166
|
-
flags: transactions[0].Flags,
|
|
167
|
-
transactionIDs: transactions[0].RawTransactions.map((rawTx) =>
|
|
168
|
-
hashSignedTx(rawTx.RawTransaction),
|
|
169
|
-
),
|
|
170
|
-
})
|
|
215
|
+
const exampleTransaction = getBatchEquivalenceKey(transactions[0])
|
|
171
216
|
if (
|
|
172
|
-
transactions
|
|
173
|
-
(
|
|
174
|
-
|
|
175
|
-
flags: tx.Flags,
|
|
176
|
-
transactionIDs: tx.RawTransactions.map((rawTx) =>
|
|
177
|
-
hashSignedTx(rawTx.RawTransaction),
|
|
178
|
-
),
|
|
179
|
-
}) !== exampleTransaction,
|
|
180
|
-
)
|
|
217
|
+
transactions
|
|
218
|
+
.slice(1)
|
|
219
|
+
.some((tx) => getBatchEquivalenceKey(tx) !== exampleTransaction)
|
|
181
220
|
) {
|
|
182
221
|
throw new ValidationError(
|
|
183
|
-
'
|
|
222
|
+
'Account, sequence, flags, and transaction hashes must be the same for all provided transactions.',
|
|
184
223
|
)
|
|
185
224
|
}
|
|
186
225
|
}
|
|
187
226
|
|
|
188
227
|
function getTransactionWithAllBatchSigners(transactions: Batch[]): Batch {
|
|
228
|
+
const outerAccount = transactions[0].Account
|
|
229
|
+
|
|
189
230
|
// Signers must be sorted in the combined transaction - See compareSigners' documentation for more details
|
|
190
231
|
const sortedSigners: BatchSigner[] = transactions
|
|
191
232
|
.flatMap((tx) => tx.BatchSigners ?? [])
|
|
192
|
-
|
|
233
|
+
// A batch signer cannot be the outer account (rippled: temBAD_SIGNER).
|
|
234
|
+
.filter((signer) => signer.BatchSigner.Account !== outerAccount)
|
|
193
235
|
.sort((signer1, signer2) =>
|
|
194
236
|
compareSigners(signer1.BatchSigner, signer2.BatchSigner),
|
|
195
237
|
)
|
|
196
238
|
|
|
197
|
-
|
|
239
|
+
// BatchSigners must be strictly ascending and unique by account, so
|
|
240
|
+
// de-duplicate when combining fragments that share a signer.
|
|
241
|
+
const dedupedSigners: BatchSigner[] = []
|
|
242
|
+
let lastAccount = ''
|
|
243
|
+
for (const signer of sortedSigners) {
|
|
244
|
+
const account = signer.BatchSigner.Account
|
|
245
|
+
if (account !== lastAccount) {
|
|
246
|
+
dedupedSigners.push(signer)
|
|
247
|
+
lastAccount = account
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { ...transactions[0], BatchSigners: dedupedSigners }
|
|
198
252
|
}
|