feat: implement addSignature in VersionedTransaction (#27945)
* Implement addSignature in VersionedTransaction * Update asserts * VersionedTransaction.addSignature test * Update VersionedTransaction tests
This commit is contained in:
parent
e1a49f7766
commit
e15a5c010e
|
@ -7,6 +7,7 @@ import {SIGNATURE_LENGTH_IN_BYTES} from './constants';
|
|||
import * as shortvec from '../utils/shortvec-encoding';
|
||||
import * as Layout from '../layout';
|
||||
import {sign} from '../utils/ed25519';
|
||||
import {PublicKey} from '../publickey';
|
||||
|
||||
export type TransactionVersion = 'legacy' | 0;
|
||||
|
||||
|
@ -106,4 +107,20 @@ export class VersionedTransaction {
|
|||
this.signatures[signerIndex] = sign(messageData, signer.secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
addSignature(publicKey: PublicKey, signature: Uint8Array) {
|
||||
assert(signature.byteLength === 64, 'Signature must be 64 bytes long');
|
||||
const signerPubkeys = this.message.staticAccountKeys.slice(
|
||||
0,
|
||||
this.message.header.numRequiredSignatures,
|
||||
);
|
||||
const signerIndex = signerPubkeys.findIndex(pubkey =>
|
||||
pubkey.equals(publicKey),
|
||||
);
|
||||
assert(
|
||||
signerIndex >= 0,
|
||||
`Can not add signature; \`${publicKey.toBase58()}\` is not required to sign this transaction`,
|
||||
);
|
||||
this.signatures[signerIndex] = signature;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {PublicKey} from '../src/publickey';
|
|||
import {
|
||||
Transaction,
|
||||
TransactionInstruction,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from '../src/transaction';
|
||||
import {StakeProgram, SystemProgram} from '../src/programs';
|
||||
|
@ -860,26 +861,6 @@ describe('Transaction', () => {
|
|||
expect(tx.verifySignatures()).to.be.true;
|
||||
});
|
||||
|
||||
it('deserializes versioned transactions', () => {
|
||||
const serializedVersionedTx = Buffer.from(
|
||||
'AdTIDASR42TgVuXKkd7mJKk373J3LPVp85eyKMVcrboo9KTY8/vm6N/Cv0NiHqk2I8iYw6VX5ZaBKG8z' +
|
||||
'9l1XjwiAAQACA+6qNbqfjaIENwt9GzEK/ENiB/ijGwluzBUmQ9xlTAMcCaS0ctnyxTcXXlJr7u2qtnaM' +
|
||||
'gIAO2/c7RBD0ipHWUcEDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAJbI7VNs6MzREUlnzRaJ' +
|
||||
'pBKP8QQoDn2dWQvD0KIgHFDiAwIACQAgoQcAAAAAAAIABQEAAAQAATYPBwAKBDIBAyQWIw0oCxIdCA4i' +
|
||||
'JzQRKwUZHxceHCohMBUJJiwpMxAaGC0TLhQxGyAMBiU2NS8VDgAAAADuAgAAAAAAAAIAAAAAAAAAAdGCT' +
|
||||
'Qiq5yw3+3m1sPoRNj0GtUNNs0FIMocxzt3zuoSZHQABAwQFBwgLDA8RFBcYGhwdHh8iIyUnKiwtLi8yF' +
|
||||
'wIGCQoNDhASExUWGRsgISQmKCkrMDEz',
|
||||
'base64',
|
||||
);
|
||||
|
||||
expect(() => Transaction.from(serializedVersionedTx)).to.throw(
|
||||
'Versioned messages must be deserialized with VersionedMessage.deserialize()',
|
||||
);
|
||||
|
||||
const versionedTx = VersionedTransaction.deserialize(serializedVersionedTx);
|
||||
expect(versionedTx.message.version).to.eq(0);
|
||||
});
|
||||
|
||||
it('can serialize, deserialize, and reserialize with a partial signer', () => {
|
||||
const signer = Keypair.generate();
|
||||
const acc0Writable = Keypair.generate();
|
||||
|
@ -963,3 +944,97 @@ describe('Transaction', () => {
|
|||
t1.serialize();
|
||||
});
|
||||
});
|
||||
|
||||
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',
|
||||
'base64',
|
||||
);
|
||||
|
||||
expect(() => Transaction.from(serializedVersionedTx)).to.throw(
|
||||
'Versioned messages must be deserialized with VersionedMessage.deserialize()',
|
||||
);
|
||||
|
||||
const versionedTx = VersionedTransaction.deserialize(serializedVersionedTx);
|
||||
expect(versionedTx.message.version).to.eq(0);
|
||||
});
|
||||
|
||||
describe('addSignature', () => {
|
||||
const signer1 = Keypair.generate();
|
||||
const signer2 = Keypair.generate();
|
||||
const signer3 = Keypair.generate();
|
||||
|
||||
const recentBlockhash = new PublicKey(3).toBuffer();
|
||||
|
||||
const message = new TransactionMessage({
|
||||
payerKey: signer1.publicKey,
|
||||
instructions: [
|
||||
new TransactionInstruction({
|
||||
data: Buffer.from('Hello!'),
|
||||
keys: [
|
||||
{
|
||||
pubkey: signer1.publicKey,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: signer2.publicKey,
|
||||
isSigner: true,
|
||||
isWritable: true,
|
||||
},
|
||||
{
|
||||
pubkey: signer3.publicKey,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
],
|
||||
programId: new PublicKey(
|
||||
'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr',
|
||||
),
|
||||
}),
|
||||
],
|
||||
recentBlockhash: bs58.encode(recentBlockhash),
|
||||
});
|
||||
|
||||
const transaction = new VersionedTransaction(message.compileToV0Message());
|
||||
|
||||
it('appends externally generated signatures at correct indexes', () => {
|
||||
const signature1 = sign(
|
||||
transaction.message.serialize(),
|
||||
signer1.secretKey,
|
||||
);
|
||||
const signature2 = sign(
|
||||
transaction.message.serialize(),
|
||||
signer2.secretKey,
|
||||
);
|
||||
|
||||
transaction.addSignature(signer2.publicKey, signature2);
|
||||
transaction.addSignature(signer1.publicKey, signature1);
|
||||
|
||||
expect(transaction.signatures).to.have.length(2);
|
||||
expect(transaction.signatures[0]).to.eq(signature1);
|
||||
expect(transaction.signatures[1]).to.eq(signature2);
|
||||
});
|
||||
|
||||
it('fatals when the signature is the wrong length', () => {
|
||||
expect(() => {
|
||||
transaction.addSignature(signer1.publicKey, new Uint8Array(32));
|
||||
}).to.throw('Signature must be 64 bytes long');
|
||||
});
|
||||
|
||||
it('fatals when adding a signature for a public key that has not been marked as a signer', () => {
|
||||
expect(() => {
|
||||
transaction.addSignature(signer3.publicKey, new Uint8Array(64));
|
||||
}).to.throw(
|
||||
`Can not add signature; \`${signer3.publicKey.toBase58()}\` is not required to sign this transaction`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue