[xc-admin] batch wormhole messages (#615)
* Add * Cleanup * Add unit test
This commit is contained in:
parent
6f49dfbb4b
commit
ecb0e174d4
|
@ -14,13 +14,13 @@ import {
|
|||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import {
|
||||
batchIntoExecutorPayload,
|
||||
batchIntoTransactions,
|
||||
getSizeOfCompressedU16,
|
||||
getSizeOfExecutorInstructions,
|
||||
getSizeOfTransaction,
|
||||
MultisigInstructionProgram,
|
||||
MultisigParser,
|
||||
MAX_EXECUTOR_PAYLOAD_SIZE,
|
||||
} from "..";
|
||||
import { PythMultisigInstruction } from "../multisig_transaction/PythMultisigInstruction";
|
||||
|
||||
it("Unit test compressed u16 size", async () => {
|
||||
expect(getSizeOfCompressedU16(127)).toBe(1);
|
||||
|
@ -115,10 +115,7 @@ it("Unit test for getSizeOfTransaction", async () => {
|
|||
);
|
||||
}
|
||||
|
||||
const txToSend: Transaction[] = batchIntoTransactions(
|
||||
ixsToSend,
|
||||
payer.publicKey
|
||||
);
|
||||
const txToSend: Transaction[] = batchIntoTransactions(ixsToSend);
|
||||
expect(
|
||||
txToSend.map((tx) => tx.instructions.length).reduce((a, b) => a + b)
|
||||
).toBe(ixsToSend.length);
|
||||
|
@ -135,4 +132,16 @@ it("Unit test for getSizeOfTransaction", async () => {
|
|||
getSizeOfTransaction(tx.instructions)
|
||||
);
|
||||
}
|
||||
|
||||
const batches: TransactionInstruction[][] =
|
||||
batchIntoExecutorPayload(ixsToSend);
|
||||
expect(batches.map((batch) => batch.length).reduce((a, b) => a + b)).toBe(
|
||||
ixsToSend.length
|
||||
);
|
||||
expect(
|
||||
batches.every(
|
||||
(batch) =>
|
||||
getSizeOfExecutorInstructions(batch) <= MAX_EXECUTOR_PAYLOAD_SIZE
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
|
|
@ -19,6 +19,8 @@ import {
|
|||
import { ExecutePostedVaa } from "./governance_payload/ExecutePostedVaa";
|
||||
import { OPS_KEY } from "./multisig";
|
||||
|
||||
export const MAX_EXECUTOR_PAYLOAD_SIZE = PACKET_DATA_SIZE - 687; // Bigger payloads won't fit in one addInstruction call when adding to the proposal
|
||||
|
||||
type SquadInstruction = {
|
||||
instruction: TransactionInstruction;
|
||||
authorityIndex?: number;
|
||||
|
@ -43,7 +45,7 @@ export async function proposeInstructions(
|
|||
): Promise<PublicKey> {
|
||||
const msAccount = await squad.getMultisig(vault);
|
||||
let ixToSend: TransactionInstruction[] = [];
|
||||
const createProposal = ixToSend.push(
|
||||
ixToSend.push(
|
||||
await squad.buildCreateTransaction(
|
||||
msAccount.publicKey,
|
||||
msAccount.authorityIndex,
|
||||
|
@ -60,12 +62,14 @@ export async function proposeInstructions(
|
|||
if (!wormholeAddress) {
|
||||
throw new Error("Need wormhole address");
|
||||
}
|
||||
for (let i = 0; i < instructions.length; i++) {
|
||||
|
||||
const batches = batchIntoExecutorPayload(instructions);
|
||||
for (const [i, batch] of batches.entries()) {
|
||||
const squadIx = await wrapAsRemoteInstruction(
|
||||
squad,
|
||||
vault,
|
||||
newProposalAddress,
|
||||
instructions[i],
|
||||
batch,
|
||||
i + 1,
|
||||
wormholeAddress
|
||||
);
|
||||
|
@ -100,7 +104,7 @@ export async function proposeInstructions(
|
|||
|
||||
ixToSend.push(await squad.buildApproveTransaction(vault, newProposalAddress));
|
||||
|
||||
const txToSend = batchIntoTransactions(ixToSend, squad.wallet.publicKey);
|
||||
const txToSend = batchIntoTransactions(ixToSend);
|
||||
await new AnchorProvider(
|
||||
squad.connection,
|
||||
squad.wallet,
|
||||
|
@ -113,12 +117,38 @@ export async function proposeInstructions(
|
|||
return newProposalAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch instructions into batches for inclusion in a remote executor payload
|
||||
*/
|
||||
export function batchIntoExecutorPayload(
|
||||
instructions: TransactionInstruction[]
|
||||
): TransactionInstruction[][] {
|
||||
let i = 0;
|
||||
const batches: TransactionInstruction[][] = [];
|
||||
while (i < instructions.length) {
|
||||
let j = i + 2;
|
||||
while (
|
||||
j < instructions.length &&
|
||||
getSizeOfExecutorInstructions(instructions.slice(i, j)) <=
|
||||
MAX_EXECUTOR_PAYLOAD_SIZE
|
||||
) {
|
||||
j += 1;
|
||||
}
|
||||
const batch: TransactionInstruction[] = [];
|
||||
for (let k = i; k < j - 1; k += 1) {
|
||||
batch.push(instructions[k]);
|
||||
}
|
||||
i = j - 1;
|
||||
batches.push(batch);
|
||||
}
|
||||
return batches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch instructions into transactions
|
||||
*/
|
||||
export function batchIntoTransactions(
|
||||
instructions: TransactionInstruction[],
|
||||
feePayer: PublicKey
|
||||
instructions: TransactionInstruction[]
|
||||
): Transaction[] {
|
||||
let i = 0;
|
||||
const txToSend: Transaction[] = [];
|
||||
|
@ -131,7 +161,6 @@ export function batchIntoTransactions(
|
|||
j += 1;
|
||||
}
|
||||
const tx = new Transaction();
|
||||
tx.feePayer = feePayer;
|
||||
for (let k = i; k < j - 1; k += 1) {
|
||||
tx.add(instructions[k]);
|
||||
}
|
||||
|
@ -141,6 +170,16 @@ export function batchIntoTransactions(
|
|||
return txToSend;
|
||||
}
|
||||
|
||||
/** Get the size of instructions when serialized as in a remote executor payload */
|
||||
export function getSizeOfExecutorInstructions(
|
||||
instructions: TransactionInstruction[]
|
||||
) {
|
||||
return instructions
|
||||
.map((ix) => {
|
||||
return 32 + 4 + ix.keys.length * 34 + 4 + ix.data.length;
|
||||
})
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
/**
|
||||
* Get the size of a transaction that would contain the provided array of instructions
|
||||
*/
|
||||
|
@ -170,6 +209,7 @@ export function getSizeOfTransaction(
|
|||
ix.data.length
|
||||
)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
|
||||
return (
|
||||
1 +
|
||||
signers.size * 64 +
|
||||
|
@ -194,7 +234,7 @@ export function getSizeOfCompressedU16(n: number) {
|
|||
* @param squad Squads client
|
||||
* @param vault vault public key (the id of the multisig where these instructions should be proposed)
|
||||
* @param proposalAddress address of the proposal
|
||||
* @param instruction instruction to be wrapped in a Wormhole message
|
||||
* @param instructions instructions to be wrapped in a Wormhole message
|
||||
* @param instructionIndex index of the instruction within the proposal
|
||||
* @param wormholeAddress address of the Wormhole bridge
|
||||
* @returns an instruction to be proposed
|
||||
|
@ -203,7 +243,7 @@ export async function wrapAsRemoteInstruction(
|
|||
squad: Squads,
|
||||
vault: PublicKey,
|
||||
proposalAddress: PublicKey,
|
||||
instruction: TransactionInstruction,
|
||||
instructions: TransactionInstruction[],
|
||||
instructionIndex: number,
|
||||
wormholeAddress: PublicKey
|
||||
): Promise<SquadInstruction> {
|
||||
|
@ -225,9 +265,7 @@ export async function wrapAsRemoteInstruction(
|
|||
provider
|
||||
);
|
||||
|
||||
const buffer: Buffer = new ExecutePostedVaa("pythnet", [
|
||||
instruction,
|
||||
]).encode();
|
||||
const buffer: Buffer = new ExecutePostedVaa("pythnet", instructions).encode();
|
||||
|
||||
const accounts = getPostMessageAccounts(wormholeAddress, emitter, messagePDA);
|
||||
|
||||
|
|
Loading…
Reference in New Issue