solana-program-library/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount...

198 lines
5.5 KiB
TypeScript

import {
Keypair,
Connection,
PublicKey,
LAMPORTS_PER_SOL,
} from '@solana/web3.js';
import { AnchorProvider } from '@project-serum/anchor';
import NodeWallet from '@project-serum/anchor/dist/cjs/nodewallet';
import { assert } from 'chai';
import { emptyNode, MerkleTree } from '../../src/merkle-tree';
import { createTreeOnChain, createEmptyTreeOnChain } from '../utils';
import {
ALL_DEPTH_SIZE_PAIRS,
ConcurrentMerkleTreeAccount,
getConcurrentMerkleTreeAccountSize,
} from '../../src';
async function assertCMTProperties(
onChainCMT: ConcurrentMerkleTreeAccount,
expectedMaxDepth: number,
expectedMaxBufferSize: number,
expectedAuthority: PublicKey,
expectedRoot: Buffer,
expectedCanopyDepth?: number
) {
assert(
onChainCMT.getMaxDepth() === expectedMaxDepth,
`Max depth does not match ${onChainCMT.getMaxDepth()}, expected ${expectedMaxDepth}`
);
assert(
onChainCMT.getMaxBufferSize() === expectedMaxBufferSize,
`Max buffer size does not match ${onChainCMT.getMaxBufferSize()}, expected ${expectedMaxBufferSize}`
);
assert(
onChainCMT.getAuthority().equals(expectedAuthority),
'Failed to write auth pubkey'
);
assert(
onChainCMT.getCurrentRoot().equals(expectedRoot),
'On chain root does not match root passed in instruction'
);
if (expectedCanopyDepth) {
assert(
onChainCMT.getCanopyDepth() === expectedCanopyDepth,
'On chain canopy depth does not match expected canopy depth'
);
}
}
describe('ConcurrentMerkleTreeAccount tests', () => {
// Configure the client to use the local cluster.
let offChainTree: MerkleTree;
let cmtKeypair: Keypair;
let payerKeypair: Keypair;
let payer: PublicKey;
let connection: Connection;
let provider: AnchorProvider;
beforeEach(async () => {
payerKeypair = Keypair.generate();
payer = payerKeypair.publicKey;
connection = new Connection('http://localhost:8899', {
commitment: 'confirmed',
});
const wallet = new NodeWallet(payerKeypair);
provider = new AnchorProvider(connection, wallet, {
commitment: connection.commitment,
skipPreflight: true,
});
await provider.connection.confirmTransaction(
await provider.connection.requestAirdrop(payer, 1e10),
'confirmed'
);
});
describe('Can deserialize a CMTAccount from an on-chain CMT with a single leaf', () => {
const MAX_SIZE = 64;
const MAX_DEPTH = 14;
beforeEach(async () => {
[cmtKeypair, offChainTree] = await createTreeOnChain(
provider,
payerKeypair,
1,
{ maxDepth: MAX_DEPTH, maxBufferSize: MAX_SIZE }
);
});
it('Interpreted on-chain fields correctly', async () => {
const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress(
connection,
cmtKeypair.publicKey,
'confirmed'
);
await assertCMTProperties(
cmt,
MAX_DEPTH,
MAX_SIZE,
payer,
offChainTree.root
);
});
});
describe('Test deserialization for available depth-size pairs', () => {
it('Test all pairs', async () => {
for (const depthSizePair of ALL_DEPTH_SIZE_PAIRS) {
// Airdrop enough SOL to cover tree creation
const size = getConcurrentMerkleTreeAccountSize(
depthSizePair.maxDepth,
depthSizePair.maxBufferSize
);
const rent = await connection.getMinimumBalanceForRentExemption(
size,
'confirmed'
);
const airdropId = await connection.requestAirdrop(
payer,
rent + 5000 * 2
);
await connection.confirmTransaction(airdropId, 'confirmed');
// Create on chain tree
cmtKeypair = await createEmptyTreeOnChain(
provider,
payerKeypair,
depthSizePair
);
const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress(
connection,
cmtKeypair.publicKey,
'confirmed'
);
// Verify it was initialized correctly
await assertCMTProperties(
cmt,
depthSizePair.maxDepth,
depthSizePair.maxBufferSize,
payer,
emptyNode(depthSizePair.maxDepth)
);
}
});
});
describe('Test deserialization for canopy size for depth 30 tree', () => {
it('Test all pairs', async () => {
const maxDepth = 30;
const maxBufferSize = 2048;
for (let canopyDepth = 1; canopyDepth <= 14; canopyDepth++) {
// Airdrop enough SOL to cover tree creation
const size = getConcurrentMerkleTreeAccountSize(
maxDepth,
maxBufferSize,
canopyDepth
);
const rent = await connection.getMinimumBalanceForRentExemption(
size,
'confirmed'
);
const airdropId = await connection.requestAirdrop(
payer,
rent + 5000 * 2
);
await connection.confirmTransaction(airdropId, 'confirmed');
// Create on chain tree
cmtKeypair = await createEmptyTreeOnChain(
provider,
payerKeypair,
{ maxDepth, maxBufferSize },
canopyDepth
);
const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress(
connection,
cmtKeypair.publicKey,
'confirmed'
);
// Verify it was initialized correctly
await assertCMTProperties(
cmt,
maxDepth,
maxBufferSize,
payer,
emptyNode(maxDepth),
canopyDepth
);
}
});
});
});