Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: extract transaction batching logic #1323

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion governance/xc_admin/packages/xc_admin_common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"bigint-buffer": "^1.1.5",
"ethers": "^5.7.2",
"lodash": "^4.17.21",
"typescript": "^4.9.4"
"typescript": "^4.9.4",
"@pythnetwork/solana-utils": "*"
},
"devDependencies": {
"@types/bn.js": "^5.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,13 @@ import {
} from "@solana/web3.js";
import {
batchIntoExecutorPayload,
batchIntoTransactions,
getSizeOfCompressedU16,
getSizeOfExecutorInstructions,
getSizeOfTransaction,
MAX_EXECUTOR_PAYLOAD_SIZE,
} from "..";

it("Unit test compressed u16 size", async () => {
expect(getSizeOfCompressedU16(127)).toBe(1);
expect(getSizeOfCompressedU16(128)).toBe(2);
expect(getSizeOfCompressedU16(16383)).toBe(2);
expect(getSizeOfCompressedU16(16384)).toBe(3);
});
import {
getSizeOfTransaction,
TransactionBuilder,
} from "@pythnetwork/solana-utils";

it("Unit test for getSizeOfTransaction", async () => {
jest.setTimeout(60000);
Expand Down Expand Up @@ -84,7 +78,7 @@ it("Unit test for getSizeOfTransaction", async () => {
transaction.recentBlockhash = "GqdFtdM7zzWw33YyHtBNwPhyBsdYKcfm9gT47bWnbHvs"; // Mock blockhash from devnet
transaction.feePayer = payer.publicKey;
expect(transaction.serialize({ requireAllSignatures: false }).length).toBe(
getSizeOfTransaction(ixsToSend)
getSizeOfTransaction(ixsToSend, false)
);
});

Expand Down Expand Up @@ -115,21 +109,22 @@ it("Unit test for getSizeOfTransaction", async () => {
);
}

const txToSend: Transaction[] = batchIntoTransactions(ixsToSend);
const txToSend: Transaction[] =
TransactionBuilder.batchIntoLegacyTransactions(ixsToSend);
expect(
txToSend.map((tx) => tx.instructions.length).reduce((a, b) => a + b)
).toBe(ixsToSend.length);
expect(
txToSend.every(
(tx) => getSizeOfTransaction(tx.instructions) <= PACKET_DATA_SIZE
(tx) => getSizeOfTransaction(tx.instructions, false) <= PACKET_DATA_SIZE
)
).toBeTruthy();

for (let tx of txToSend) {
tx.recentBlockhash = "GqdFtdM7zzWw33YyHtBNwPhyBsdYKcfm9gT47bWnbHvs"; // Mock blockhash from devnet
tx.feePayer = payer.publicKey;
expect(tx.serialize({ requireAllSignatures: false }).length).toBe(
getSizeOfTransaction(tx.instructions)
getSizeOfTransaction(tx.instructions, false)
);
}

Expand Down
79 changes: 3 additions & 76 deletions governance/xc_admin/packages/xc_admin_common/src/propose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import SquadsMesh, { getIxAuthorityPDA, getTxPDA } from "@sqds/mesh";
import { MultisigAccount } from "@sqds/mesh/lib/types";
import { mapKey } from "./remote_executor";
import { WORMHOLE_ADDRESS } from "./wormhole";
import { TransactionBuilder } from "@pythnetwork/solana-utils";

export const MAX_EXECUTOR_PAYLOAD_SIZE = PACKET_DATA_SIZE - 687; // Bigger payloads won't fit in one addInstruction call when adding to the proposal
export const MAX_INSTRUCTIONS_PER_PROPOSAL = 256 - 1;
Expand Down Expand Up @@ -256,7 +257,7 @@ export class MultisigVault {
ixToSend.push(await this.activateProposalIx(proposalAddress));
ixToSend.push(await this.approveProposalIx(proposalAddress));

const txToSend = batchIntoTransactions(ixToSend);
const txToSend = TransactionBuilder.batchIntoLegacyTransactions(ixToSend);
await this.sendAllTransactions(txToSend);
return proposalAddress;
}
Expand Down Expand Up @@ -360,7 +361,7 @@ export class MultisigVault {
}
}

const txToSend = batchIntoTransactions(ixToSend);
const txToSend = TransactionBuilder.batchIntoLegacyTransactions(ixToSend);

await this.sendAllTransactions(txToSend);
return newProposals;
Expand Down Expand Up @@ -445,32 +446,6 @@ export function batchIntoExecutorPayload(
return batches;
}

/**
* Batch instructions into transactions
*/
export function batchIntoTransactions(
instructions: TransactionInstruction[]
): Transaction[] {
let i = 0;
const txToSend: Transaction[] = [];
while (i < instructions.length) {
let j = i + 2;
while (
j < instructions.length &&
getSizeOfTransaction(instructions.slice(i, j)) <= PACKET_DATA_SIZE
) {
j += 1;
}
const tx = new Transaction();
for (let k = i; k < j - 1; k += 1) {
tx.add(instructions[k]);
}
i = j - 1;
txToSend.push(tx);
}
return txToSend;
}

/** Get the size of instructions when serialized as in a remote executor payload */
export function getSizeOfExecutorInstructions(
instructions: TransactionInstruction[]
Expand All @@ -481,54 +456,6 @@ export function getSizeOfExecutorInstructions(
})
.reduce((a, b) => a + b);
}
/**
* Get the size of a transaction that would contain the provided array of instructions
*/
export function getSizeOfTransaction(
instructions: TransactionInstruction[]
): number {
const signers = new Set<string>();
const accounts = new Set<string>();

instructions.map((ix) => {
accounts.add(ix.programId.toBase58()),
ix.keys.map((key) => {
if (key.isSigner) {
signers.add(key.pubkey.toBase58());
}
accounts.add(key.pubkey.toBase58());
});
});

const instruction_sizes: number = instructions
.map(
(ix) =>
1 +
getSizeOfCompressedU16(ix.keys.length) +
ix.keys.length +
getSizeOfCompressedU16(ix.data.length) +
ix.data.length
)
.reduce((a, b) => a + b, 0);

return (
1 +
signers.size * 64 +
3 +
getSizeOfCompressedU16(accounts.size) +
32 * accounts.size +
32 +
getSizeOfCompressedU16(instructions.length) +
instruction_sizes
);
}

/**
* Get the size of n in bytes when serialized as a CompressedU16
*/
export function getSizeOfCompressedU16(n: number) {
return 1 + Number(n >= 128) + Number(n >= 16384);
}

/**
* Wrap `instruction` in a Wormhole message for remote execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ const usePyth = (): PythHookData => {
connectionRef.current = connection
;(async () => {
try {
const allPythAccounts = await connection.getProgramAccounts(
getPythProgramKeyForCluster(cluster)
)
const allPythAccounts = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by CI fix

...(await connection.getProgramAccounts(
getPythProgramKeyForCluster(cluster)
)),
]
if (cancelled) return
const priceRawConfigs: { [key: string]: PriceRawConfig } = {}

Expand Down
Loading
Loading