-
Notifications
You must be signed in to change notification settings - Fork 652
/
verifySignature.ts
172 lines (159 loc) · 5.48 KB
/
verifySignature.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import {
Commitment,
Connection,
PublicKey,
PublicKeyInitData,
SystemProgram,
SYSVAR_INSTRUCTIONS_PUBKEY,
SYSVAR_RENT_PUBKEY,
TransactionInstruction,
} from "@solana/web3.js";
import { createSecp256k1Instruction } from "../../utils";
import {
getGuardianSet,
deriveGuardianSetKey,
getWormholeBridgeData,
} from "../accounts";
import { isBytes, ParsedVaa, parseVaa, SignedVaa } from "../../../vaa";
import { createReadOnlyWormholeProgramInterface } from "../program";
const MAX_LEN_GUARDIAN_KEYS = 19;
/**
* This is used in {@link createPostSignedVaaTransactions}'s initial transactions.
*
* Signatures are batched in groups of 7 due to instruction
* data limits. These signatures are passed through to the Secp256k1
* program to verify that the guardian public keys can be recovered.
* This instruction is paired with `verify_signatures` to validate the
* pubkey recovery.
*
* There are at most three pairs of instructions created.
*
* https://github.com/certusone/wormhole/blob/main/solana/bridge/program/src/api/verify_signature.rs
*
*
* @param {Connection} connection - Solana web3 connection
* @param {PublicKeyInitData} wormholeProgramId - wormhole program address
* @param {PublicKeyInitData} payer - transaction signer address
* @param {SignedVaa | ParsedVaa} vaa - either signed VAA bytes or parsed VAA (use {@link parseVaa} on signed VAA)
* @param {PublicKeyInitData} signatureSet - address to account of verified signatures
* @param {web3.ConfirmOptions} [options] - Solana confirmation options
*/
export async function createVerifySignaturesInstructions(
connection: Connection,
wormholeProgramId: PublicKeyInitData,
payer: PublicKeyInitData,
vaa: SignedVaa | ParsedVaa,
signatureSet: PublicKeyInitData,
commitment?: Commitment
): Promise<TransactionInstruction[]> {
const parsed = isBytes(vaa) ? parseVaa(vaa) : vaa;
const guardianSetIndex = parsed.guardianSetIndex;
const info = await getWormholeBridgeData(connection, wormholeProgramId);
if (guardianSetIndex != info.guardianSetIndex) {
throw new Error("guardianSetIndex != config.guardianSetIndex");
}
const guardianSetData = await getGuardianSet(
connection,
wormholeProgramId,
guardianSetIndex,
commitment
);
const guardianSignatures = parsed.guardianSignatures;
const guardianKeys = guardianSetData.keys;
const batchSize = 7;
const instructions: TransactionInstruction[] = [];
for (let i = 0; i < Math.ceil(guardianSignatures.length / batchSize); ++i) {
const start = i * batchSize;
const end = Math.min(guardianSignatures.length, (i + 1) * batchSize);
const signatureStatus = new Array(MAX_LEN_GUARDIAN_KEYS).fill(-1);
const signatures: Buffer[] = [];
const keys: Buffer[] = [];
for (let j = 0; j < end - start; ++j) {
const item = guardianSignatures.at(j + start)!;
signatures.push(item.signature);
const key = guardianKeys.at(item.index)!;
keys.push(key);
signatureStatus[item.index] = j;
}
instructions.push(
createSecp256k1Instruction(signatures, keys, parsed.hash)
);
instructions.push(
createVerifySignaturesInstruction(
wormholeProgramId,
payer,
parsed,
signatureSet,
signatureStatus
)
);
}
return instructions;
}
/**
* Make {@link TransactionInstruction} for `verify_signatures` instruction.
*
* This is used in {@link createVerifySignaturesInstructions} for each batch of signatures being verified.
* `signatureSet` is a {@link web3.Keypair} generated outside of this method, used
* for writing signatures and the message hash to.
*
* https://github.com/certusone/wormhole/blob/main/solana/bridge/program/src/api/verify_signature.rs
*
* @param {PublicKeyInitData} wormholeProgramId - wormhole program address
* @param {PublicKeyInitData} payer - transaction signer address
* @param {SignedVaa | ParsedVaa} vaa - either signed VAA (Buffer) or parsed VAA (use {@link parseVaa} on signed VAA)
* @param {PublicKeyInitData} signatureSet - key for signature set account
* @param {Buffer} signatureStatus - array of guardian indices
*
*/
function createVerifySignaturesInstruction(
wormholeProgramId: PublicKeyInitData,
payer: PublicKeyInitData,
vaa: SignedVaa | ParsedVaa,
signatureSet: PublicKeyInitData,
signatureStatus: number[]
): TransactionInstruction {
const methods = createReadOnlyWormholeProgramInterface(
wormholeProgramId
).methods.verifySignatures(signatureStatus);
// @ts-ignore
return methods._ixFn(...methods._args, {
accounts: getVerifySignatureAccounts(
wormholeProgramId,
payer,
signatureSet,
vaa
) as any,
signers: undefined,
remainingAccounts: undefined,
preInstructions: undefined,
postInstructions: undefined,
});
}
export interface VerifySignatureAccounts {
payer: PublicKey;
guardianSet: PublicKey;
signatureSet: PublicKey;
instructions: PublicKey;
rent: PublicKey;
systemProgram: PublicKey;
}
export function getVerifySignatureAccounts(
wormholeProgramId: PublicKeyInitData,
payer: PublicKeyInitData,
signatureSet: PublicKeyInitData,
vaa: SignedVaa | ParsedVaa
): VerifySignatureAccounts {
const parsed = isBytes(vaa) ? parseVaa(vaa) : vaa;
return {
payer: new PublicKey(payer),
guardianSet: deriveGuardianSetKey(
wormholeProgramId,
parsed.guardianSetIndex
),
signatureSet: new PublicKey(signatureSet),
instructions: SYSVAR_INSTRUCTIONS_PUBKEY,
rent: SYSVAR_RENT_PUBKEY,
systemProgram: SystemProgram.programId,
};
}