mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
refactor combine logic for clarity (#1486)
refactor combine logic for clarity by using functional styles and breaking down the logic into digestible pieces
This commit is contained in:
@@ -1,43 +1,66 @@
|
||||
import * as _ from 'lodash'
|
||||
import binary from 'ripple-binary-codec'
|
||||
import * as utils from './utils'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {ValidationError} from '../common/errors'
|
||||
import {decodeAccountID} from 'ripple-address-codec'
|
||||
import {validate} from '../common'
|
||||
import {computeBinaryTransactionHash} from '../common/hashes'
|
||||
import {JsonObject} from 'ripple-binary-codec/dist/types/serialized-type'
|
||||
|
||||
/**
|
||||
* The transactions should all be equal except for the 'Signers' field.
|
||||
*/
|
||||
function validateTransactionEquivalence(transactions: Array<JsonObject>) {
|
||||
const exampleTransaction = JSON.stringify({...transactions[0], Signers: null})
|
||||
if (transactions.slice(1).some(tx => JSON.stringify({...tx, Signers: null}) !== exampleTransaction)) {
|
||||
throw new ValidationError('txJSON is not the same for all signedTransactions')
|
||||
}
|
||||
}
|
||||
|
||||
function addressToBigNumber(address) {
|
||||
const hex = Buffer.from(decodeAccountID(address)).toString('hex')
|
||||
return new BigNumber(hex, 16)
|
||||
}
|
||||
|
||||
/**
|
||||
* If presented in binary form, the Signers array must be sorted based on
|
||||
* the numeric value of the signer addresses, with the lowest value first.
|
||||
* (If submitted as JSON, the submit_multisigned method handles this automatically.)
|
||||
* https://xrpl.org/multi-signing.html
|
||||
*/
|
||||
function compareSigners(a, b) {
|
||||
return addressToBigNumber(a.Signer.Account).comparedTo(
|
||||
addressToBigNumber(b.Signer.Account)
|
||||
)
|
||||
}
|
||||
|
||||
function getTransactionWithAllSigners(transactions: Array<JsonObject>): JsonObject {
|
||||
// Signers must be sorted - see compareSigners for more details
|
||||
const sortedSigners = _.flatMap(transactions, tx => tx.Signers)
|
||||
.filter(signer => signer)
|
||||
.sort(compareSigners)
|
||||
|
||||
return {...transactions[0], Signers: sortedSigners}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param signedTransactions A collection of the same transaction signed by different signers. The only difference
|
||||
* between the elements of signedTransactions should be the Signers field.
|
||||
* @returns An object with the combined transaction (now having a sorted list of all signers) which is encoded, along
|
||||
* with a transaction id based on the combined transaction.
|
||||
*/
|
||||
function combine(signedTransactions: Array<string>): object {
|
||||
validate.combine({signedTransactions})
|
||||
|
||||
// TODO: signedTransactions is an array of strings in the documentation, but
|
||||
// tests and this code handle it as an array of objects. Fix!
|
||||
const txs: any[] = signedTransactions.map(binary.decode)
|
||||
const tx = _.omit(txs[0], 'Signers')
|
||||
if (!txs.every((_tx) => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON is not the same for all signedTransactions'
|
||||
)
|
||||
const transactions: JsonObject[] = signedTransactions.map(binary.decode);
|
||||
validateTransactionEquivalence(transactions)
|
||||
|
||||
const signedTransaction = binary.encode(getTransactionWithAllSigners(transactions))
|
||||
return {
|
||||
signedTransaction: signedTransaction,
|
||||
id: computeBinaryTransactionHash(signedTransaction)
|
||||
}
|
||||
const unsortedSigners = txs.reduce(
|
||||
(accumulator, _tx) => accumulator.concat(_tx.Signers || []),
|
||||
[]
|
||||
)
|
||||
const signers = unsortedSigners.sort(compareSigners)
|
||||
const signedTx = Object.assign({}, tx, {Signers: signers})
|
||||
const signedTransaction = binary.encode(signedTx)
|
||||
const id = computeBinaryTransactionHash(signedTransaction)
|
||||
return {signedTransaction, id}
|
||||
}
|
||||
|
||||
export default combine
|
||||
|
||||
Reference in New Issue
Block a user