You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am encountering verification issues with both the createVerifyLeafInstruction function from the @solana/spl-account-compression library and the createTransferInstruction function from the @metaplex-foundation/mpl-bubblegum library.
I have implemented a local function to calculate the Merkle tree root and leaf, and the calculations match the proof provided in the tree. However, when using either createVerifyLeafInstruction or createTransferInstruction, the verification fails, indicating that the leaf or root does not match.
Steps to Reproduce
Implement a local function to calculate the Merkle tree root and leaf.
Verify that the local calculations match the provided proof tree.
Use createVerifyLeafInstruction to verify the proof, or createTransferInstruction to perform a transfer operation.
Observe that both functions return errors indicating verification failures.
What I've Done
Created a local function to calculate the Merkle tree root and leaf, ensuring they match the proof data.
Verified the proof using the local implementation, confirming its correctness.
Attempted to use createVerifyLeafInstruction and createTransferInstruction, both of which returned errors.
Expected Behavior
Both createVerifyLeafInstruction and createTransferInstruction should work correctly when the root and proof match the expected values.
Actual Behavior
Both functions fail, returning errors indicating mismatches in the leaf or root, even though local calculations confirm the proof is valid.
Snippets
Here’s the local function I used to calculate the Merkle tree root and verify the proof:
import { PublicKey } from "@solana/web3.js";
import bs58 from "bs58";
import { MerkleTree, MerkleTreeProof } from "../edge/utils";
import * as web3 from "@solana/web3.js";
import { fetchAssetProof, fetchDasAssets } from "../edge/utils";
import { connection } from "../tests/_base";
import { createVerifyLeafInstruction } from "@solana/spl-account-compression";
import { createTransferInstruction } from "@metaplex-foundation/mpl-bubblegum";
import { Asset } from "../edge/types";
import { GraphQLError } from "graphql";
import { Proof } from "../edge/generated";
import {
SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
SPL_NOOP_PROGRAM_ID,
} from "@solana/spl-account-compression";
import { BN } from "@coral-xyz/anchor";
/**
* @param base58Str - The base58-encoded string.
* @returns The decoded Buffer.
*/
function decodeBase58(base58Str: string): Buffer {
return Buffer.from(bs58.decode(base58Str));
}
const secretKeyToKeyPair = (secretKey: string) => {
return web3.Keypair.fromSecretKey(bs58.decode(secretKey));
};
const secretKeyToPublicKey = (secretKey: string) => {
return secretKeyToKeyPair(secretKey).publicKey;
};
/**
* Verifies the Merkle proof.
* @param data - The Merkle tree data from Helius.
* @returns Whether the proof is valid or not.
*/
function verifyHeliusMerkleProof(data: Proof): boolean {
const { root, proof, node_index, leaf } = data;
// Decode the inputs
const decodedRoot = decodeBase58(root);
const decodedLeaf = decodeBase58(leaf);
const decodedProof = proof.map(decodeBase58);
// Create the proof object
const merkleProof: MerkleTreeProof = {
root: decodedRoot,
leaf: decodedLeaf,
leafIndex: Number(node_index),
proof: decodedProof,
};
// Verify the proof
const isValid = MerkleTree.verify(merkleProof.root, merkleProof, true);
console.log(`Is proof valid? ${isValid}`);
return isValid;
}
/**
* Calculate the root from the proof (if verification is not needed).
* @param data - The Merkle tree data from Helius.
* @returns The calculated root as a base58 string.
*/
function calculateRoot(data: Proof): string {
const { proof, node_index, leaf } = data;
// Decode the inputs
const decodedLeaf = decodeBase58(leaf);
const decodedProof = proof.map(decodeBase58);
// Calculate the root
const calculatedRoot = MerkleTree.hashProof({
leaf: decodedLeaf,
leafIndex: Number(node_index),
proof: decodedProof,
root: Buffer.alloc(0), // Placeholder
});
return bs58.encode(calculatedRoot);
}
// Example usage with Helius data
// const heliusData = {
// root: "CDyDYWz85pz3L2VrfHACaFy5ZQeeNHWKKEs4PnsC2ZZ7",
// proof: [
// "DY4JYxvipo95t4vJVFw3aWTpFh23APAvV4ZpB2rTVg7M",
// "PiHTnb2o3DyUUyzxxW3Sq5Z6KQBumyqanxXU75b5DeJ",
// "8MEcfbEBU5VTJycRELCBRb6Vx83xLZsW3UBnJRWBzWnf",
// "1HKxXeZGRptnBjxNzrhV65Ez9EDHRQZgGMNj635eoKm",
// "XHQLXz9F1w4KKwnbUurjK4SDipABUy4saZBNNVfRUqr",
// "Gj51WoA4GmabvHCgX4g2eZs1cT7TjBJLytLPp99HMWcE",
// "5egQR2WEpNMsxVjMAW9XSdzPdZ3RBcZizyBVjQzjdZvx",
// "H8efNp7nfRQe6CUxLm96pvePbQhUsrJjwYdPG1f35SZ1",
// "DJtTArdE9DAbNPr5bf6Hcqe2LjKFhSUTzpvfruRPy8Xi",
// "BxPxMzHLHNk7EHmH2XS38krtHx6UCdwVc9UHNq2EQasD",
// "D9UBcydgqYyTMMuh34AtFJXjy5SQof9W2FF8G9JB4zhm",
// "AAvZnTFwCsjHj12K219EW6sJp351B3yzzmUD3muj6R1S",
// "5hPFGEZubYQriWjiB1nAu2tdLa8HbPax292qPwhbFjpH",
// "9XsA1PzMVeycm9DqoG586vA6PKgLcPaGrtLJuYgtBefs",
// "ESsTwGbxmfN4BMyCQ87SS3fdyfjn4vHQ62nYm9JN1iyc",
// "FjV56x6MT13Y4Zr5pLaH4RWSZKY8hnaBqBnzP8H1C9Wf",
// "GP2ccSDMGyEscNcR6e1SGzdPPYgtYpgxcHZ3seKXpic4",
// "GtJ8SF7pWdb9NUdxm3QxnBnem9KzFdgVNFYwjZpVNF5s",
// "39kHwi5HFPrKCpGg1menD5qY5nFkZeP82o3r3QV5u9SR",
// "D5Rty4haKVQFbuqgLz8bYHBYicQdnheNzUcgickt1mry",
// ],
// node_index: 1310948,
// leaf: "BBjtQRXQNF2mViUu3wQNewnQH6f7gKakiYwuXP23MmyM",
// tree_id: "BXMBo3FKjihFk5F7Mj8UdByNrufmzhfC4Ng6qcUA5VDs",
// };
const shyftData: Proof = {
root: "2v5UkPQwxzCkQozJxKuDpwrvMASCrCxZwVejVAfbeTdj",
proof: [
"DY4JYxvipo95t4vJVFw3aWTpFh23APAvV4ZpB2rTVg7M",
"PiHTnb2o3DyUUyzxxW3Sq5Z6KQBumyqanxXU75b5DeJ",
"8MEcfbEBU5VTJycRELCBRb6Vx83xLZsW3UBnJRWBzWnf",
"1HKxXeZGRptnBjxNzrhV65Ez9EDHRQZgGMNj635eoKm",
"XHQLXz9F1w4KKwnbUurjK4SDipABUy4saZBNNVfRUqr",
"Gj51WoA4GmabvHCgX4g2eZs1cT7TjBJLytLPp99HMWcE",
"5egQR2WEpNMsxVjMAW9XSdzPdZ3RBcZizyBVjQzjdZvx",
"H8efNp7nfRQe6CUxLm96pvePbQhUsrJjwYdPG1f35SZ1",
"oyLn6pYXu1aZyonjbuz6Nhf5o2wq5ihhiENguw7LzzL",
"5okG88hBY4fRhP3nG9aENkdQP7AvPPgWyT3AAUm9dDZu",
"CYes99Ag6kRdKVxi8qfs1Qnd4Vz73HZwMUQJoKAVtL58",
"CU2kc4HjgbXzwUoY2aJwv3WkZ6SwWpyMK3PHsTmMuTfa",
"ErbbNWA7AhsBiwz7gejRyRDfchUfLabKidmfP6qv7Hiw",
"BE7NpZpnjq3i7MayEiK4CQ9vGaNhaaDetyZgbuWDbi1q",
"88GAnAHfR4pAeWkbhozwmtbk5tGHgVdhN6AR8MXbqyvD",
"HFphNMSTH2JgaXY87D7WBv6yNrhrcdU1hk5xkqXK1DEU",
"8mgGPAHc3kLmMBXv3ohHNbBNPqVHt9uBaU82MvdsonLr",
"Fw2mwmpc8h8vKcYrq9hJedt9otrzpNkMXde4jsoZLNnG",
"4bqevQpTCzhJjDR5ZsZALQkFvHST4Xcfi3CQpCgwAKGy",
"G6xXqFURgb15jEe4N18irqn3hakZmc2SB1MA6hTNhe4t",
],
maxDepth: 20,
node_index: 1310948n,
leaf_index: undefined,
leaf: "9AL3ULsY446kVzgfQ9Nu3Nf91jAkXUbg9Ar7LgEtMA2E",
tree_id: "BXMBo3FKjihFk5F7Mj8UdByNrufmzhfC4Ng6qcUA5VDs",
canopy_depth: 0,
};
const driverKeypair = web3.Keypair.fromSecretKey(
bs58.decode(process.env.DRIVER_KEYPAIR)
);
async function compileAndSendTransaction(
action: string,
instructions: web3.TransactionInstruction[],
payerKey: web3.PublicKey,
signers: web3.Signer[]
) {
const { blockhash, lastValidBlockHeight } =
await connection.getLatestBlockhash();
const versionedTx = new web3.VersionedTransaction(
new web3.TransactionMessage({
instructions,
payerKey,
recentBlockhash: blockhash,
}).compileToV0Message()
);
versionedTx.sign(signers);
return connection.simulateTransaction(versionedTx);
}
async function main(mintAddress: string, secretKey?: string) {
const assetsArr = await fetchDasAssets(
{ mintList: [new web3.PublicKey(mintAddress)] },
process.env.RPC_URL
);
const asset = assetsArr[0] as Asset;
const proof = mintAddress ? await fetchAssetProof(mintAddress) : shyftData;
if (!proof) throw new GraphQLError("Could not find asset proof");
console.log("proof", proof);
// Verify the proof
verifyHeliusMerkleProof(proof as any);
// Calculate the root from the proof
const calculatedRoot = calculateRoot(proof);
console.log("Calculated Root:", calculatedRoot);
const t = await compileAndSendTransaction(
`Verify leaf`,
[
createTransferInstruction(
{
compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID,
leafDelegate: secretKey ? secretKeyToPublicKey(secretKey) : driverKeypair.publicKey,
merkleTree: new web3.PublicKey(proof.tree_id),
leafOwner: secretKey ? secretKeyToPublicKey(secretKey) : driverKeypair.publicKey,
logWrapper: SPL_NOOP_PROGRAM_ID,
newLeafOwner: secretKey ? secretKeyToPublicKey(secretKey) : driverKeypair.publicKey,
treeAuthority: new web3.PublicKey(
"BN1pKRWZC9xuDKoaLBqdKfvsQeGQ3fu12QuwunZMKFvY"
),
anchorRemainingAccounts: proof.proof.map((x) => ({
pubkey: new web3.PublicKey(x),
isWritable: false,
isSigner: false,
})),
},
{
root: Array.from(bs58.decode(proof.root)),
index: Number(proof.leaf_index),
creatorHash: Array.from(bs58.decode(asset.compression.creatorHash)),
dataHash: Array.from(bs58.decode(asset.compression.dataHash)),
nonce: new BN((proof.leaf_index || 0).toString()),
}
),
createVerifyLeafInstruction(
{
merkleTree: new web3.PublicKey(proof.tree_id),
anchorRemainingAccounts: proof.proof.map((x) => ({
pubkey: new web3.PublicKey(x),
isWritable: false,
isSigner: false,
})),
},
{
index: Number(proof.leaf_index),
leaf: Array.from(bs58.decode(proof.leaf)),
root: Array.from(bs58.decode(proof.root)),
}
),
],
secretKey ? secretKeyToPublicKey(secretKey) : driverKeypair.publicKey,
secretKey ? [secretKeyToKeyPair(secretKey)] : [driverKeypair]
);
console.log("TTTT", t);
console.dir(asset, { depth: null });
}
main(process.argv[2], process.argv[3]);
Questions
Could there be an issue with how these functions process the proof and inputs?
If I am using these functions incorrectly, could you provide guidance or examples of proper usage?
Thank you for your assistance!
The text was updated successfully, but these errors were encountered:
I am encountering verification issues with both the
createVerifyLeafInstruction
function from the@solana/spl-account-compression
library and thecreateTransferInstruction
function from the@metaplex-foundation/mpl-bubblegum
library.I have implemented a local function to calculate the Merkle tree root and leaf, and the calculations match the proof provided in the tree. However, when using either
createVerifyLeafInstruction
orcreateTransferInstruction
, the verification fails, indicating that the leaf or root does not match.Steps to Reproduce
Implement a local function to calculate the Merkle tree root and leaf.
Verify that the local calculations match the provided proof tree.
Use
createVerifyLeafInstruction
to verify the proof, orcreateTransferInstruction
to perform a transfer operation.Observe that both functions return errors indicating verification failures.
What I've Done
Created a local function to calculate the Merkle tree root and leaf, ensuring they match the proof data.
Verified the proof using the local implementation, confirming its correctness.
Attempted to use
createVerifyLeafInstruction
andcreateTransferInstruction
, both of which returned errors.Expected Behavior
Both
createVerifyLeafInstruction
andcreateTransferInstruction
should work correctly when the root and proof match the expected values.Actual Behavior
Both functions fail, returning errors indicating mismatches in the leaf or root, even though local calculations confirm the proof is valid.
Snippets
Here’s the local function I used to calculate the Merkle tree root and verify the proof:
Questions
Thank you for your assistance!
The text was updated successfully, but these errors were encountered: