From ce7e4c622c36d79e23426d77cc979ae1fcbc3e5d Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:24:36 -0800 Subject: [PATCH] adds a single signer transaction signing test (#5683) adds a test case to wallet slow tests to serve as an example for how to build a test transaction and sign it using the JavaScript SDK useful for integrations that require testing transaction signing (e.g., Ledger integration) without running a network and/or building a chain --- ironfish/src/wallet/wallet.test.slow.ts | 71 ++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/ironfish/src/wallet/wallet.test.slow.ts b/ironfish/src/wallet/wallet.test.slow.ts index cae2f9263b..293baafc2a 100644 --- a/ironfish/src/wallet/wallet.test.slow.ts +++ b/ironfish/src/wallet/wallet.test.slow.ts @@ -2,10 +2,19 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import type { SpiedFunction } from 'jest-mock' -import { Asset, ASSET_ID_LENGTH, generateKey, multisig } from '@ironfish/rust-nodejs' +import { + Asset, + ASSET_ID_LENGTH, + generateKey, + multisig, + Note as NativeNote, + verifyTransactions, +} from '@ironfish/rust-nodejs' import { Assert } from '../assert' -import { Transaction } from '../primitives' +import { makeFakeWitness } from '../devUtils' +import { Note, RawTransaction, Transaction } from '../primitives' import { Target } from '../primitives/target' +import { TransactionVersion } from '../primitives/transaction' import { createNodeTest, useAccountFixture, @@ -1368,4 +1377,62 @@ describe('Wallet', () => { .map((identity) => identity.toString('hex')) expect(identities.sort()).toEqual(storedIdentities.sort()) }) + + it('build and signs transactions', () => { + // Generate random key + const { outgoingViewKey, proofAuthorizingKey, publicAddress, spendingKey, viewKey } = + generateKey() + + const inNote = new NativeNote( + publicAddress, + 42n, + Buffer.from(''), + Asset.nativeId(), + publicAddress, + ) + const outNote = new NativeNote( + publicAddress, + 40n, + Buffer.from(''), + Asset.nativeId(), + publicAddress, + ) + const asset = new Asset(publicAddress, 'Testcoin', 'A really cool coin') + const mintOutNote = new NativeNote( + publicAddress, + 5n, + Buffer.from(''), + asset.id(), + publicAddress, + ) + + // Construct fake note witness for input note to spend + const witness = makeFakeWitness(new Note(inNote.serialize())) + + // Construct raw transaction + const raw = new RawTransaction(TransactionVersion.V1) + raw.spends.push({ note: new Note(inNote.serialize()), witness }) + raw.outputs.push({ note: new Note(outNote.serialize()) }) + raw.outputs.push({ note: new Note(mintOutNote.serialize()) }) + raw.mints.push({ + creator: asset.creator().toString('hex'), + name: asset.name().toString(), + metadata: asset.metadata().toString(), + value: mintOutNote.value(), + }) + raw.fee = 1n + + // Build transaction and construct proofs to generate unsigned transaction + const unsignedTransaction = raw.build(proofAuthorizingKey, viewKey, outgoingViewKey) + + // Sign unsigned transaction + const serializedTransaction = unsignedTransaction.sign(spendingKey) + const signedTransaction = new Transaction(serializedTransaction) + + // Check that hash is equal to hash of unsigned transaction + expect(signedTransaction.unsignedHash()).toEqualBuffer(unsignedTransaction.hash()) + + // Check that signed transaction verifies + expect(verifyTransactions([serializedTransaction])).toBeTruthy() + }) })