From a690aee4a9546b1d2707639e110524924a31d186 Mon Sep 17 00:00:00 2001 From: Jarry Xiao <61092285+jarry-xiao@users.noreply.github.com> Date: Tue, 14 Jun 2022 15:13:17 -0500 Subject: [PATCH] Jank Indexer (#95) Jank Indexer V1 Co-authored-by: Noah Gundotra --- contracts/sdk/gummyroll/accounts/index.ts | 274 ++++++++++++---------- contracts/sdk/gummyroll/index.ts | 17 ++ 2 files changed, 172 insertions(+), 119 deletions(-) diff --git a/contracts/sdk/gummyroll/accounts/index.ts b/contracts/sdk/gummyroll/accounts/index.ts index e111fa276dc..2405caafef1 100644 --- a/contracts/sdk/gummyroll/accounts/index.ts +++ b/contracts/sdk/gummyroll/accounts/index.ts @@ -1,146 +1,182 @@ -import { - PublicKey, - Connection, -} from '@solana/web3.js'; -import * as borsh from 'borsh'; -import { BN } from '@project-serum/anchor'; +import { PublicKey, Connection } from "@solana/web3.js"; +import * as borsh from "borsh"; +import { BN } from "@project-serum/anchor"; import { assert } from "chai"; import { readPublicKey } from '../../utils'; /** * Manually create a model for MerkleRoll in order to deserialize correctly */ -export type OnChainMerkleRoll = { - header: MerkleRollHeader, - roll: MerkleRoll +export class OnChainMerkleRoll { + header: MerkleRollHeader; + roll: MerkleRoll; + + constructor(header: MerkleRollHeader, roll: MerkleRoll) { + this.header = header; + this.roll = roll; + } + + getChangeLogsWithNodeIndex(): PathNode[][] { + const mask = this.header.maxBufferSize - 1; + let pathNodeList = []; + for (let j = 0; j < this.roll.bufferSize; j++) { + let pathNodes = []; + let idx = (this.roll.activeIndex - j) & mask; + let changeLog = this.roll.changeLogs[idx]; + let pathLen = changeLog.pathNodes.length; + for (const [lvl, key] of changeLog.pathNodes.entries()) { + let nodeIdx = (1 << (pathLen - lvl)) + (changeLog.index >> lvl); + pathNodes.push({ + node: key, + index: nodeIdx, + }); + } + pathNodes.push({ + node: changeLog.root, + index: 1, + }); + pathNodeList.push(pathNodes); + } + return pathNodeList; + } } type MerkleRollHeader = { - maxDepth: number, // u32 - maxBufferSize: number, // u32 - authority: PublicKey, - appendAuthority: PublicKey, -} + maxDepth: number; // u32 + maxBufferSize: number; // u32 + authority: PublicKey; + appendAuthority: PublicKey; +}; type MerkleRoll = { - sequenceNumber: BN, // u128 - activeIndex: number, // u64 - bufferSize: number, // u64 - changeLogs: ChangeLog[], - rightMostPath: Path, -} + sequenceNumber: BN; // u128 + activeIndex: number; // u64 + bufferSize: number; // u64 + changeLogs: ChangeLog[]; + rightMostPath: Path; +}; + +export type PathNode = { + node: PublicKey; + index: number; +}; type ChangeLog = { - root: PublicKey, - pathNodes: PublicKey[] - index: number, // u32 - _padding: number, // u32 -} + root: PublicKey; + pathNodes: PublicKey[]; + index: number; // u32 + _padding: number; // u32 +}; type Path = { - leaf: PublicKey, - proof: PublicKey[], - index: number, - _padding: number, + leaf: PublicKey; + proof: PublicKey[]; + index: number; + _padding: number; }; export function decodeMerkleRoll(buffer: Buffer): OnChainMerkleRoll { - let reader = new borsh.BinaryReader(buffer); - - let header: MerkleRollHeader = { - maxBufferSize: reader.readU32(), - maxDepth: reader.readU32(), - authority: readPublicKey(reader), - appendAuthority: readPublicKey(reader) - }; - - // Decode MerkleRoll - let sequenceNumber = reader.readU128(); - let activeIndex = reader.readU64().toNumber(); - let bufferSize = reader.readU64().toNumber(); - - // Decode ChangeLogs - let changeLogs = []; - for (let i = 0; i < header.maxBufferSize; i++) { - let root = readPublicKey(reader); - - let pathNodes = []; - for (let j = 0; j < header.maxDepth; j++) { - pathNodes.push(readPublicKey(reader)); - } - changeLogs.push({ - pathNodes, - root, - index: reader.readU32(), - _padding: reader.readU32(), - }); - } - - // Decode Right-Most Path - let leaf = readPublicKey(reader); - let proof = []; + let reader = new borsh.BinaryReader(buffer); + + let header: MerkleRollHeader = { + maxBufferSize: reader.readU32(), + maxDepth: reader.readU32(), + authority: readPublicKey(reader), + appendAuthority: readPublicKey(reader), + }; + + // Decode MerkleRoll + let sequenceNumber = reader.readU128(); + let activeIndex = reader.readU64().toNumber(); + let bufferSize = reader.readU64().toNumber(); + + // Decode ChangeLogs + let changeLogs = []; + for (let i = 0; i < header.maxBufferSize; i++) { + let root = readPublicKey(reader); + + let pathNodes = []; for (let j = 0; j < header.maxDepth; j++) { - proof.push(readPublicKey(reader)); - } - const rightMostPath = { - proof, - leaf, - index: reader.readU32(), - _padding: reader.readU32(), - } - - const roll = { - sequenceNumber, - activeIndex, - bufferSize, - changeLogs, - rightMostPath - } - - if (getMerkleRollAccountSize(header.maxDepth, header.maxBufferSize) != reader.offset) { - throw new Error("Failed to process whole buffer when deserializing Merkle Account Data") - } - return { - header, - roll + pathNodes.push(readPublicKey(reader)); } + changeLogs.push({ + pathNodes, + root, + index: reader.readU32(), + _padding: reader.readU32(), + }); + } + + // Decode Right-Most Path + let leaf = readPublicKey(reader); + let proof = []; + for (let j = 0; j < header.maxDepth; j++) { + proof.push(readPublicKey(reader)); + } + const rightMostPath = { + proof, + leaf, + index: reader.readU32(), + _padding: reader.readU32(), + }; + + const roll = { + sequenceNumber, + activeIndex, + bufferSize, + changeLogs, + rightMostPath, + }; + + if ( + getMerkleRollAccountSize(header.maxDepth, header.maxBufferSize) != + reader.offset + ) { + throw new Error( + "Failed to process whole buffer when deserializing Merkle Account Data" + ); + } + return new OnChainMerkleRoll(header, roll); } -export function getMerkleRollAccountSize(maxDepth: number, maxBufferSize: number): number { - let headerSize = 8 + 32 + 32; - let changeLogSize = (maxDepth * 32 + 32 + 4 + 4) * maxBufferSize; - let rightMostPathSize = maxDepth * 32 + 32 + 4 + 4; - let merkleRollSize = 8 + 8 + 16 + changeLogSize + rightMostPathSize; - return merkleRollSize + headerSize; +export function getMerkleRollAccountSize( + maxDepth: number, + maxBufferSize: number +): number { + let headerSize = 8 + 32 + 32; + let changeLogSize = (maxDepth * 32 + 32 + 4 + 4) * maxBufferSize; + let rightMostPathSize = maxDepth * 32 + 32 + 4 + 4; + let merkleRollSize = 8 + 8 + 16 + changeLogSize + rightMostPathSize; + return merkleRollSize + headerSize; } export async function assertOnChainMerkleRollProperties( - connection: Connection, - expectedMaxDepth: number, - expectedMaxBufferSize: number, - expectedAuthority: PublicKey, - expectedRoot: PublicKey, - merkleRollPubkey: PublicKey + connection: Connection, + expectedMaxDepth: number, + expectedMaxBufferSize: number, + expectedAuthority: PublicKey, + expectedRoot: PublicKey, + merkleRollPubkey: PublicKey ) { - const merkleRoll = await connection.getAccountInfo(merkleRollPubkey); - const merkleRollAcct = decodeMerkleRoll(merkleRoll.data); - - assert( - merkleRollAcct.header.maxDepth === expectedMaxDepth, - `Max depth does not match ${merkleRollAcct.header.maxDepth}, expected ${expectedMaxDepth}` - ); - assert( - merkleRollAcct.header.maxBufferSize === expectedMaxBufferSize, - `Max buffer size does not match ${merkleRollAcct.header.maxBufferSize}, expected ${expectedMaxBufferSize}` - ); - - assert( - merkleRollAcct.header.authority.equals(expectedAuthority), - "Failed to write auth pubkey" - ); - - assert( - merkleRollAcct.roll.changeLogs[0].root.equals(expectedRoot), - "On chain root does not match root passed in instruction" - ); + const merkleRoll = await connection.getAccountInfo(merkleRollPubkey); + const merkleRollAcct = decodeMerkleRoll(merkleRoll.data); + + assert( + merkleRollAcct.header.maxDepth === expectedMaxDepth, + `Max depth does not match ${merkleRollAcct.header.maxDepth}, expected ${expectedMaxDepth}` + ); + assert( + merkleRollAcct.header.maxBufferSize === expectedMaxBufferSize, + `Max buffer size does not match ${merkleRollAcct.header.maxBufferSize}, expected ${expectedMaxBufferSize}` + ); + + assert( + merkleRollAcct.header.authority.equals(expectedAuthority), + "Failed to write auth pubkey" + ); + + assert( + merkleRollAcct.roll.changeLogs[0].root.equals(expectedRoot), + "On chain root does not match root passed in instruction" + ); } diff --git a/contracts/sdk/gummyroll/index.ts b/contracts/sdk/gummyroll/index.ts index 5218298c8c8..6f9e53887ae 100644 --- a/contracts/sdk/gummyroll/index.ts +++ b/contracts/sdk/gummyroll/index.ts @@ -1,3 +1,20 @@ +import { PublicKey } from '@solana/web3.js'; export * from './instructions'; export * from './accounts'; export * from './types'; + +/** + * Program address + * + * @category constants + * @category generated + */ +export const PROGRAM_ADDRESS = 'GRoLLMza82AiYN7W9S9KCCtCyyPRAQP2ifBy4v4D5RMD' + +/** + * Program public key + * + * @category constants + * @category generated + */ +export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS)