mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
Refactor: Rewrite transaction signing (#1693)
* refactor: sign with wallet * refactor: change multisign and rename signTx to sign * refactor: specify multisign as boolean * refactor: support address as well as booleans for multisign * feat: return hash from sign fn and fix tests
This commit is contained in:
@@ -5,7 +5,6 @@ import { ValidationError, XrplError } from '../errors'
|
||||
import { TxResponse } from '../models/methods'
|
||||
import { Transaction } from '../models/transactions'
|
||||
import { hashes } from '../utils'
|
||||
import { sign } from '../wallet/signer'
|
||||
|
||||
// general time for a ledger to close, in milliseconds
|
||||
const LEDGER_CLOSE_TIME = 4000
|
||||
@@ -35,8 +34,8 @@ async function submit(
|
||||
transaction: Transaction,
|
||||
): Promise<SubmitResponse> {
|
||||
const tx = await this.autofill(transaction)
|
||||
const signedTxEncoded = sign(wallet, tx)
|
||||
return this.submitSigned(signedTxEncoded)
|
||||
const { tx_blob } = wallet.sign(tx)
|
||||
return this.submitSigned(tx_blob)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,8 +82,8 @@ async function submitReliable(
|
||||
transaction: Transaction,
|
||||
): Promise<TxResponse> {
|
||||
const tx = await this.autofill(transaction)
|
||||
const signedTxEncoded = sign(wallet, tx)
|
||||
return this.submitSignedReliable(signedTxEncoded)
|
||||
const { tx_blob } = wallet.sign(tx)
|
||||
return this.submitSignedReliable(tx_blob)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import ECDSA from '../ecdsa'
|
||||
import { ValidationError } from '../errors'
|
||||
import { Transaction } from '../models/transactions'
|
||||
import { hashSignedTx } from '../utils/hashes/ledgerHash'
|
||||
|
||||
const DEFAULT_ALGORITHM: ECDSA = ECDSA.ed25519
|
||||
const DEFAULT_DERIVATION_PATH = "m/44'/144'/0'/0/0"
|
||||
@@ -30,6 +31,10 @@ const DEFAULT_DERIVATION_PATH = "m/44'/144'/0'/0/0"
|
||||
function hexFromBuffer(buffer: Buffer): string {
|
||||
return buffer.toString('hex').toUpperCase()
|
||||
}
|
||||
export interface SignedTxBlobHash {
|
||||
tx_blob: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
|
||||
@@ -171,17 +176,23 @@ class Wallet {
|
||||
*
|
||||
* @param this - Wallet instance.
|
||||
* @param transaction - A transaction to be signed offline.
|
||||
* @param multisignAddress - Multisign only. An account address corresponding to the multi-signature being added. If this
|
||||
* wallet represents your [master keypair](https://xrpl.org/cryptographic-keys.html#master-key-pair) you can get your account address
|
||||
* with the Wallet.getClassicAddress() function.
|
||||
* @param multisign - Specify true/false to use multisign or actual address (classic/x-address) to make multisign tx request.
|
||||
* @returns A signed transaction.
|
||||
* @throws ValidationError if the transaction is already signed or does not encode/decode to same result.
|
||||
*/
|
||||
public signTransaction(
|
||||
// eslint-disable-next-line max-lines-per-function -- introduced more checks to support both string and boolean inputs.
|
||||
public sign(
|
||||
this: Wallet,
|
||||
transaction: Transaction,
|
||||
multisignAddress?: string,
|
||||
): string {
|
||||
multisign?: boolean | string,
|
||||
): SignedTxBlobHash {
|
||||
let multisignAddress: boolean | string = false
|
||||
if (typeof multisign === 'string' && multisign.startsWith('X')) {
|
||||
multisignAddress = multisign
|
||||
} else if (multisign) {
|
||||
multisignAddress = this.getClassicAddress()
|
||||
}
|
||||
|
||||
if (transaction.TxnSignature || transaction.Signers) {
|
||||
throw new ValidationError(
|
||||
'txJSON must not contain "TxnSignature" or "Signers" properties',
|
||||
@@ -211,7 +222,10 @@ class Wallet {
|
||||
}
|
||||
const serialized = encode(txToSignAndEncode)
|
||||
this.checkTxSerialization(serialized, transaction)
|
||||
return serialized
|
||||
return {
|
||||
tx_blob: serialized,
|
||||
hash: hashSignedTx(serialized),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,22 +16,6 @@ import { validateBaseTransaction } from '../models/transactions/common'
|
||||
|
||||
import Wallet from '.'
|
||||
|
||||
/**
|
||||
* Uses a wallet to cryptographically sign a transaction which proves the owner of the wallet
|
||||
* is issuing this transaction.
|
||||
*
|
||||
* @param wallet - A Wallet that holds your cryptographic keys.
|
||||
* @param tx - The Transaction that is being signed.
|
||||
* @param forMultisign - If true, changes the signature format to encode for multisigning.
|
||||
* @returns A signed Transaction.
|
||||
*/
|
||||
function sign(wallet: Wallet, tx: Transaction, forMultisign = false): string {
|
||||
return wallet.signTransaction(
|
||||
tx,
|
||||
forMultisign ? wallet.getClassicAddress() : '',
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes several transactions with Signer fields (in object or blob form) and creates a
|
||||
* single transaction with all Signers that then gets signed and returned.
|
||||
@@ -183,4 +167,4 @@ function getDecodedTransaction(txOrBlob: Transaction | string): Transaction {
|
||||
return decode(txOrBlob) as unknown as Transaction
|
||||
}
|
||||
|
||||
export { sign, authorizeChannel, verifySignature, multisign }
|
||||
export { authorizeChannel, verifySignature, multisign }
|
||||
|
||||
@@ -5,7 +5,7 @@ import _ from 'lodash'
|
||||
import { Client } from 'xrpl-local'
|
||||
import { AccountSet, SignerListSet } from 'xrpl-local/models/transactions'
|
||||
import { convertStringToHex } from 'xrpl-local/utils'
|
||||
import { sign, multisign } from 'xrpl-local/wallet/signer'
|
||||
import { multisign } from 'xrpl-local/wallet/signer'
|
||||
|
||||
import serverUrl from './serverUrl'
|
||||
import { setupClient, suiteClientSetup, teardownClient } from './setup'
|
||||
@@ -64,9 +64,9 @@ describe('integration tests', function () {
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
const accountSetTx = await client.autofill(accountSet, 2)
|
||||
const signed1 = sign(signerWallet1, accountSetTx, true)
|
||||
const signed2 = sign(signerWallet2, accountSetTx, true)
|
||||
const multisignedTx = multisign([signed1, signed2])
|
||||
const { tx_blob: tx_blob1 } = signerWallet1.sign(accountSetTx, true)
|
||||
const { tx_blob: tx_blob2 } = signerWallet2.sign(accountSetTx, true)
|
||||
const multisignedTx = multisign([tx_blob1, tx_blob2])
|
||||
const submitResponse = await client.submitSigned(multisignedTx)
|
||||
await ledgerAccept(client)
|
||||
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('reliable submission', function () {
|
||||
Account: this.wallet.getClassicAddress(),
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
const signedAccountSet = this.wallet.signTransaction(
|
||||
const { tx_blob: signedAccountSet } = this.wallet.sign(
|
||||
await this.client.autofill(accountSet),
|
||||
)
|
||||
const responsePromise = this.client.submitSignedReliable(signedAccountSet)
|
||||
@@ -60,7 +60,7 @@ describe('reliable submission', function () {
|
||||
Account: this.wallet.getClassicAddress(),
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
const signedAccountSet = this.wallet.signTransaction(
|
||||
const { tx_blob: signedAccountSet } = this.wallet.sign(
|
||||
await this.client.autofill(accountSet),
|
||||
)
|
||||
const responsePromise = this.client.submitSignedReliable(signedAccountSet)
|
||||
|
||||
@@ -34,15 +34,19 @@ describe('submit', function () {
|
||||
}
|
||||
|
||||
const autofilledTx = await this.client.autofill(accountSet)
|
||||
const signedTx = this.wallet.signTransaction(autofilledTx)
|
||||
const signedTx = this.wallet.sign(autofilledTx)
|
||||
const submitRequest: SubmitRequest = {
|
||||
command: 'submit',
|
||||
tx_blob: signedTx,
|
||||
tx_blob: signedTx.tx_blob,
|
||||
}
|
||||
const submitResponse = await this.client.request(submitRequest)
|
||||
|
||||
await ledgerAccept(this.client)
|
||||
await verifySubmittedTransaction(this.client, signedTx)
|
||||
await verifySubmittedTransaction(
|
||||
this.client,
|
||||
signedTx.tx_blob,
|
||||
signedTx.hash,
|
||||
)
|
||||
|
||||
const expectedResponse: SubmitResponse = {
|
||||
id: submitResponse.id,
|
||||
@@ -52,10 +56,10 @@ describe('submit', function () {
|
||||
engine_result_code: 0,
|
||||
engine_result_message:
|
||||
'The transaction was applied. Only final in a validated ledger.',
|
||||
tx_blob: signedTx,
|
||||
tx_blob: signedTx.tx_blob,
|
||||
tx_json: {
|
||||
...(decode(signedTx) as unknown as Transaction),
|
||||
hash: hashSignedTx(signedTx),
|
||||
...(decode(signedTx.tx_blob) as unknown as Transaction),
|
||||
hash: hashSignedTx(signedTx.tx_blob),
|
||||
},
|
||||
accepted: true,
|
||||
account_sequence_available:
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
hashes,
|
||||
} from 'xrpl-local'
|
||||
import { convertStringToHex } from 'xrpl-local/utils'
|
||||
import { multisign, sign } from 'xrpl-local/wallet/signer'
|
||||
import { multisign } from 'xrpl-local/wallet/signer'
|
||||
|
||||
import serverUrl from '../serverUrl'
|
||||
import { setupClient, suiteClientSetup, teardownClient } from '../setup'
|
||||
@@ -68,9 +68,9 @@ describe('submit_multisigned', function () {
|
||||
Domain: convertStringToHex('example.com'),
|
||||
}
|
||||
const accountSetTx = await client.autofill(accountSet, 2)
|
||||
const signed1 = sign(signerWallet1, accountSetTx, true)
|
||||
const signed2 = sign(signerWallet2, accountSetTx, true)
|
||||
const multisigned = multisign([signed1, signed2])
|
||||
const signed1 = signerWallet1.sign(accountSetTx, true)
|
||||
const signed2 = signerWallet2.sign(accountSetTx, true)
|
||||
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
|
||||
const multisignedRequest: SubmitMultisignedRequest = {
|
||||
command: 'submit_multisigned',
|
||||
tx_json: decode(multisigned) as unknown as Transaction,
|
||||
|
||||
@@ -44,8 +44,9 @@ export async function generateFundedWallet(client: Client): Promise<Wallet> {
|
||||
export async function verifySubmittedTransaction(
|
||||
client: Client,
|
||||
tx: Transaction | string,
|
||||
hashTx?: string,
|
||||
): Promise<void> {
|
||||
const hash = hashSignedTx(tx)
|
||||
const hash = hashTx ?? hashSignedTx(tx)
|
||||
const data = await client.request({
|
||||
command: 'tx',
|
||||
transaction: hash,
|
||||
|
||||
@@ -200,10 +200,11 @@ describe('Wallet', function () {
|
||||
})
|
||||
|
||||
it('signTransaction successfully', async function () {
|
||||
const result = wallet.signTransaction(
|
||||
REQUEST_FIXTURES.normal as Transaction,
|
||||
)
|
||||
assert.deepEqual(result, RESPONSE_FIXTURES.normal.signedTransaction)
|
||||
const result = wallet.sign(REQUEST_FIXTURES.normal as Transaction)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob: RESPONSE_FIXTURES.normal.signedTransaction,
|
||||
hash: '93F6C6CE73C092AA005103223F3A1F557F4C097A2943D96760F6490F04379917',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with lowercase hex data in memo (hex should be case insensitive)', async function () {
|
||||
@@ -230,62 +231,73 @@ describe('Wallet', function () {
|
||||
],
|
||||
}
|
||||
|
||||
const result = Wallet.fromSeed(secret).signTransaction(lowercaseMemoTx)
|
||||
assert.equal(
|
||||
result,
|
||||
'120000228000000023000022B8240000000C2E0000270F201B00D5A36761400000000098968068400000000000000C73210305E09ED602D40AB1AF65646A4007C2DAC17CB6CDACDE301E74FB2D728EA057CF744730450221009C00E8439E017CA622A5A1EE7643E26B4DE9C808DE2ABE45D33479D49A4CEC66022062175BE8733442FA2A4D9A35F85A57D58252AE7B19A66401FE238B36FA28E5A081146C1856D0E36019EA75C56D7E8CBA6E35F9B3F71583147FB49CD110A1C46838788CD12764E3B0F837E0DDF9EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1F1',
|
||||
)
|
||||
const result = Wallet.fromSeed(secret).sign(lowercaseMemoTx)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob:
|
||||
'120000228000000023000022B8240000000C2E0000270F201B00D5A36761400000000098968068400000000000000C73210305E09ED602D40AB1AF65646A4007C2DAC17CB6CDACDE301E74FB2D728EA057CF744730450221009C00E8439E017CA622A5A1EE7643E26B4DE9C808DE2ABE45D33479D49A4CEC66022062175BE8733442FA2A4D9A35F85A57D58252AE7B19A66401FE238B36FA28E5A081146C1856D0E36019EA75C56D7E8CBA6E35F9B3F71583147FB49CD110A1C46838788CD12764E3B0F837E0DDF9EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1F1',
|
||||
hash: '41B9CB78D8E18A796CDD4B0BC6FB0EA19F64C4F25FDE23049197852CAB71D10D',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with EscrowFinish', async function () {
|
||||
const result = wallet.signTransaction(
|
||||
REQUEST_FIXTURES.escrow as Transaction,
|
||||
)
|
||||
assert.deepEqual(result, RESPONSE_FIXTURES.escrow.signedTransaction)
|
||||
const result = wallet.sign(REQUEST_FIXTURES.escrow as Transaction)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob: RESPONSE_FIXTURES.escrow.signedTransaction,
|
||||
hash: '645B7676DF057E4F5E83F970A18B3751B6813807F1030A8D2F482D02DC885106',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with multisignAddress', async function () {
|
||||
const signature = wallet.signTransaction(
|
||||
const signature = wallet.sign(
|
||||
REQUEST_FIXTURES.signAs as Transaction,
|
||||
wallet.getClassicAddress(),
|
||||
true,
|
||||
)
|
||||
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs.signedTransaction)
|
||||
assert.deepEqual(signature, {
|
||||
tx_blob: RESPONSE_FIXTURES.signAs.signedTransaction,
|
||||
hash: 'D8CF5FC93CFE5E131A34599AFB7CE186A5B8D1B9F069E35F4634AD3B27837E35',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with X Address and no given tag for multisignAddress', async function () {
|
||||
const signature = wallet.signTransaction(
|
||||
const signature = wallet.sign(
|
||||
REQUEST_FIXTURES.signAs as Transaction,
|
||||
wallet.getXAddress(),
|
||||
)
|
||||
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs.signedTransaction)
|
||||
assert.deepEqual(signature, {
|
||||
tx_blob: RESPONSE_FIXTURES.signAs.signedTransaction,
|
||||
hash: 'D8CF5FC93CFE5E131A34599AFB7CE186A5B8D1B9F069E35F4634AD3B27837E35',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with X Address and tag for multisignAddress', async function () {
|
||||
const signature = wallet.signTransaction(
|
||||
const signature = wallet.sign(
|
||||
REQUEST_FIXTURES.signAs as Transaction,
|
||||
wallet.getXAddress(0),
|
||||
)
|
||||
// Adding a tag changes the classicAddress, which changes the signature from RESPONSE_FIXTURES.signAs
|
||||
const expectedSignature =
|
||||
'120000240000000261400000003B9ACA00684000000000000032730081142E244E6F20104E57C0C60BD823CB312BF10928C78314B5F762798A53D543A014CAF8B297CFF8F2F937E8F3E0102300000000732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100B3F8205578C6A68D3BBD27650F5D2E983718D502C250C5147F07B7EDD8E8583E02207B892818BD58E328C2797F15694A505937861586D527849065B582523E390B128114B3263BD0A9BF9DFDBBBBD07F536355FF477BF0E9E1F1'
|
||||
const expectedSignature = {
|
||||
tx_blob:
|
||||
'120000240000000261400000003B9ACA00684000000000000032730081142E244E6F20104E57C0C60BD823CB312BF10928C78314B5F762798A53D543A014CAF8B297CFF8F2F937E8F3E0102300000000732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100B3F8205578C6A68D3BBD27650F5D2E983718D502C250C5147F07B7EDD8E8583E02207B892818BD58E328C2797F15694A505937861586D527849065B582523E390B128114B3263BD0A9BF9DFDBBBBD07F536355FF477BF0E9E1F1',
|
||||
hash: 'D4E6BC7FEA5B9624A09750F8931BE269554BE8C735A83EBD012188B87D4757C8',
|
||||
}
|
||||
|
||||
assert.deepEqual(signature, expectedSignature)
|
||||
})
|
||||
|
||||
it('signTransaction throws when given a transaction that is already signed', async function () {
|
||||
const result = wallet.signTransaction(
|
||||
REQUEST_FIXTURES.normal as Transaction,
|
||||
)
|
||||
const result = wallet.sign(REQUEST_FIXTURES.normal as Transaction)
|
||||
assert.throws(() => {
|
||||
const tx = decode(result) as unknown as Transaction
|
||||
wallet.signTransaction(tx)
|
||||
const tx = decode(result.tx_blob) as unknown as Transaction
|
||||
wallet.sign(tx)
|
||||
}, /txJSON must not contain "TxnSignature" or "Signers" properties/u)
|
||||
})
|
||||
|
||||
it('signTransaction with an EscrowExecution transaction', async function () {
|
||||
const result = wallet.signTransaction(
|
||||
REQUEST_FIXTURES.escrow as Transaction,
|
||||
)
|
||||
assert.deepEqual(result, RESPONSE_FIXTURES.escrow.signedTransaction)
|
||||
const result = wallet.sign(REQUEST_FIXTURES.escrow as Transaction)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob: RESPONSE_FIXTURES.escrow.signedTransaction,
|
||||
hash: '645B7676DF057E4F5E83F970A18B3751B6813807F1030A8D2F482D02DC885106',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction succeeds when given a transaction with no flags', async function () {
|
||||
@@ -297,15 +309,19 @@ describe('Wallet', function () {
|
||||
Sequence: 1,
|
||||
Fee: '12',
|
||||
}
|
||||
const result = wallet.signTransaction(tx)
|
||||
const expectedResult =
|
||||
'1200002400000001614000000001312D0068400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F5447446304402201C0A74EE8ECF5ED83734D7171FB65C01D90D67040DEDCC66414BD546CE302B5802205356843841BFFF60D15F5F5F9FB0AB9D66591778140AB2D137FF576D9DEC44BC8114EE3046A5DDF8422C40DDB93F1D522BB4FE6419158314FDB08D07AAA0EB711793A3027304D688E10C3648'
|
||||
const decoded = decode(result)
|
||||
const result = wallet.sign(tx)
|
||||
const expectedResult = {
|
||||
tx_blob:
|
||||
'1200002400000001614000000001312D0068400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F5447446304402201C0A74EE8ECF5ED83734D7171FB65C01D90D67040DEDCC66414BD546CE302B5802205356843841BFFF60D15F5F5F9FB0AB9D66591778140AB2D137FF576D9DEC44BC8114EE3046A5DDF8422C40DDB93F1D522BB4FE6419158314FDB08D07AAA0EB711793A3027304D688E10C3648',
|
||||
hash: 'E22186AE9FE477821BF361358174C2B0AC2D3289AA6F7E8C1102B3D270C41204',
|
||||
}
|
||||
|
||||
const decoded = decode(result.tx_blob)
|
||||
assert(
|
||||
decoded.Flags == null,
|
||||
`Flags = ${JSON.stringify(decoded.Flags)}, should be undefined`,
|
||||
)
|
||||
assert.equal(result, expectedResult)
|
||||
assert.deepEqual(result, expectedResult)
|
||||
})
|
||||
|
||||
it('signTransaction succeeds with source.amount/destination.minAmount', async function () {
|
||||
@@ -337,10 +353,13 @@ describe('Wallet', function () {
|
||||
Fee: '12',
|
||||
}
|
||||
|
||||
const result = wallet.signTransaction(tx)
|
||||
const expectedResult =
|
||||
'12000022800200002400000017201B0086955361EC6386F26FC0FFFF0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A68400000000000000C69D4438D7EA4C6800000000000000000000000000047425000000000000C155FFE99C8C91F67083CEFFDB69EBFE76348CA6AD4446F8C5D8A5E0B0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544744630440220297E0C7670C7DA491E0D649E62C123D988BA93FD7EA1B9141F1D376CDDF902F502205AF1936B22B18BBA7793A88ABEEABADB4CE0E4C3BE583066480F2F476B5ED08E81145E7B112523F68D2F5E879DB4EAC51C6698A6930483149F500E50C2F016CA01945E5A1E5846B61EF2D376'
|
||||
const decoded = decode(result)
|
||||
const result = wallet.sign(tx)
|
||||
const expectedResult = {
|
||||
tx_blob:
|
||||
'12000022800200002400000017201B0086955361EC6386F26FC0FFFF0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A68400000000000000C69D4438D7EA4C6800000000000000000000000000047425000000000000C155FFE99C8C91F67083CEFFDB69EBFE76348CA6AD4446F8C5D8A5E0B0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544744630440220297E0C7670C7DA491E0D649E62C123D988BA93FD7EA1B9141F1D376CDDF902F502205AF1936B22B18BBA7793A88ABEEABADB4CE0E4C3BE583066480F2F476B5ED08E81145E7B112523F68D2F5E879DB4EAC51C6698A6930483149F500E50C2F016CA01945E5A1E5846B61EF2D376',
|
||||
hash: 'FB2813E9E673EF56609070A4BA9640FAD0508DA567320AE9D92FB5A356A03D84',
|
||||
}
|
||||
const decoded = decode(result.tx_blob)
|
||||
assert(
|
||||
decoded.Flags === 2147614720,
|
||||
`Flags = ${JSON.stringify(decoded.Flags)}, should be 2147614720`,
|
||||
@@ -362,7 +381,7 @@ describe('Wallet', function () {
|
||||
}
|
||||
|
||||
assert.throws(() => {
|
||||
wallet.signTransaction(tx)
|
||||
wallet.sign(tx)
|
||||
}, /1\.2 is an illegal amount/u)
|
||||
})
|
||||
|
||||
@@ -380,15 +399,16 @@ describe('Wallet', function () {
|
||||
}
|
||||
|
||||
assert.throws(() => {
|
||||
wallet.signTransaction(tx)
|
||||
wallet.sign(tx)
|
||||
}, /1123456\.7 is an illegal amount/u)
|
||||
})
|
||||
|
||||
it('signTransaction with a ticket transaction', async function () {
|
||||
const result = wallet.signTransaction(
|
||||
REQUEST_FIXTURES.ticket as Transaction,
|
||||
)
|
||||
assert.deepEqual(result, RESPONSE_FIXTURES.ticket.signedTransaction)
|
||||
const result = wallet.sign(REQUEST_FIXTURES.ticket as Transaction)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob: RESPONSE_FIXTURES.ticket.signedTransaction,
|
||||
hash: '0AC60B1E1F063904D9D9D0E9D03F2E9C8D41BC6FC872D5B8BF87E15BBF9669BB',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with a Payment transaction with paths', async function () {
|
||||
@@ -416,11 +436,12 @@ describe('Wallet', function () {
|
||||
Sequence: 1,
|
||||
Fee: '12',
|
||||
}
|
||||
const result = wallet.signTransaction(payment)
|
||||
assert.deepEqual(
|
||||
result,
|
||||
'12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474463044022049AD75980A5088EBCD768547E06427736BD8C4396B9BD3762CA8C1341BD7A4F9022060C94071C3BDF99FAB4BEB7C0578D6EBEE083157B470699645CCE4738A41D61081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900',
|
||||
)
|
||||
const result = wallet.sign(payment)
|
||||
assert.deepEqual(result, {
|
||||
tx_blob:
|
||||
'12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474463044022049AD75980A5088EBCD768547E06427736BD8C4396B9BD3762CA8C1341BD7A4F9022060C94071C3BDF99FAB4BEB7C0578D6EBEE083157B470699645CCE4738A41D61081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900',
|
||||
hash: '71D0B4AA13277B32E2C2E751566BB0106764881B0CAA049905A0EDAC73257745',
|
||||
})
|
||||
})
|
||||
|
||||
it('signTransaction with a prepared payment', async function () {
|
||||
@@ -434,9 +455,13 @@ describe('Wallet', function () {
|
||||
LastLedgerSequence: 8819954,
|
||||
Fee: '12',
|
||||
}
|
||||
const result = wallet.signTransaction(payment)
|
||||
const expectedResult =
|
||||
'12000022800000002400000017201B008694F261400000000000000168400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100E8929B68B137AB2AAB1AD3A4BB253883B0C8C318DC8BB39579375751B8E54AC502206893B2D61244AFE777DAC9FA3D9DDAC7780A9810AF4B322D629784FD626B8CE481145E7B112523F68D2F5E879DB4EAC51C6698A693048314FDB08D07AAA0EB711793A3027304D688E10C3648'
|
||||
const result = wallet.sign(payment)
|
||||
const expectedResult = {
|
||||
tx_blob:
|
||||
'12000022800000002400000017201B008694F261400000000000000168400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100E8929B68B137AB2AAB1AD3A4BB253883B0C8C318DC8BB39579375751B8E54AC502206893B2D61244AFE777DAC9FA3D9DDAC7780A9810AF4B322D629784FD626B8CE481145E7B112523F68D2F5E879DB4EAC51C6698A693048314FDB08D07AAA0EB711793A3027304D688E10C3648',
|
||||
hash: 'AA1D2BDC59E504AA6C2416E864C615FB18042C1AB4457BEB883F7194D8C452B5',
|
||||
}
|
||||
|
||||
assert.deepEqual(result, expectedResult)
|
||||
})
|
||||
|
||||
@@ -452,7 +477,7 @@ describe('Wallet', function () {
|
||||
Fee: '12',
|
||||
}
|
||||
assert.throws(() => {
|
||||
wallet.signTransaction(payment)
|
||||
wallet.sign(payment)
|
||||
}, /^1.1234567 is an illegal amount/u)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,12 +5,13 @@ import { JsonObject } from 'ripple-binary-codec/dist/types/serialized-type'
|
||||
import { Transaction, ValidationError } from 'xrpl-local'
|
||||
import Wallet from 'xrpl-local/wallet'
|
||||
import {
|
||||
sign,
|
||||
authorizeChannel,
|
||||
multisign,
|
||||
verifySignature,
|
||||
} from 'xrpl-local/wallet/signer'
|
||||
|
||||
import { SignedTxBlobHash } from '../../src/wallet'
|
||||
|
||||
const publicKey =
|
||||
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
|
||||
const privateKey =
|
||||
@@ -70,91 +71,98 @@ const multisignTx1: Transaction = {
|
||||
SigningPubKey: '',
|
||||
}
|
||||
|
||||
const multisignTxToCombine1: Transaction = {
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
SigningPubKey:
|
||||
'02B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF',
|
||||
TxnSignature:
|
||||
'30450221009C195DBBF7967E223D8626CA19CF02073667F2B22E206727BFE848FF42BEAC8A022048C323B0BED19A988BDBEFA974B6DE8AA9DCAE250AA82BBD1221787032A864E5',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
}
|
||||
|
||||
const multisignTxToCombine2: Transaction = {
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rJvuSQhQR37czfxRou4vNWaM97uEhT4ShE',
|
||||
SigningPubKey:
|
||||
'02B78EEA571B2633180834CC6E7B4ED84FBF6811D12ECB59410E0C92D13B7726F5',
|
||||
TxnSignature:
|
||||
'304502210098009CEFA61EE9843BB7FC29B78CFFAACF28352A4A7CF3AAE79EF12D79BA50910220684F116266E5E4519A7A33F7421631EB8494082BE51A8B03FECCB3E59F77154A',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
}
|
||||
|
||||
const expectedMultisign: string = encode({
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
SigningPubKey:
|
||||
'02B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF',
|
||||
TxnSignature:
|
||||
'30450221009C195DBBF7967E223D8626CA19CF02073667F2B22E206727BFE848FF42BEAC8A022048C323B0BED19A988BDBEFA974B6DE8AA9DCAE250AA82BBD1221787032A864E5',
|
||||
},
|
||||
},
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rJvuSQhQR37czfxRou4vNWaM97uEhT4ShE',
|
||||
SigningPubKey:
|
||||
'02B78EEA571B2633180834CC6E7B4ED84FBF6811D12ECB59410E0C92D13B7726F5',
|
||||
TxnSignature:
|
||||
'304502210098009CEFA61EE9843BB7FC29B78CFFAACF28352A4A7CF3AAE79EF12D79BA50910220684F116266E5E4519A7A33F7421631EB8494082BE51A8B03FECCB3E59F77154A',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
})
|
||||
|
||||
describe('Signer', function () {
|
||||
let multisignTxToCombine1
|
||||
let multisignTxToCombine2
|
||||
let multisignJSON
|
||||
let expectedMultisign
|
||||
|
||||
beforeEach(function () {
|
||||
multisignTxToCombine1 = {
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
SigningPubKey:
|
||||
'02B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF',
|
||||
TxnSignature:
|
||||
'30450221009C195DBBF7967E223D8626CA19CF02073667F2B22E206727BFE848FF42BEAC8A022048C323B0BED19A988BDBEFA974B6DE8AA9DCAE250AA82BBD1221787032A864E5',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
} as Transaction
|
||||
|
||||
multisignTxToCombine2 = {
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rJvuSQhQR37czfxRou4vNWaM97uEhT4ShE',
|
||||
SigningPubKey:
|
||||
'02B78EEA571B2633180834CC6E7B4ED84FBF6811D12ECB59410E0C92D13B7726F5',
|
||||
TxnSignature:
|
||||
'304502210098009CEFA61EE9843BB7FC29B78CFFAACF28352A4A7CF3AAE79EF12D79BA50910220684F116266E5E4519A7A33F7421631EB8494082BE51A8B03FECCB3E59F77154A',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
} as Transaction
|
||||
|
||||
multisignJSON = {
|
||||
Account: 'rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC',
|
||||
Fee: '30000',
|
||||
Flags: 262144,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
value: '100',
|
||||
},
|
||||
Sequence: 2,
|
||||
Signers: [
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
SigningPubKey:
|
||||
'02B3EC4E5DD96029A647CFA20DA07FE1F85296505552CCAC114087E66B46BD77DF',
|
||||
TxnSignature:
|
||||
'30450221009C195DBBF7967E223D8626CA19CF02073667F2B22E206727BFE848FF42BEAC8A022048C323B0BED19A988BDBEFA974B6DE8AA9DCAE250AA82BBD1221787032A864E5',
|
||||
},
|
||||
},
|
||||
{
|
||||
Signer: {
|
||||
Account: 'rJvuSQhQR37czfxRou4vNWaM97uEhT4ShE',
|
||||
SigningPubKey:
|
||||
'02B78EEA571B2633180834CC6E7B4ED84FBF6811D12ECB59410E0C92D13B7726F5',
|
||||
TxnSignature:
|
||||
'304502210098009CEFA61EE9843BB7FC29B78CFFAACF28352A4A7CF3AAE79EF12D79BA50910220684F116266E5E4519A7A33F7421631EB8494082BE51A8B03FECCB3E59F77154A',
|
||||
},
|
||||
},
|
||||
],
|
||||
SigningPubKey: '',
|
||||
TransactionType: 'TrustSet',
|
||||
}
|
||||
expectedMultisign = encode(multisignJSON)
|
||||
})
|
||||
it('sign', function () {
|
||||
// Test case data generated using this tutorial - https://xrpl.org/send-xrp.html#send-xrp
|
||||
const tx3: Transaction = {
|
||||
@@ -167,19 +175,21 @@ describe('Signer', function () {
|
||||
Fee: '12',
|
||||
Sequence: 20582260,
|
||||
}
|
||||
const signedTxBlob =
|
||||
'120000228000000024013A0F74201B013A0FC36140000000014FB18068400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544744730450221009ECB5324717E14DD6970126271F05BC2626D2A8FA9F3797555D417F8257C1E6002206BDD74A0F30425F2BA9DB69C90F21B3E27735C190FB4F3A640F066ACBBF06AD98114B3263BD0A9BF9DFDBBBBD07F536355FF477BF0E98314F667B0CA50CC7709A220B0561B85E53A48461FA8'
|
||||
const signedTxResponse = {
|
||||
tx_blob:
|
||||
'120000228000000024013A0F74201B013A0FC36140000000014FB18068400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544744730450221009ECB5324717E14DD6970126271F05BC2626D2A8FA9F3797555D417F8257C1E6002206BDD74A0F30425F2BA9DB69C90F21B3E27735C190FB4F3A640F066ACBBF06AD98114B3263BD0A9BF9DFDBBBBD07F536355FF477BF0E98314F667B0CA50CC7709A220B0561B85E53A48461FA8',
|
||||
hash: 'F73E975C70497A3DA61ADB76A3B39CD971A2DE017419A690BFAD6733B5FD8B3B',
|
||||
}
|
||||
|
||||
const signedTx: string = sign(wallet, tx3)
|
||||
|
||||
assert.equal(signedTx, signedTxBlob)
|
||||
const signedTx: SignedTxBlobHash = wallet.sign(tx3)
|
||||
assert.deepEqual(signedTx, signedTxResponse)
|
||||
})
|
||||
|
||||
it('sign in multisign format', function () {
|
||||
const multisignWallet = Wallet.fromSeed(unsignedSecret1)
|
||||
|
||||
assert.deepEqual(
|
||||
decode(sign(multisignWallet, unsignedTx1, true)),
|
||||
decode(multisignWallet.sign(unsignedTx1, true).tx_blob),
|
||||
multisignTx1 as unknown as JsonObject,
|
||||
)
|
||||
})
|
||||
@@ -190,6 +200,17 @@ describe('Signer', function () {
|
||||
assert.deepEqual(multisign(transactions), expectedMultisign)
|
||||
})
|
||||
|
||||
it('multisign runs successfully with X-address', function () {
|
||||
multisignTxToCombine1.Account =
|
||||
'XVJfK5FpouB7gtk3kaZHqbgV4Bswir4ccz3rsJw9oMf71tc'
|
||||
multisignTxToCombine2.Account =
|
||||
'XVJfK5FpouB7gtk3kaZHqbgV4Bswir4ccz3rsJw9oMf71tc'
|
||||
|
||||
const transactions = [multisignTxToCombine1, multisignTxToCombine2]
|
||||
|
||||
assert.deepEqual(multisign(transactions), expectedMultisign)
|
||||
})
|
||||
|
||||
it('multisign runs successfully with tx_blobs', function () {
|
||||
const transactions = [multisignTxToCombine1, multisignTxToCombine2]
|
||||
|
||||
@@ -263,21 +284,23 @@ describe('Signer', function () {
|
||||
})
|
||||
|
||||
it('verifySignature succeeds for valid signed transaction blob', function () {
|
||||
const signedTx = sign(verifyWallet, tx)
|
||||
const signedTx = verifyWallet.sign(tx)
|
||||
|
||||
assert.isTrue(verifySignature(signedTx))
|
||||
assert.isTrue(verifySignature(signedTx.tx_blob))
|
||||
})
|
||||
|
||||
it('verify succeeds for valid signed transaction object', function () {
|
||||
const signedTx = sign(verifyWallet, tx)
|
||||
const signedTx = verifyWallet.sign(tx)
|
||||
|
||||
assert.isTrue(verifySignature(decode(signedTx) as unknown as Transaction))
|
||||
assert.isTrue(
|
||||
verifySignature(decode(signedTx.tx_blob) as unknown as Transaction),
|
||||
)
|
||||
})
|
||||
|
||||
it('verify throws for invalid signing key', function () {
|
||||
const signedTx = sign(verifyWallet, tx)
|
||||
const signedTx = verifyWallet.sign(tx)
|
||||
|
||||
const decodedTx = decode(signedTx) as unknown as Transaction
|
||||
const decodedTx = decode(signedTx.tx_blob) as unknown as Transaction
|
||||
|
||||
// Use a different key for validation
|
||||
decodedTx.SigningPubKey =
|
||||
|
||||
Reference in New Issue
Block a user