solana-program-library/account-compression/sdk/tests/events/changelog.test.ts

129 lines
4.4 KiB
TypeScript

import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet";
import { BN } from 'bn.js';
import { AnchorProvider } from "@project-serum/anchor";
import {
Connection,
Signer,
Keypair,
PublicKey,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { bs58 } from "@project-serum/anchor/dist/cjs/utils/bytes";
import { assert } from "chai";
import * as crypto from "crypto";
import {
createReplaceIx,
createAppendIx,
createTransferAuthorityIx,
createVerifyLeafIx,
createAllocTreeIx,
createInitEmptyMerkleTreeInstruction,
ConcurrentMerkleTreeAccount,
changeLogEventBeet,
deserializeAccountCompressionEvent,
SPL_NOOP_PROGRAM_ID,
changeLogEventV1Beet,
} from "../../src";
import {
buildTree,
Tree,
updateTree,
} from "../merkleTree";
import {
assertCMTProperties,
confirmAndLogTx,
execute,
createTreeOnChain,
} from '../utils'
describe("Serde tests", () => {
let offChainTree: Tree;
let cmtKeypair: Keypair;
let payer: Keypair;
let connection: Connection;
let provider: AnchorProvider;
const MAX_SIZE = 64;
const MAX_DEPTH = 14;
beforeEach(async () => {
payer = Keypair.generate();
connection = new Connection("http://localhost:8899", {
commitment: "confirmed",
});
const wallet = new NodeWallet(payer);
provider = new AnchorProvider(connection, wallet, {
commitment: connection.commitment,
skipPreflight: true,
});
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(payer.publicKey, 1e10),
"confirmed"
);
});
describe("ChangeLogEvent tests", () => {
let cmt: PublicKey;
beforeEach(async () => {
[cmtKeypair, offChainTree] = await createTreeOnChain(provider, payer, 0, MAX_DEPTH, MAX_SIZE);
cmt = cmtKeypair.publicKey;
})
it("Can deserialize a ChangeLogEvent", async () => {
const newLeaf = crypto.randomBytes(32);
const txId = await execute(
provider,
[createAppendIx(newLeaf, payer, cmt)],
[payer]
);
updateTree(offChainTree, newLeaf, 0);
const transaction = await connection.getTransaction(txId, {
commitment: "confirmed",
maxSupportedTransactionVersion: 2,
});
// Get noop program instruction
const accountKeys = transaction!.transaction.message.getAccountKeys();
const noopInstruction = transaction!.meta!.innerInstructions![0].instructions[0];
const programId = accountKeys.get(noopInstruction.programIdIndex)!;
if (!programId.equals(SPL_NOOP_PROGRAM_ID)) {
throw Error(`Only inner ix should be a noop, but instead is a ${programId.toBase58()}`)
}
const cpiData = Buffer.from(bs58.decode(noopInstruction.data));
const changeLogEvent = deserializeAccountCompressionEvent(cpiData)
assert(
changeLogEvent.treeId.equals(cmt),
`Tree id in changeLog differs from expected tree ${cmt.toBase58()}`
);
assert(
changeLogEvent.index === 0,
`ChangeLog should have index 0, but has index ${changeLogEvent.index}`
);
assert(
new BN.BN(changeLogEvent.seq).toNumber() === 1,
`ChangeLog should have sequence number of 1, but has seq number of ${changeLogEvent.seq.toString()}`
)
// Check that the emitted ChangeLog path matches up with the updated Tree
let nodeIndex = 0 + (1 << MAX_DEPTH);
let realTreeNode = offChainTree.leaves[0];
while (nodeIndex > 0) {
const clNode = changeLogEvent.path.shift()!;
assert(
nodeIndex === clNode.index,
`Expected changeLog index to be ${nodeIndex} but is ${clNode.index}`
)
assert(
realTreeNode!.node.equals(Buffer.from(clNode.node)),
`ChangeLog node differs at node index: ${clNode.index}`,
);
realTreeNode = realTreeNode.parent!;
nodeIndex = nodeIndex >> 1;
}
});
})
})