Skip to content

Commit

Permalink
feat: add tests for partially signed tx verification
Browse files Browse the repository at this point in the history
  • Loading branch information
cryptopapi997 committed Dec 20, 2022
1 parent 9cf63c2 commit 6da02f1
Showing 1 changed file with 135 additions and 62 deletions.
197 changes: 135 additions & 62 deletions web3.js/test/transaction.test.ts
@@ -1,23 +1,23 @@
import bs58 from 'bs58';
import {Buffer} from 'buffer';
import {expect} from 'chai';
import { Buffer } from 'buffer';
import { expect } from 'chai';

import {Connection} from '../src/connection';
import {Keypair} from '../src/keypair';
import {PublicKey} from '../src/publickey';
import { Connection } from '../src/connection';
import { Keypair } from '../src/keypair';
import { PublicKey } from '../src/publickey';
import {
Transaction,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from '../src/transaction';
import {StakeProgram, SystemProgram} from '../src/programs';
import {Message} from '../src/message';
import { StakeProgram, SystemProgram } from '../src/programs';
import { Message } from '../src/message';
import invariant from '../src/utils/assert';
import {toBuffer} from '../src/utils/to-buffer';
import {helpers} from './mocks/rpc-http';
import {url} from './url';
import {sign} from '../src/utils/ed25519';
import { toBuffer } from '../src/utils/to-buffer';
import { helpers } from './mocks/rpc-http';
import { url } from './url';
import { sign } from '../src/utils/ed25519';

describe('Transaction', () => {
describe('compileMessage', () => {
Expand Down Expand Up @@ -61,19 +61,19 @@ describe('Transaction', () => {
}).add({
keys: [
// Regular accounts
{pubkey: accountRegular9, isSigner: false, isWritable: false},
{pubkey: accountRegular8, isSigner: false, isWritable: false},
{ pubkey: accountRegular9, isSigner: false, isWritable: false },
{ pubkey: accountRegular8, isSigner: false, isWritable: false },
// Writable accounts
{pubkey: accountWritable7, isSigner: false, isWritable: true},
{pubkey: accountWritable6, isSigner: false, isWritable: true},
{ pubkey: accountWritable7, isSigner: false, isWritable: true },
{ pubkey: accountWritable6, isSigner: false, isWritable: true },
// Signers
{pubkey: accountSigner5, isSigner: true, isWritable: false},
{pubkey: accountSigner4, isSigner: true, isWritable: false},
{ pubkey: accountSigner5, isSigner: true, isWritable: false },
{ pubkey: accountSigner4, isSigner: true, isWritable: false },
// Writable Signers
{pubkey: accountWritableSigner3, isSigner: true, isWritable: true},
{pubkey: accountWritableSigner2, isSigner: true, isWritable: true},
{ pubkey: accountWritableSigner3, isSigner: true, isWritable: true },
{ pubkey: accountWritableSigner2, isSigner: true, isWritable: true },
// Payer.
{pubkey: payer, isSigner: true, isWritable: true},
{ pubkey: payer, isSigner: true, isWritable: true },
],
programId,
});
Expand Down Expand Up @@ -126,19 +126,19 @@ describe('Transaction', () => {
}).add({
keys: [
// Should sort last.
{pubkey: account5, isSigner: false, isWritable: false},
{pubkey: account5, isSigner: false, isWritable: false},
{ pubkey: account5, isSigner: false, isWritable: false },
{ pubkey: account5, isSigner: false, isWritable: false },
// Should be considered writeable.
{pubkey: account4, isSigner: false, isWritable: false},
{pubkey: account4, isSigner: false, isWritable: true},
{ pubkey: account4, isSigner: false, isWritable: false },
{ pubkey: account4, isSigner: false, isWritable: true },
// Should be considered a signer.
{pubkey: account3, isSigner: false, isWritable: false},
{pubkey: account3, isSigner: true, isWritable: false},
{ pubkey: account3, isSigner: false, isWritable: false },
{ pubkey: account3, isSigner: true, isWritable: false },
// Should be considered a writable signer.
{pubkey: account2, isSigner: false, isWritable: true},
{pubkey: account2, isSigner: true, isWritable: false},
{ pubkey: account2, isSigner: false, isWritable: true },
{ pubkey: account2, isSigner: true, isWritable: false },
// Payer.
{pubkey: payer, isSigner: true, isWritable: true},
{ pubkey: payer, isSigner: true, isWritable: true },
],
programId,
});
Expand Down Expand Up @@ -169,8 +169,8 @@ describe('Transaction', () => {
lastValidBlockHeight: 9999,
}).add({
keys: [
{pubkey: other.publicKey, isSigner: true, isWritable: true},
{pubkey: payer.publicKey, isSigner: true, isWritable: true},
{ pubkey: other.publicKey, isSigner: true, isWritable: true },
{ pubkey: payer.publicKey, isSigner: true, isWritable: true },
],
programId,
});
Expand Down Expand Up @@ -223,7 +223,7 @@ describe('Transaction', () => {
blockhash: recentBlockhash,
lastValidBlockHeight: 9999,
}).add({
keys: [{pubkey: payer.publicKey, isSigner: true, isWritable: false}],
keys: [{ pubkey: payer.publicKey, isSigner: true, isWritable: false }],
programId,
});

Expand Down Expand Up @@ -358,7 +358,7 @@ describe('Transaction', () => {
const accountFrom = Keypair.generate();
const accountTo = Keypair.generate();

const latestBlockhash = await helpers.latestBlockhash({connection});
const latestBlockhash = await helpers.latestBlockhash({ connection });

const transaction = new Transaction({
feePayer: accountFrom.publicKey,
Expand Down Expand Up @@ -406,7 +406,7 @@ describe('Transaction', () => {

expect(() => partialTransaction.serialize()).to.throw();
expect(() =>
partialTransaction.serialize({requireAllSignatures: false}),
partialTransaction.serialize({ requireAllSignatures: false }),
).not.to.throw();

partialTransaction.partialSign(account2);
Expand All @@ -421,7 +421,7 @@ describe('Transaction', () => {
invariant(partialTransaction.signatures[0].signature);
partialTransaction.signatures[0].signature.fill(1);
expect(() =>
partialTransaction.serialize({requireAllSignatures: false}),
partialTransaction.serialize({ requireAllSignatures: false }),
).to.throw();
expect(() =>
partialTransaction.serialize({
Expand All @@ -444,9 +444,9 @@ describe('Transaction', () => {
lastValidBlockHeight: 9999,
}).add({
keys: [
{pubkey: duplicate1.publicKey, isSigner: true, isWritable: true},
{pubkey: payer.publicKey, isSigner: false, isWritable: true},
{pubkey: duplicate2.publicKey, isSigner: true, isWritable: false},
{ pubkey: duplicate1.publicKey, isSigner: true, isWritable: true },
{ pubkey: payer.publicKey, isSigner: false, isWritable: true },
{ pubkey: duplicate2.publicKey, isSigner: true, isWritable: false },
],
programId,
});
Expand Down Expand Up @@ -475,9 +475,9 @@ describe('Transaction', () => {
lastValidBlockHeight: 9999,
}).add({
keys: [
{pubkey: duplicate1.publicKey, isSigner: true, isWritable: true},
{pubkey: payer.publicKey, isSigner: false, isWritable: true},
{pubkey: duplicate2.publicKey, isSigner: true, isWritable: false},
{ pubkey: duplicate1.publicKey, isSigner: true, isWritable: true },
{ pubkey: payer.publicKey, isSigner: false, isWritable: true },
{ pubkey: duplicate2.publicKey, isSigner: true, isWritable: false },
],
programId,
});
Expand Down Expand Up @@ -566,7 +566,7 @@ describe('Transaction', () => {
}),
};

const transferTransaction = new Transaction({nonceInfo}).add(
const transferTransaction = new Transaction({ nonceInfo }).add(
SystemProgram.transfer({
fromPubkey: account1.publicKey,
toPubkey: account2.publicKey,
Expand All @@ -580,7 +580,7 @@ describe('Transaction', () => {

const stakeAccount = Keypair.generate();
const voteAccount = Keypair.generate();
const stakeTransaction = new Transaction({nonceInfo}).add(
const stakeTransaction = new Transaction({ nonceInfo }).add(
StakeProgram.delegate({
stakePubkey: stakeAccount.publicKey,
authorizedPubkey: account1.publicKey,
Expand Down Expand Up @@ -715,7 +715,7 @@ describe('Transaction', () => {
authorizedPubkey: nonceAuthority,
}),
};
const transaction = new Transaction({nonceInfo});
const transaction = new Transaction({ nonceInfo });
expect(transaction.recentBlockhash).to.be.undefined;
expect(transaction.lastValidBlockHeight).to.be.undefined;
expect(transaction.nonceInfo).to.equal(nonceInfo);
Expand Down Expand Up @@ -785,7 +785,7 @@ describe('Transaction', () => {
expectedTransaction.serialize();
}).to.throw('Transaction fee payer required');
expect(() => {
expectedTransaction.serialize({verifySignatures: false});
expectedTransaction.serialize({ verifySignatures: false });
}).to.throw('Transaction fee payer required');
expect(() => {
expectedTransaction.serializeMessage();
Expand All @@ -799,7 +799,7 @@ describe('Transaction', () => {
}).to.throw('Signature verification failed');

// Serializing without signatures is allowed if sigverify disabled.
expectedTransaction.serialize({verifySignatures: false});
expectedTransaction.serialize({ verifySignatures: false });

// Serializing the message is allowed when signature array has null signatures
expectedTransaction.serializeMessage();
Expand All @@ -814,20 +814,20 @@ describe('Transaction', () => {
}).to.throw('Signature verification failed');

// Serializing without signatures is allowed if sigverify disabled.
expectedTransaction.serialize({verifySignatures: false});
expectedTransaction.serialize({ verifySignatures: false });

// Serializing the message is allowed when signature array has null signatures
expectedTransaction.serializeMessage();

const expectedSerializationWithNoSignatures = Buffer.from(
'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAABAAEDE5j2LG0aRXxRumpLXz29L2n8qTIWIY3ImX5Ba9F9k8r9' +
'Q5/Mtmcn8onFxt47xKj+XdXXd3C8j/FcPu7csUrz/AAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAxJrndgN4IFTxep3s6kO0ROug7bEsbx0xxuDkqEvwUusBAgIAAQwC' +
'AAAAMQAAAAAAAAA=',
'AAAAAAAAAAAAAAAAAAABAAEDE5j2LG0aRXxRumpLXz29L2n8qTIWIY3ImX5Ba9F9k8r9' +
'Q5/Mtmcn8onFxt47xKj+XdXXd3C8j/FcPu7csUrz/AAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAxJrndgN4IFTxep3s6kO0ROug7bEsbx0xxuDkqEvwUusBAgIAAQwC' +
'AAAAMQAAAAAAAAA=',
'base64',
);
expect(expectedTransaction.serialize({requireAllSignatures: false})).to.eql(
expect(expectedTransaction.serialize({ requireAllSignatures: false })).to.eql(
expectedSerializationWithNoSignatures,
);

Expand All @@ -836,15 +836,88 @@ describe('Transaction', () => {
expect(expectedTransaction.signatures).to.have.length(1);
const expectedSerialization = Buffer.from(
'AVuErQHaXv0SG0/PchunfxHKt8wMRfMZzqV0tkC5qO6owYxWU2v871AoWywGoFQr4z+q/7mE8lIufNl/' +
'kxj+nQ0BAAEDE5j2LG0aRXxRumpLXz29L2n8qTIWIY3ImX5Ba9F9k8r9Q5/Mtmcn8onFxt47xKj+XdXX' +
'd3C8j/FcPu7csUrz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxJrndgN4IFTxep3s6kO0' +
'ROug7bEsbx0xxuDkqEvwUusBAgIAAQwCAAAAMQAAAAAAAAA=',
'kxj+nQ0BAAEDE5j2LG0aRXxRumpLXz29L2n8qTIWIY3ImX5Ba9F9k8r9Q5/Mtmcn8onFxt47xKj+XdXX' +
'd3C8j/FcPu7csUrz/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxJrndgN4IFTxep3s6kO0' +
'ROug7bEsbx0xxuDkqEvwUusBAgIAAQwCAAAAMQAAAAAAAAA=',
'base64',
);
expect(expectedTransaction.serialize()).to.eql(expectedSerialization);
expect(expectedTransaction.signatures).to.have.length(1);
});

it('partially signed transaction', () => {
const sender = Keypair.fromSeed(Uint8Array.from(Array(32).fill(8))); // Arbitrary known account
const feePayer = Keypair.fromSeed(Uint8Array.from(Array(32).fill(9))); // Arbitrary known account
const fakeKey = Keypair.fromSeed(Uint8Array.from(Array(32).fill(10))); // Arbitrary known account
const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash
const recipient = new PublicKey(
'J3dxNj7nDRRqRRXuEMynDG57DkZK4jYRuv3Garmb1i99',
); // Arbitrary known public key
const transfer = SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipient,
lamports: 49,
});
const expectedTransaction = new Transaction({
blockhash: recentBlockhash,
lastValidBlockHeight: 9999,
}).add(transfer);

// To have 2 required signers we add a feepayer
expectedTransaction.feePayer = feePayer.publicKey;

expect(expectedTransaction.signatures).to.have.length(0);

// No extra param should require all sigs, should be false for no sigs
expect(expectedTransaction.verifySignatures()).to.be.false;

// True should require all sigs, should be false for no sigs
expect(expectedTransaction.verifySignatures(true)).to.be.false;

// False should verify only the available sigs, should be true for no sigs
expect(expectedTransaction.verifySignatures(false)).to.be.true;

// Add one required sig
expectedTransaction.partialSign(sender);

expect(expectedTransaction.signatures.filter(sig => sig.signature !== null)).to.have.length(1);

// No extra param should require all sigs, should be false for one missing sig
expect(expectedTransaction.verifySignatures()).to.be.false;

// True should require all sigs, should be false one missing sigs
expect(expectedTransaction.verifySignatures(true)).to.be.false;

// False should verify only the available sigs, should be true one valid sig
expect(expectedTransaction.verifySignatures(false)).to.be.true;

// Add all required sigs
expectedTransaction.partialSign(feePayer);

expect(expectedTransaction.signatures.filter(sig => sig.signature !== null)).to.have.length(2);

// No extra param should require all sigs, should be true for no missing sig
expect(expectedTransaction.verifySignatures()).to.be.true;

// True should require all sigs, should be true for no missing sig
expect(expectedTransaction.verifySignatures(true)).to.be.true;

// False should verify only the available sigs, should be true for no missing sig
expect(expectedTransaction.verifySignatures(false)).to.be.true;

// Add a wrong signature
expectedTransaction.signatures[0].publicKey = fakeKey.publicKey;

// No extra param should require all sigs, should throw for wrong sig
expect(() => expectedTransaction.verifySignatures()).to.throw('unknown signer: ' + fakeKey.publicKey.toBase58());

// True should require all sigs, should throw for wrong sig
expect(() => expectedTransaction.verifySignatures(true)).to.throw('unknown signer: ' + fakeKey.publicKey.toBase58());

// False should verify only the available sigs, should throw for wrong sig
expect(() => expectedTransaction.verifySignatures(false)).to.throw('unknown signer: ' + fakeKey.publicKey.toBase58());
});

it('deprecated - externally signed stake delegate', () => {
const authority = Keypair.fromSeed(Uint8Array.from(Array(32).fill(1)));
const stake = new PublicKey(2);
Expand Down Expand Up @@ -961,7 +1034,7 @@ describe('Transaction', () => {
programId: Keypair.generate().publicKey,
}),
);
const t1 = Transaction.from(t0.serialize({requireAllSignatures: false}));
const t1 = Transaction.from(t0.serialize({ requireAllSignatures: false }));
t1.partialSign(signer);
t1.serialize();
});
Expand All @@ -971,12 +1044,12 @@ describe('VersionedTransaction', () => {
it('deserializes versioned transactions', () => {
const serializedVersionedTx = Buffer.from(
'AdTIDASR42TgVuXKkd7mJKk373J3LPVp85eyKMVcrboo9KTY8/vm6N/Cv0NiHqk2I8iYw6VX5ZaBKG8z' +
'9l1XjwiAAQACA+6qNbqfjaIENwt9GzEK/ENiB/ijGwluzBUmQ9xlTAMcCaS0ctnyxTcXXlJr7u2qtnaM' +
'gIAO2/c7RBD0ipHWUcEDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAJbI7VNs6MzREUlnzRaJ' +
'pBKP8QQoDn2dWQvD0KIgHFDiAwIACQAgoQcAAAAAAAIABQEAAAQAATYPBwAKBDIBAyQWIw0oCxIdCA4i' +
'JzQRKwUZHxceHCohMBUJJiwpMxAaGC0TLhQxGyAMBiU2NS8VDgAAAADuAgAAAAAAAAIAAAAAAAAAAdGCT' +
'Qiq5yw3+3m1sPoRNj0GtUNNs0FIMocxzt3zuoSZHQABAwQFBwgLDA8RFBcYGhwdHh8iIyUnKiwtLi8yF' +
'wIGCQoNDhASExUWGRsgISQmKCkrMDEz',
'9l1XjwiAAQACA+6qNbqfjaIENwt9GzEK/ENiB/ijGwluzBUmQ9xlTAMcCaS0ctnyxTcXXlJr7u2qtnaM' +
'gIAO2/c7RBD0ipHWUcEDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAJbI7VNs6MzREUlnzRaJ' +
'pBKP8QQoDn2dWQvD0KIgHFDiAwIACQAgoQcAAAAAAAIABQEAAAQAATYPBwAKBDIBAyQWIw0oCxIdCA4i' +
'JzQRKwUZHxceHCohMBUJJiwpMxAaGC0TLhQxGyAMBiU2NS8VDgAAAADuAgAAAAAAAAIAAAAAAAAAAdGCT' +
'Qiq5yw3+3m1sPoRNj0GtUNNs0FIMocxzt3zuoSZHQABAwQFBwgLDA8RFBcYGhwdHh8iIyUnKiwtLi8yF' +
'wIGCQoNDhASExUWGRsgISQmKCkrMDEz',
'base64',
);

Expand Down

0 comments on commit 6da02f1

Please sign in to comment.