Remove sign.ts and move functionality into Wallet (#1620)

* Move sign.ts functionality into Wallet
* Move the corresponding tests to wallet's test cases and simplify them for readability
* Delete sign.ts
This commit is contained in:
Jackson Mills
2021-09-17 12:16:57 -07:00
committed by Mayukha Vadari
parent 05e1d4d3c5
commit 6fac420d9f
14 changed files with 443 additions and 685 deletions

View File

@@ -1,376 +0,0 @@
import { assert, expect } from 'chai'
import binary from 'ripple-binary-codec'
import { ValidationError } from '../../src/common/errors'
import requests from '../fixtures/requests'
import responses from '../fixtures/responses'
import { setupClient, teardownClient } from '../setupClient'
const { sign: REQUEST_FIXTURES } = requests
const { sign: RESPONSE_FIXTURES } = responses
describe('client.sign', function () {
beforeEach(setupClient)
afterEach(teardownClient)
it('sign', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = this.client.sign(REQUEST_FIXTURES.normal.txJSON, secret)
assert.deepEqual(result, RESPONSE_FIXTURES.normal)
})
it('sign with lowercase hex data in memo (hex should be case insensitive)', async function () {
const secret = 'shd2nxpFD6iBRKWsRss2P4tKMWyy9'
const lowercaseMemoTxJson = {
TransactionType: 'Payment',
Flags: 2147483648,
Account: 'rwiZ3q3D3QuG4Ga2HyGdq3kPKJRGctVG8a',
Amount: '10000000',
LastLedgerSequence: 14000999,
Destination: 'rUeEBYXHo8vF86Rqir3zWGRQ84W9efdAQd',
Fee: '12',
Sequence: 12,
SourceTag: 8888,
DestinationTag: 9999,
Memos: [
{
Memo: {
MemoType:
'687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963',
MemoData: '72656e74',
},
},
],
}
const txParams = JSON.stringify(lowercaseMemoTxJson)
const result = this.client.sign(txParams, secret)
assert.deepEqual(result, {
signedTransaction:
'120000228000000023000022B8240000000C2E0000270F201B00D5A36761400000000098968068400000000000000C73210305E09ED602D40AB1AF65646A4007C2DAC17CB6CDACDE301E74FB2D728EA057CF744730450221009C00E8439E017CA622A5A1EE7643E26B4DE9C808DE2ABE45D33479D49A4CEC66022062175BE8733442FA2A4D9A35F85A57D58252AE7B19A66401FE238B36FA28E5A081146C1856D0E36019EA75C56D7E8CBA6E35F9B3F71583147FB49CD110A1C46838788CD12764E3B0F837E0DDF9EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1F1',
id: '41B9CB78D8E18A796CDD4B0BC6FB0EA19F64C4F25FDE23049197852CAB71D10D',
})
})
it('EscrowExecution', async function () {
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
const result = this.client.sign(REQUEST_FIXTURES.escrow.txJSON, secret)
assert.deepEqual(result, RESPONSE_FIXTURES.escrow)
})
it('signAs', async function () {
const txJSON = REQUEST_FIXTURES.signAs
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
const signature = this.client.sign(JSON.stringify(txJSON), secret, {
signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
})
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs)
})
it('withKeypair', async function () {
const keypair = {
privateKey:
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
publicKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8',
}
const result = this.client.sign(REQUEST_FIXTURES.normal.txJSON, keypair)
assert.deepEqual(result, RESPONSE_FIXTURES.normal)
})
it('withKeypair already signed', async function () {
const keypair = {
privateKey:
'00ACCD3309DB14D1A4FC9B1DAE608031F4408C85C73EE05E035B7DC8B25840107A',
publicKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8',
}
const result = this.client.sign(REQUEST_FIXTURES.normal.txJSON, keypair)
assert.throws(() => {
const tx = JSON.stringify(binary.decode(result.signedTransaction))
this.client.sign(tx, keypair)
}, /txJSON must not contain "TxnSignature" or "Signers" properties/u)
})
it('withKeypair EscrowExecution', async function () {
const keypair = {
privateKey:
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
publicKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
}
const result = this.client.sign(REQUEST_FIXTURES.escrow.txJSON, keypair)
assert.deepEqual(result, RESPONSE_FIXTURES.escrow)
})
it('withKeypair signAs', async function () {
const txJSON = REQUEST_FIXTURES.signAs
const keypair = {
privateKey:
'001ACAAEDECE405B2A958212629E16F2EB46B153EEE94CDD350FDEFF52795525B7',
publicKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
}
const signature = this.client.sign(JSON.stringify(txJSON), keypair, {
signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
})
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs)
})
it('already signed', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = this.client.sign(REQUEST_FIXTURES.normal.txJSON, secret)
assert.throws(() => {
const tx = JSON.stringify(binary.decode(result.signedTransaction))
this.client.sign(tx, secret)
}, /txJSON must not contain "TxnSignature" or "Signers" properties/u)
})
it('succeeds - no flags', async function () {
const txJSON =
'{"TransactionType":"Payment","Account":"r45Rev1EXGxy2hAUmJPCne97KUE7qyrD3j","Destination":"rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r","Amount":"20000000","Sequence":1,"Fee":"12"}'
const secret = 'shotKgaEotpcYsshSE39vmSnBDRim'
const result = this.client.sign(txJSON, secret)
const expectedResult = {
signedTransaction:
'1200002400000001614000000001312D0068400000000000000C7321022B05847086686F9D0499B13136B94AD4323EE1B67D4C429ECC987AB35ACFA34574473045022100C104B7B97C31FACA4597E7D6FCF13BD85BD11375963A62A0AC45B0061236E39802207784F157F6A98DFC85B051CDDF61CC3084C4F5750B82674801C8E9950280D1998114EE3046A5DDF8422C40DDB93F1D522BB4FE6419158314FDB08D07AAA0EB711793A3027304D688E10C3648',
id: '0596925967F541BF332FF6756645B2576A9858414B5B363DC3D34915BE8A70D6',
}
const decoded = binary.decode(result.signedTransaction)
assert(
decoded.Flags == null,
`Flags = ${decoded.Flags as number}, should be undefined`,
)
assert.deepEqual(result, expectedResult)
})
it('sign succeeds with source.amount/destination.minAmount', async function () {
// See also: 'preparePayment with source.amount/destination.minAmount'
const txJSON =
'{"TransactionType":"Payment","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Destination":"rEX4LtGJubaUcMWCJULcy4NVxGT9ZEMVRq","Amount":{"currency":"USD","issuer":"rMaa8VLBTjwTJWA2kSme4Sqgphhr6Lr6FH","value":"999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000"},"Flags":2147614720,"SendMax":{"currency":"GBP","issuer":"rpat5TmYjDsnFSStmgTumFgXCM9eqsWPro","value":"0.1"},"DeliverMin":{"currency":"USD","issuer":"rMaa8VLBTjwTJWA2kSme4Sqgphhr6Lr6FH","value":"0.1248548562296331"},"Sequence":23,"LastLedgerSequence":8820051,"Fee":"12"}'
const secret = 'shotKgaEotpcYsshSE39vmSnBDRim'
const result = this.client.sign(txJSON, secret)
const expectedResult = {
signedTransaction:
'12000022800200002400000017201B0086955361EC6386F26FC0FFFF0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A68400000000000000C69D4438D7EA4C6800000000000000000000000000047425000000000000C155FFE99C8C91F67083CEFFDB69EBFE76348CA6AD4446F8C5D8A5E0B0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A7321022B05847086686F9D0499B13136B94AD4323EE1B67D4C429ECC987AB35ACFA34574473045022100D9634523D8E232D4A7807A71856023D82AC928FA29848571B820867898413B5F022041AC00EC1F81A26A6504EBF844A38CC3204694EF2CC1A97A87632721631F93DA81145E7B112523F68D2F5E879DB4EAC51C6698A6930483149F500E50C2F016CA01945E5A1E5846B61EF2D376',
id: '1C558AA9B926C24FB6BBD6950B2DB1350A83F9F12E4385208867907019761A2D',
}
const decoded = binary.decode(result.signedTransaction)
assert(
decoded.Flags === 2147614720,
`Flags = ${decoded.Flags as number}, should be 2147614720`,
)
assert.deepEqual(result, expectedResult)
})
it('throws when encoded tx does not match decoded tx - AccountSet', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"6578616D706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1.2","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '0.0000012',
sequence: 23,
maxLedgerVersion: 8820051,
},
}
assert.throws(() => {
this.client.sign(request.txJSON, secret)
}, /1\.2 is an illegal amount/u)
})
it('throws when encoded tx does not match decoded tx - higher fee', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"6578616D706C652E636F6D","LastLedgerSequence":8820051,"Fee":"1123456.7","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '1.1234567',
sequence: 23,
maxLedgerVersion: 8820051,
},
}
assert.throws(() => {
this.client.sign(request.txJSON, secret)
}, /1123456\.7 is an illegal amount/u)
})
it('permits fee exceeding 2000000 drops when maxFeeXRP is higher than 2 XRP', async function () {
this.client.maxFeeXRP = '2.1'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
// TODO: This fails when address is X-address
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051,
},
}
const result = this.client.sign(request.txJSON, secret)
const expectedResponse = {
signedTransaction:
'12000322800000002400000017201B008695536840000000001EAB90732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200203F219F5371D2C6506888B1B02B27E74998F7A42D412C32FE319AC1A5B8DEF02205959A1B02253ACCCE542759E9886466C56D16B04676FA492AD34AA0E877E91F381145E7B112523F68D2F5E879DB4EAC51C6698A69304',
id: '061D5593E0A117F389826419CAC049A73C7CFCA65A20B788781D41240143D864',
}
assert.deepEqual(result, expectedResponse)
})
it('sign with ticket', async function () {
const secret = 'sn7n5R1cR5Y3fRFkuWXA94Ts1frVJ'
const result = this.client.sign(REQUEST_FIXTURES.ticket.txJSON, secret)
assert.deepEqual(result, RESPONSE_FIXTURES.ticket)
})
it('throws when Fee exceeds maxFeeXRP (in drops)', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"6578616D706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051,
},
}
assert.throws(() => {
this.client.sign(request.txJSON, secret)
}, /Fee" should not exceed "2000000"\. To use a higher fee, set `maxFeeXRP` in the Client constructor\./u)
})
it('throws when Fee exceeds maxFeeXRP (in drops) - custom maxFeeXRP', async function () {
this.client.maxFeeXRP = '1.9'
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const request = {
txJSON: `{"Flags":2147483648,"TransactionType":"AccountSet","Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59","Domain":"6578616D706C652E636F6D","LastLedgerSequence":8820051,"Fee":"2010000","Sequence":23,"SigningPubKey":"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8"}`,
instructions: {
fee: '2.01',
sequence: 23,
maxLedgerVersion: 8820051,
},
}
assert.throws(() => {
this.client.sign(request.txJSON, secret)
}, /Fee" should not exceed "1900000"\. To use a higher fee, set `maxFeeXRP` in the Client constructor\./u)
})
it('sign with paths', async function () {
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const payment = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
Amount: {
currency: 'USD',
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
value:
'999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000',
},
Flags: 2147614720,
SendMax: '100',
DeliverMin: {
currency: 'USD',
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
value: '0.00004579644712312366',
},
Paths: [
[{ currency: 'USD', issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc' }],
],
LastLedgerSequence: 15696358,
Sequence: 1,
Fee: '12',
}
const result = this.client.sign(JSON.stringify(payment), secret)
assert.deepEqual(result, {
signedTransaction:
'12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200A693FB5CA6B21250EBDFD8CFF526EE0DF7C9E4E31EB0660692E75E6A93BF5F802203CC39463DDA21386898CA31E18AD1A6828647D65741DD637BAD71BC83E29DB9481145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900',
id: '78874FE5F5299FEE3EA85D3CF6C1FB1F1D46BB08F716662A3E3D1F0ADE4EF796',
})
})
it('succeeds - prepared payment', async function () {
const payment = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '1',
Flags: 2147483648,
Sequence: 23,
LastLedgerSequence: 8819954,
Fee: '12',
}
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
const result = this.client.sign(JSON.stringify(payment), secret)
const expectedResult = {
signedTransaction:
'12000022800000002400000017201B008694F261400000000000000168400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100A9C91D4CFAE45686146EE0B56D4C53A2E7C2D672FB834D43E0BE2D2E9106519A022075DDA2F92DE552B0C45D83D4E6D35889B3FBF51BFBBD9B25EBF70DE3C96D0D6681145E7B112523F68D2F5E879DB4EAC51C6698A693048314FDB08D07AAA0EB711793A3027304D688E10C3648',
id: '88D6B913C66279EA31ADC25C5806C48B2D4E5680261666790A736E1961217700',
}
assert.deepEqual(result, expectedResult)
})
it('throws when encoded tx does not match decoded tx - prepared payment', async function () {
const payment = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '1.1234567',
Flags: 2147483648,
Sequence: 23,
LastLedgerSequence: 8819954,
Fee: '12',
}
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
assert.throws(() => {
this.client.sign(JSON.stringify(payment), secret)
}, /^1.1234567 is an illegal amount/u)
})
it('throws when encoded tx does not match decoded tx - prepared order', async function () {
const offerCreate = {
TransactionType: 'OfferCreate',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
TakerGets: {
currency: 'USD',
issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B',
value: '3.140000',
},
TakerPays: '31415000000',
Flags: 2148007936,
Sequence: 123,
LastLedgerSequence: 8819954,
Fee: '12',
}
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
try {
this.client.sign(JSON.stringify(offerCreate), secret)
return await Promise.reject(
new Error('this.client.sign should have thrown'),
)
} catch (error) {
if (error instanceof ValidationError) {
assert.equal(error.name, 'ValidationError')
assert.equal(
error.message,
'Serialized transaction does not match original txJSON. See `error.data`',
)
expect(error.data).to.deep.include({
diff: {
TakerGets: {
value: '3.14',
},
},
})
} else {
assert(false)
}
}
})
})

View File

@@ -1,8 +1,10 @@
{
"txJSON": "{\"Flags\":2147483648,\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Domain\":\"6578616D706C652E636F6D\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23,\"SigningPubKey\":\"02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8\"}",
"instructions": {
"fee": "0.000012",
"sequence": 23,
"maxLedgerVersion": 8820051
}
"TransactionType":"AccountSet",
"Flags":2147483648,
"Sequence":23,
"LastLedgerSequence":8820051,
"Fee":"12",
"SigningPubKey":"02A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544",
"Domain":"6578616D706C652E636F6D",
"Account":"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
}

View File

@@ -1,8 +1,12 @@
{
"txJSON": "{\"TransactionType\":\"EscrowFinish\",\"Account\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"Owner\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"OfferSequence\":2,\"Condition\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Fulfillment\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"Flags\":2147483648,\"LastLedgerSequence\":102,\"Fee\":\"12\",\"Sequence\":1}",
"instructions": {
"fee": "0.000012",
"sequence": 1,
"maxLedgerVersion": 102
}
"TransactionType":"EscrowFinish",
"Account":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Owner":"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"OfferSequence":2,
"Condition":"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D",
"Fulfillment":"74686973206D757374206861766520333220636861726163746572732E2E2E2E",
"Flags":2147483648,
"LastLedgerSequence":102,
"Fee":"12",
"Sequence":1
}

View File

@@ -1,9 +1,8 @@
{
"txJSON": "{\"TransactionType\": \"TicketCreate\", \"TicketCount\": 1, \"Account\": \"r4SDqUD1ZcfoZrhnsZ94XNFKxYL4oHYJyA\", \"Sequence\": 0, \"TicketSequence\": 23, \"Fee\": \"10000\"}",
"instructions": {
"ticketSequence": 23,
"maxLedgerVersion": 8820051
}
"TransactionType":"TicketCreate",
"TicketCount":1,
"Account":"r4SDqUD1ZcfoZrhnsZ94XNFKxYL4oHYJyA",
"Sequence":0,
"TicketSequence":23,
"Fee":"10000"
}

View File

@@ -1,4 +1,4 @@
{
"signedTransaction": "12000322800000002400000017201B0086955368400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8744630440220680070A157682D9EB510E8AD58C35DC9C8346B155077D73792E88120B7A3B6B1022079537D3300C9B4D2D3D62ACCE1E66CDA893F9612CB2577ADEC8154B933765336770B6578616D706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
"id": "10B54D31384A49336C36A5907E3C28227139E282D3C7F734FEA351DE446F3674"
"signedTransaction": "12000322800000002400000017201B0086955368400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474463044022025464FA5466B6E28EEAD2E2D289A7A36A11EB9B269D211F9C76AB8E8320694E002205D5F99CB56E5A996E5636A0E86D029977BEFA232B7FB64ABA8F6E29DC87A9E89770B6578616D706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
"id": "93F6C6CE73C092AA005103223F3A1F557F4C097A2943D96760F6490F04379917"
}

View File

@@ -1,4 +1,4 @@
{
"signedTransaction": "120000240000000261400000003B9ACA00684000000000000032730081142E244E6F20104E57C0C60BD823CB312BF10928C78314B5F762798A53D543A014CAF8B297CFF8F2F937E8F3E01073210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074473045022100BB6FC77F26BC88587204CAA79B2230C420D7EC937B8AC3A0CF9B0BE988BAB0D002203BF86893BA3B764375FFFAD9D54A4AAEDABD07C4D72ADB9C1B20C10B4DD712898114B5F762798A53D543A014CAF8B297CFF8F2F937E8E1F1",
"id": "AB7632D7C07E591658635CED6A5DDE832B22CA066907CB131DEFAAA925B98185"
"signedTransaction": "120000240000000261400000003B9ACA00684000000000000032730081142E244E6F20104E57C0C60BD823CB312BF10928C78314B5F762798A53D543A014CAF8B297CFF8F2F937E8F3E010732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100B3F8205578C6A68D3BBD27650F5D2E983718D502C250C5147F07B7EDD8E8583E02207B892818BD58E328C2797F15694A505937861586D527849065B582523E390B128114B3263BD0A9BF9DFDBBBBD07F536355FF477BF0E9E1F1",
"id": "D8CF5FC93CFE5E131A34599AFB7CE186A5B8D1B9F069E35F4634AD3B27837E35"
}

View File

@@ -1,4 +1,4 @@
{
"signedTransaction": "12000222800000002400000001201900000002201B0000006668400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD0207446304402205CDD40611008680E151EF6C5C70A6DFBF7977DE73800E04F1B6F1FE8688BBE3E022075DB3990E6CBF0BCD990D92FA13E3D3F95510CA2CCBAB92BD2CE288FA6F2F34870102074686973206D757374206861766520333220636861726163746572732E2E2E2E701120712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D8114B5F762798A53D543A014CAF8B297CFF8F2F937E88214B5F762798A53D543A014CAF8B297CFF8F2F937E8",
"id": "E76178CD799A82BAB6EE83A70DE0060F0BDC8BDE29980F0832D791D8D9D5CACC"
"signedTransaction": "12000222800000002400000001201900000002201B0000006668400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F5447446304402204652E8572AEED964451C603EB110AC9945A65E3C5C288D144BB02F259755F6E202205B64E27293248F0650A3F7A4FD66BC16A61F4883AC3ED8EE8A48EF569C06812070102074686973206D757374206861766520333220636861726163746572732E2E2E2E701120712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D8114B5F762798A53D543A014CAF8B297CFF8F2F937E88214B5F762798A53D543A014CAF8B297CFF8F2F937E8",
"id": "645B7676DF057E4F5E83F970A18B3751B6813807F1030A8D2F482D02DC885106"
}

View File

@@ -1,4 +1,4 @@
{
"signedTransaction": "12000A2400000000202800000001202900000017684000000000002710732103E985C55BDCE4171394A0521AA84C71F81425680A7CE510AEF49662CF5A78D38674473045022100A77F102B632779C0E3F25B8715CB8FF2A15A702F3A39D1E6416C981B604D2E0302207E1DB8418D547E8AE322F49585E1C554E8759C0FBF7B88158BE3D0EE6B2E4E0A8114EB1FC04FDA0248FB6DE5BA4235425773D61DF0F3",
"id": "9F1002A8DB9D06D5F3AB2070387F17E12421DCE8444EED13E5F6928291EB4F43"
"signedTransaction": "12000A2400000000202800000001202900000017684000000000002710732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100896CFE083767C88B9539DE2F28894429BC2760865161792D576C090BB93E1EAA02203015D30BC59245C8CFB8A9D78386B91B251DCB946A4C0FAB12A5FA41C202FF3A8114EB1FC04FDA0248FB6DE5BA4235425773D61DF0F3",
"id": "0AC60B1E1F063904D9D9D0E9D03F2E9C8D41BC6FC872D5B8BF87E15BBF9669BB"
}

View File

@@ -1,9 +1,14 @@
import { assert } from 'chai'
import { Payment } from 'xrpl-local'
import { decode } from 'ripple-binary-codec/dist'
import ECDSA from '../../src/common/ecdsa'
import { Transaction } from '../../src/models/transactions'
import Wallet from '../../src/wallet'
import requests from '../fixtures/requests'
import responses from '../fixtures/responses'
const { sign: REQUEST_FIXTURES } = requests
const { sign: RESPONSE_FIXTURES } = responses
/**
* Wallet testing.
@@ -181,27 +186,267 @@ describe('Wallet', function () {
})
describe('signTransaction', function () {
const publicKey =
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
const privateKey =
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
const address = 'rhvh5SrgBL5V8oeV9EpDuVszeJSSCEkbPc'
let wallet: Wallet
it('signs a transaction offline', function () {
const txJSON: Payment = {
beforeEach(function () {
wallet = Wallet.fromSeed('ss1x3KLrSvfg7irFc1D929WXZ7z9H')
})
it('signTransaction successfully', async function () {
const result = wallet.signTransaction(
REQUEST_FIXTURES.normal as Transaction,
)
assert.deepEqual(result, RESPONSE_FIXTURES.normal.signedTransaction)
})
it('signTransaction with lowercase hex data in memo (hex should be case insensitive)', async function () {
const secret = 'shd2nxpFD6iBRKWsRss2P4tKMWyy9'
const lowercaseMemoTx: Transaction = {
TransactionType: 'Payment',
Account: address,
Flags: 2147483648,
Account: 'rwiZ3q3D3QuG4Ga2HyGdq3kPKJRGctVG8a',
Amount: '10000000',
LastLedgerSequence: 14000999,
Destination: 'rUeEBYXHo8vF86Rqir3zWGRQ84W9efdAQd',
Fee: '12',
Sequence: 12,
SourceTag: 8888,
DestinationTag: 9999,
Memos: [
{
Memo: {
MemoType:
'687474703a2f2f6578616d706c652e636f6d2f6d656d6f2f67656e65726963',
MemoData: '72656e74',
},
},
],
}
const result = Wallet.fromSeed(secret).signTransaction(lowercaseMemoTx)
assert.equal(
result,
'120000228000000023000022B8240000000C2E0000270F201B00D5A36761400000000098968068400000000000000C73210305E09ED602D40AB1AF65646A4007C2DAC17CB6CDACDE301E74FB2D728EA057CF744730450221009C00E8439E017CA622A5A1EE7643E26B4DE9C808DE2ABE45D33479D49A4CEC66022062175BE8733442FA2A4D9A35F85A57D58252AE7B19A66401FE238B36FA28E5A081146C1856D0E36019EA75C56D7E8CBA6E35F9B3F71583147FB49CD110A1C46838788CD12764E3B0F837E0DDF9EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1F1',
)
})
it('signTransaction with EscrowFinish', async function () {
const result = wallet.signTransaction(
REQUEST_FIXTURES.escrow as Transaction,
)
assert.deepEqual(result, RESPONSE_FIXTURES.escrow.signedTransaction)
})
it('signTransaction with multisignAddress', async function () {
const signature = wallet.signTransaction(
REQUEST_FIXTURES.signAs as Transaction,
wallet.getClassicAddress(),
)
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs.signedTransaction)
})
it('signTransaction with X Address and no given tag for multisignAddress', async function () {
const signature = wallet.signTransaction(
REQUEST_FIXTURES.signAs as Transaction,
wallet.getXAddress(),
)
assert.deepEqual(signature, RESPONSE_FIXTURES.signAs.signedTransaction)
})
it('signTransaction with X Address and tag for multisignAddress', async function () {
const signature = wallet.signTransaction(
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'
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,
)
assert.throws(() => {
const tx = decode(result) as unknown as Transaction
wallet.signTransaction(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)
})
it('signTransaction succeeds when given a transaction with no flags', async function () {
const tx: Transaction = {
TransactionType: 'Payment',
Account: 'r45Rev1EXGxy2hAUmJPCne97KUE7qyrD3j',
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '20000000',
Sequence: 1,
Fee: '12',
SigningPubKey: publicKey,
}
const wallet = new Wallet(publicKey, privateKey)
const signedTx: string = wallet.signTransaction(txJSON)
const result = wallet.signTransaction(tx)
const expectedResult =
'1200002400000001614000000001312D0068400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F5447446304402201C0A74EE8ECF5ED83734D7171FB65C01D90D67040DEDCC66414BD546CE302B5802205356843841BFFF60D15F5F5F9FB0AB9D66591778140AB2D137FF576D9DEC44BC8114EE3046A5DDF8422C40DDB93F1D522BB4FE6419158314FDB08D07AAA0EB711793A3027304D688E10C3648'
const decoded = decode(result)
assert(
decoded.Flags == null,
`Flags = ${JSON.stringify(decoded.Flags)}, should be undefined`,
)
assert.equal(result, expectedResult)
})
// TODO: Check the output of the signature against a known result
assert.isString(signedTx)
it('signTransaction succeeds with source.amount/destination.minAmount', async function () {
// See also: 'preparePayment with source.amount/destination.minAmount'
const tx: Transaction = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rEX4LtGJubaUcMWCJULcy4NVxGT9ZEMVRq',
Amount: {
currency: 'USD',
issuer: 'rMaa8VLBTjwTJWA2kSme4Sqgphhr6Lr6FH',
value:
'999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000',
},
Flags: 2147614720,
SendMax: {
currency: 'GBP',
issuer: 'rpat5TmYjDsnFSStmgTumFgXCM9eqsWPro',
value: '0.1',
},
DeliverMin: {
currency: 'USD',
issuer: 'rMaa8VLBTjwTJWA2kSme4Sqgphhr6Lr6FH',
value: '0.1248548562296331',
},
Sequence: 23,
LastLedgerSequence: 8820051,
Fee: '12',
}
const result = wallet.signTransaction(tx)
const expectedResult =
'12000022800200002400000017201B0086955361EC6386F26FC0FFFF0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A68400000000000000C69D4438D7EA4C6800000000000000000000000000047425000000000000C155FFE99C8C91F67083CEFFDB69EBFE76348CA6AD4446F8C5D8A5E0B0000000000000000000000005553440000000000DC596C88BCDE4E818D416FCDEEBF2C8656BADC9A732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F544744630440220297E0C7670C7DA491E0D649E62C123D988BA93FD7EA1B9141F1D376CDDF902F502205AF1936B22B18BBA7793A88ABEEABADB4CE0E4C3BE583066480F2F476B5ED08E81145E7B112523F68D2F5E879DB4EAC51C6698A6930483149F500E50C2F016CA01945E5A1E5846B61EF2D376'
const decoded = decode(result)
assert(
decoded.Flags === 2147614720,
`Flags = ${JSON.stringify(decoded.Flags)}, should be 2147614720`,
)
assert.deepEqual(result, expectedResult)
})
it('signTransaction throws when encoded tx does not match decoded tx because of illegal small fee', async function () {
const tx: Transaction = {
Flags: 2147483648,
TransactionType: 'AccountSet',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Domain: '6578616D706C652E636F6D',
LastLedgerSequence: 8820051,
Fee: '1.2',
Sequence: 23,
SigningPubKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8',
}
assert.throws(() => {
wallet.signTransaction(tx)
}, /1\.2 is an illegal amount/u)
})
it('signTransaction throws when encoded tx does not match decoded tx because of illegal higher fee', async function () {
const tx: Transaction = {
Flags: 2147483648,
TransactionType: 'AccountSet',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Domain: '6578616D706C652E636F6D',
LastLedgerSequence: 8820051,
Fee: '1123456.7',
Sequence: 23,
SigningPubKey:
'02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8',
}
assert.throws(() => {
wallet.signTransaction(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)
})
it('signTransaction with a Payment transaction with paths', async function () {
const payment: Transaction = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
Amount: {
currency: 'USD',
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
value:
'999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000',
},
Flags: 2147614720,
SendMax: '100',
DeliverMin: {
currency: 'USD',
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
value: '0.00004579644712312366',
},
Paths: [
[{ currency: 'USD', issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc' }],
],
LastLedgerSequence: 15696358,
Sequence: 1,
Fee: '12',
}
const result = wallet.signTransaction(payment)
assert.deepEqual(
result,
'12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474463044022049AD75980A5088EBCD768547E06427736BD8C4396B9BD3762CA8C1341BD7A4F9022060C94071C3BDF99FAB4BEB7C0578D6EBEE083157B470699645CCE4738A41D61081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900',
)
})
it('signTransaction with a prepared payment', async function () {
const payment: Transaction = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '1',
Flags: 2147483648,
Sequence: 23,
LastLedgerSequence: 8819954,
Fee: '12',
}
const result = wallet.signTransaction(payment)
const expectedResult =
'12000022800000002400000017201B008694F261400000000000000168400000000000000C732102A8A44DB3D4C73EEEE11DFE54D2029103B776AA8A8D293A91D645977C9DF5F54474473045022100E8929B68B137AB2AAB1AD3A4BB253883B0C8C318DC8BB39579375751B8E54AC502206893B2D61244AFE777DAC9FA3D9DDAC7780A9810AF4B322D629784FD626B8CE481145E7B112523F68D2F5E879DB4EAC51C6698A693048314FDB08D07AAA0EB711793A3027304D688E10C3648'
assert.deepEqual(result, expectedResult)
})
it('signTransaction throws when an illegal amount is provided', async function () {
const payment: Transaction = {
TransactionType: 'Payment',
Account: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
Destination: 'rQ3PTWGLCbPz8ZCicV5tCX3xuymojTng5r',
Amount: '1.1234567',
Flags: 2147483648,
Sequence: 23,
LastLedgerSequence: 8819954,
Fee: '12',
}
assert.throws(() => {
wallet.signTransaction(payment)
}, /^1.1234567 is an illegal amount/u)
})
})