198 lines
5.5 KiB
TypeScript
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
|
|
);
|
|
}
|
|
});
|
|
});
|
|
});
|