From 0b17bd823e163618dfa7277bdea072bf9646ada9 Mon Sep 17 00:00:00 2001 From: Ondra Chaloupka Date: Mon, 1 Jul 2024 13:43:44 +0200 Subject: [PATCH] [contract] fix for claim settlement on exact match --- .../__tests__/bankrun/claimSettlement.spec.ts | 115 +++++++++++++++++- .../__tests__/utils/merkleTreeTestData.ts | 110 ++++++++++++++--- .../settlement/claim_settlement.rs | 8 +- .../src/merkle_tree_collection.rs | 84 ++++++------- 4 files changed, 248 insertions(+), 69 deletions(-) diff --git a/packages/validator-bonds-sdk/__tests__/bankrun/claimSettlement.spec.ts b/packages/validator-bonds-sdk/__tests__/bankrun/claimSettlement.spec.ts index 3c305a78..bbd4db21 100644 --- a/packages/validator-bonds-sdk/__tests__/bankrun/claimSettlement.spec.ts +++ b/packages/validator-bonds-sdk/__tests__/bankrun/claimSettlement.spec.ts @@ -3,12 +3,14 @@ import { MerkleTreeNode, SETTLEMENT_CLAIM_SEED, ValidatorBondsProgram, + bondsWithdrawerAuthority, claimSettlementInstruction, fundSettlementInstruction, getRentExemptStake, getSettlement, getSettlementClaim, settlementClaimAddress, + settlementStakerAuthority, } from '../../src' import { BankrunExtendedProvider, @@ -36,6 +38,7 @@ import { createSettlementFundedDelegatedStake, createDelegatedStakeAccount, createVoteAccount, + createInitializedStakeAccount, } from '../utils/staking' import { signer, @@ -50,16 +53,19 @@ import { totalClaimVoteAccount1, totalClaimVoteAccount2, treeNodeBy, + treeNodesVoteAccount1, voteAccount1Keypair, voteAccount2Keypair, withdrawer1, withdrawer2, withdrawer3, + withdrawer4, } from '../utils/merkleTreeTestData' import { verifyError } from '@marinade.finance/anchor-common' import BN from 'bn.js' import { executeTxWithError } from '../utils/helpers' import { initBankrunTest } from './bankrun' +import assert from 'assert' describe('Validator Bonds claim settlement', () => { const epochsToClaimSettlement = 4 @@ -85,6 +91,7 @@ describe('Validator Bonds claim settlement', () => { beforeAll(async () => { ;({ provider, program } = await initBankrunTest()) + const epochNow = await currentEpoch(provider) const firstSlotOfEpoch = await getFirstSlotOfEpoch(provider, epochNow) const firstSlotOfNextEpoch = await getFirstSlotOfEpoch( @@ -126,7 +133,9 @@ describe('Validator Bonds claim settlement', () => { voteAccount: voteAccount2, validatorIdentity: validatorIdentity2, })) + }) + async function initVariousTest() { rentCollector = Keypair.generate() settlementEpoch = await currentEpoch(provider) ;({ settlementAccount: settlementAccount1 } = await executeInitSettlement({ @@ -193,9 +202,11 @@ describe('Validator Bonds claim settlement', () => { fundIx2 ) await createWithdrawerUsers(provider) - }) + } it('claim settlement various', async () => { + await initVariousTest() + const treeNode1Withdrawer1 = treeNodeBy(voteAccount1, withdrawer1) const stakeAccountLamportsBefore = 123 * LAMPORTS_PER_SOL const stakeAccountTreeNode1Withdrawer1 = await createDelegatedStakeAccount({ @@ -585,6 +596,108 @@ describe('Validator Bonds claim settlement', () => { assertNotExist(provider, accTooLate) }) + it('claim settlement with exact match on stake account size', async () => { + await warpToNextEpoch(provider) // we want to have different settlement account address + + settlementEpoch = await currentEpoch(provider) + const maxTotalClaim = treeNodesVoteAccount1 + .map(t => t.treeNode.data.claim) + .reduce((a, b) => a.add(b)) + const { settlementAccount } = await executeInitSettlement({ + configAccount, + program, + provider, + voteAccount: voteAccount1, + operatorAuthority, + currentEpoch: settlementEpoch, + merkleRoot: MERKLE_ROOT_VOTE_ACCOUNT_1_BUF, + maxMerkleNodes: treeNodesVoteAccount1.length, + maxTotalClaim, + }) + const [withdrawAuth] = bondsWithdrawerAuthority( + configAccount, + program.programId + ) + const [stakeAuth] = settlementStakerAuthority( + settlementAccount, + program.programId + ) + + const amount1 = LAMPORTS_PER_SOL * 1 + const amount2 = LAMPORTS_PER_SOL * 42 + + const treeNode1Withdrawer4 = treeNodesVoteAccount1.filter(t => + t.treeNode.data.withdrawAuthority.equals(withdrawer4) + ) + expect(treeNode1Withdrawer4.length).toEqual(2) + const treeNode1OneLamport = treeNode1Withdrawer4.find( + t => t.treeNode.data.claim.toNumber() === amount1 + ) + assert(treeNode1OneLamport !== undefined) + const { stakeAccount: stakeAccountOneLamportFrom } = + await createInitializedStakeAccount({ + provider, + rentExempt: amount1, + staker: stakeAuth, + withdrawer: withdrawAuth, + }) + const treeNode42Lamports = treeNode1Withdrawer4.find( + t => t.treeNode.data.claim.toNumber() === amount2 + ) + assert(treeNode42Lamports !== undefined) + const { stakeAccount: stakeAccount42LamportsFrom } = + await createInitializedStakeAccount({ + provider, + rentExempt: amount2, + staker: stakeAuth, + withdrawer: withdrawAuth, + }) + + const stakeAccountOneLamportTo = await createDelegatedStakeAccount({ + provider, + lamports: 10 * LAMPORTS_PER_SOL, + voteAccount: voteAccount1, + staker: treeNode1OneLamport.treeNode.stakeAuthority, + withdrawer: treeNode1OneLamport.treeNode.withdrawAuthority, + }) + const stakeAccount42LamportsTo = await createDelegatedStakeAccount({ + provider, + lamports: 10 * LAMPORTS_PER_SOL, + voteAccount: voteAccount1, + staker: treeNode42Lamports.treeNode.stakeAuthority, + withdrawer: treeNode42Lamports.treeNode.withdrawAuthority, + }) + + // warp to be able to claim (see slotsToStartSettlementClaiming) + warpToNextEpoch(provider) + + const { instruction: ix1 } = await claimSettlementInstruction({ + program, + claimAmount: amount1, + merkleProof: treeNode1OneLamport.proof, + settlementAccount: settlementAccount, + stakeAccountFrom: stakeAccountOneLamportFrom, + stakeAccountTo: stakeAccountOneLamportTo, + stakeAccountStaker: treeNode1OneLamport.treeNode.stakeAuthority, + stakeAccountWithdrawer: treeNode1OneLamport.treeNode.withdrawAuthority, + }) + await provider.sendIx([], ix1) + assertNotExist(provider, stakeAccountOneLamportFrom) + + const { instruction: ix2 } = await claimSettlementInstruction({ + program, + claimAmount: amount2, + merkleProof: treeNode42Lamports.proof, + settlementAccount: settlementAccount, + stakeAccountFrom: stakeAccount42LamportsFrom, + stakeAccountTo: stakeAccount42LamportsTo, + stakeAccountStaker: treeNode42Lamports.treeNode.stakeAuthority, + stakeAccountWithdrawer: treeNode42Lamports.treeNode.withdrawAuthority, + }) + await provider.sendIx([], ix2) + assertNotExist(provider, stakeAccount42LamportsFrom) + }) + async function warpToNotBeClaimable() { await warpOffsetEpoch(provider, epochsToClaimSettlement + 1) } diff --git a/packages/validator-bonds-sdk/__tests__/utils/merkleTreeTestData.ts b/packages/validator-bonds-sdk/__tests__/utils/merkleTreeTestData.ts index 7499dfcb..a1cc1409 100644 --- a/packages/validator-bonds-sdk/__tests__/utils/merkleTreeTestData.ts +++ b/packages/validator-bonds-sdk/__tests__/utils/merkleTreeTestData.ts @@ -5,20 +5,6 @@ import BN from 'bn.js' import { ExtendedProvider } from '@marinade.finance/web3js-common' import { createUserAndFund } from '@marinade.finance/web3js-common' -export const MERKLE_PROOF_VOTE_ACCOUNT_1 = - 'EnBJg4qV4GjH3Sgigsi8wkWz966QYgSQkgPMCmWto51f' -export const MERKLE_ROOT_VOTE_ACCOUNT_1_BUF = bs58.decode( - MERKLE_PROOF_VOTE_ACCOUNT_1 -) -export const MERKLE_PROOF_VOTE_ACCOUNT_2 = - 'Asi9uVpB3Tx29L17X3Z46jrPizKywTRttqsvnLTzgh27' -export const MERKLE_ROOT_VOTE_ACCOUNT_2_BUF = bs58.decode( - MERKLE_PROOF_VOTE_ACCOUNT_2 -) -export const MERKLE_PROOF_OPERATOR = - 'D8rFThGJXYVFcKdqovz3VMA1nALNugHzvGYhSn8dLwip' -export const MERKLE_ROOT_VOTE_OPERATOR_BUF = bs58.decode(MERKLE_PROOF_OPERATOR) - export const configAccount = new PublicKey( '4wQELTA1RMEM3cKN7gjbiNN247e3GY9Sga7MKpNV38kL' ) @@ -87,6 +73,18 @@ export const withdrawer3Keypair = Keypair.fromSecretKey( 101, 205, 13, 212, 139, 234, 174, 137, 193, 203, 120, 62, 72, 48, 54, ]) ) +export const withdrawer4 = new PublicKey( + 'DdWhr91hqajDZRaRVt4QhD5yJasjmyeweST5VUbfCKGy' +) +export const withdrawer4Keypair = Keypair.fromSecretKey( + new Uint8Array([ + 137, 198, 40, 27, 37, 227, 249, 231, 34, 199, 32, 244, 110, 23, 214, 53, 74, + 169, 123, 60, 47, 124, 240, 31, 152, 202, 22, 22, 219, 120, 37, 14, 187, + 166, 189, 44, 111, 242, 7, 250, 248, 14, 163, 244, 255, 202, 153, 170, 45, + 159, 43, 102, 71, 254, 58, 222, 149, 1, 233, 215, 141, 139, 98, 62, + ]) +) + export const staker1 = new PublicKey( '82ewSU2zNH87PajZHf7betFbZAaGR8bwDp8azSHNCAnA' ) @@ -126,6 +124,11 @@ export type MerkleTreeNodeWithProof = { proof: number[][] } +export const MERKLE_PROOF_VOTE_ACCOUNT_1 = + '6H5xisVj8r1aYRX2B2PyeG62ofF9aUiy8qHzwwkJCqqH' +export const MERKLE_ROOT_VOTE_ACCOUNT_1_BUF = bs58.decode( + MERKLE_PROOF_VOTE_ACCOUNT_1 +) export const ITEMS_VOTE_ACCOUNT_1: MerkleTreeNodeWithProof[] = [ { // tree node hash: 3tSbFBfFg83LCgVneuENUFs8hKgsdTKvfVV6Cqz3q6RT @@ -140,9 +143,12 @@ export const ITEMS_VOTE_ACCOUNT_1: MerkleTreeNodeWithProof[] = [ 108, 104, 68, 176, 233, 152, 64, 34, 167, 84, 90, 65, 102, 170, 109, ], [ - 84, 75, 193, 1, 167, 55, 248, 48, 129, 33, 198, 240, 33, 229, 57, 27, - 194, 110, 52, 184, 244, 142, 198, 188, 161, 150, 177, 49, 26, 123, 214, - 187, + 242, 32, 26, 226, 118, 158, 156, 230, 202, 164, 42, 249, 57, 87, 29, 89, + 247, 47, 67, 135, 233, 170, 92, 204, 187, 9, 203, 71, 176, 249, 129, 21, + ], + [ + 100, 183, 165, 4, 15, 25, 171, 235, 171, 51, 238, 200, 78, 13, 144, 57, + 166, 114, 241, 15, 80, 249, 164, 234, 94, 171, 12, 64, 164, 69, 112, 50, ], ], }, @@ -159,9 +165,12 @@ export const ITEMS_VOTE_ACCOUNT_1: MerkleTreeNodeWithProof[] = [ 55, 244, 31, 206, 177, 91, 206, 203, 184, 48, 99, 76, 163, 203, 232, 44, ], [ - 84, 75, 193, 1, 167, 55, 248, 48, 129, 33, 198, 240, 33, 229, 57, 27, - 194, 110, 52, 184, 244, 142, 198, 188, 161, 150, 177, 49, 26, 123, 214, - 187, + 242, 32, 26, 226, 118, 158, 156, 230, 202, 164, 42, 249, 57, 87, 29, 89, + 247, 47, 67, 135, 233, 170, 92, 204, 187, 9, 203, 71, 176, 249, 129, 21, + ], + [ + 100, 183, 165, 4, 15, 25, 171, 235, 171, 51, 238, 200, 78, 13, 144, 57, + 166, 114, 241, 15, 80, 249, 164, 234, 94, 171, 12, 64, 164, 69, 112, 50, ], ], }, @@ -172,6 +181,30 @@ export const ITEMS_VOTE_ACCOUNT_1: MerkleTreeNodeWithProof[] = [ stakeAuthority: staker2, claim: 212121, }), + proof: [ + [ + 153, 210, 192, 197, 37, 37, 63, 137, 37, 158, 9, 107, 244, 72, 195, 21, + 115, 61, 19, 35, 188, 142, 139, 64, 251, 216, 66, 116, 222, 158, 212, + 34, + ], + [ + 146, 196, 239, 63, 54, 200, 90, 234, 50, 1, 61, 217, 219, 111, 207, 131, + 119, 168, 107, 251, 218, 240, 133, 67, 116, 40, 11, 109, 116, 34, 154, + 73, + ], + [ + 100, 183, 165, 4, 15, 25, 171, 235, 171, 51, 238, 200, 78, 13, 144, 57, + 166, 114, 241, 15, 80, 249, 164, 234, 94, 171, 12, 64, 164, 69, 112, 50, + ], + ], + }, + { + // tree node hash: CzTkYPYkzXstjNTaqPfWMQDQESnPDrRBK2SjRac8kDYs + treeNode: new MerkleTreeNode({ + withdrawAuthority: withdrawer4, + stakeAuthority: staker2, + claim: LAMPORTS_PER_SOL, + }), proof: [ [ 166, 246, 173, 43, 141, 45, 116, 63, 47, 72, 233, 142, 194, 147, 46, 95, @@ -183,9 +216,42 @@ export const ITEMS_VOTE_ACCOUNT_1: MerkleTreeNodeWithProof[] = [ 119, 168, 107, 251, 218, 240, 133, 67, 116, 40, 11, 109, 116, 34, 154, 73, ], + [ + 100, 183, 165, 4, 15, 25, 171, 235, 171, 51, 238, 200, 78, 13, 144, 57, + 166, 114, 241, 15, 80, 249, 164, 234, 94, 171, 12, 64, 164, 69, 112, 50, + ], + ], + }, + { + // tree node hash: GFn1wci7vwUfPgq4C9vYRYRn8UgTE1i2qVdE3raabvoK + treeNode: new MerkleTreeNode({ + withdrawAuthority: withdrawer4, + stakeAuthority: staker3, + claim: 42 * LAMPORTS_PER_SOL, + }), + proof: [ + [ + 65, 32, 94, 245, 186, 250, 39, 41, 113, 203, 105, 228, 195, 14, 19, 171, + 171, 3, 51, 176, 218, 36, 182, 71, 65, 5, 255, 231, 202, 30, 200, 169, + ], + [ + 145, 10, 181, 241, 68, 0, 136, 90, 81, 187, 131, 86, 95, 194, 24, 193, + 160, 42, 141, 227, 199, 253, 222, 96, 80, 73, 248, 1, 109, 42, 6, 62, + ], + [ + 174, 148, 230, 241, 186, 94, 91, 190, 170, 56, 66, 207, 104, 237, 226, + 1, 103, 251, 185, 246, 22, 235, 238, 29, 118, 132, 60, 5, 61, 15, 25, + 43, + ], ], }, ] + +export const MERKLE_PROOF_VOTE_ACCOUNT_2 = + 'Asi9uVpB3Tx29L17X3Z46jrPizKywTRttqsvnLTzgh27' +export const MERKLE_ROOT_VOTE_ACCOUNT_2_BUF = bs58.decode( + MERKLE_PROOF_VOTE_ACCOUNT_2 +) export const ITEMS_VOTE_ACCOUNT_2: MerkleTreeNodeWithProof[] = [ { // tree node hash: 2niLq4dRayu3GE5KWuBUR4hAjSikubd1hmGKLJ56ZzUP @@ -217,6 +283,10 @@ export const ITEMS_VOTE_ACCOUNT_2: MerkleTreeNodeWithProof[] = [ ], }, ] + +export const MERKLE_PROOF_OPERATOR = + 'D8rFThGJXYVFcKdqovz3VMA1nALNugHzvGYhSn8dLwip' +export const MERKLE_ROOT_VOTE_OPERATOR_BUF = bs58.decode(MERKLE_PROOF_OPERATOR) export const ITEMS_OPERATOR: MerkleTreeNodeWithProof[] = [ { // tree node hash: C8ZfYuKidJa8EGF4YF5Xou3icvqqGQ6fJBE6SN3ixT1w diff --git a/programs/validator-bonds/src/instructions/settlement/claim_settlement.rs b/programs/validator-bonds/src/instructions/settlement/claim_settlement.rs index 303e6263..24761820 100644 --- a/programs/validator-bonds/src/instructions/settlement/claim_settlement.rs +++ b/programs/validator-bonds/src/instructions/settlement/claim_settlement.rs @@ -213,11 +213,13 @@ impl<'info> ClaimSettlement<'info> { )?; // The provided stake account must be sufficiently large to cover the claim while remaining valid. - // It is the SDK's responsibility to merge stake accounts if necessary. + // It is the caller's responsibility to merge stake accounts if necessary. // - The invariant is that the stake account will always be rent-exempt and of minimum size. // This must be ensured by the fund_settlement instruction. if ctx.accounts.stake_account_from.get_lamports() - < claim + minimal_size_stake_account(&stake_from_meta, &ctx.accounts.config) + < claim + minimal_size_stake_account(&stake_from_meta, &ctx.accounts.config) && + // on perfect match when stake account lamports is equal to the claim amount we can withdraw all + ctx.accounts.stake_account_from.get_lamports() != claim { return Err(error!(ErrorCode::ClaimingStakeAccountLamportsInsufficient) .with_account_name("stake_account_from") @@ -240,7 +242,7 @@ impl<'info> ClaimSettlement<'info> { ) { return Err(error!(ErrorCode::ClaimSettlementProofFailed).with_values(( "Merkle proof verification failed", - format!("Tree node: {:?}", tree_node), + format!("Tree node: {:?}, hash: {:?}", tree_node, tree_node_hash), ))); } diff --git a/settlement-engine/src/merkle_tree_collection.rs b/settlement-engine/src/merkle_tree_collection.rs index 45496f50..7485ac8e 100644 --- a/settlement-engine/src/merkle_tree_collection.rs +++ b/settlement-engine/src/merkle_tree_collection.rs @@ -106,6 +106,7 @@ mod tests { use super::*; use solana_sdk::bs58; use solana_sdk::hash::hashv; + use solana_sdk::native_token::LAMPORTS_PER_SOL; use solana_sdk::pubkey::Pubkey; use std::str::FromStr; @@ -136,78 +137,69 @@ mod tests { // TS cross-check constant test #[test] pub fn ts_cross_check_merkle_proof() { + let staker1 = Pubkey::from_str("82ewSU2zNH87PajZHf7betFbZAaGR8bwDp8azSHNCAnA").unwrap(); + let staker2 = Pubkey::from_str("yrWTX1AuJRqziVpdhg3eAWYhDcY6z1kmEaG4sn1uDDj").unwrap(); + let staker3 = Pubkey::from_str("121WqnefAgXvLZdW42LsGUbkFjv7LVUqvcpkskxyVgeu").unwrap(); + let withdrawer1 = Pubkey::from_str("3vGstFWWyQbDknu9WKr9vbTn2Kw5qgorP7UkRXVrfe9t").unwrap(); + let withdrawer2 = Pubkey::from_str("DBnWKq1Ln9y8HtGwYxFMqMWLY1Ld9xpB28ayKfHejiTs").unwrap(); + let withdrawer3 = Pubkey::from_str("CgoqXy3e1hsnuNw6bJ8iuzqZwr93CA4jsRa1AnsseJ53").unwrap(); + let withdrawer4 = Pubkey::from_str("DdWhr91hqajDZRaRVt4QhD5yJasjmyeweST5VUbfCKGy").unwrap(); let mut items_vote_account1: Vec = vec![ TreeNode { - stake_authority: Pubkey::from_str("82ewSU2zNH87PajZHf7betFbZAaGR8bwDp8azSHNCAnA") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "3vGstFWWyQbDknu9WKr9vbTn2Kw5qgorP7UkRXVrfe9t", - ) - .unwrap(), + stake_authority: staker1, + withdraw_authority: withdrawer1, claim: 1234, proof: None, }, TreeNode { - stake_authority: Pubkey::from_str("82ewSU2zNH87PajZHf7betFbZAaGR8bwDp8azSHNCAnA") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "DBnWKq1Ln9y8HtGwYxFMqMWLY1Ld9xpB28ayKfHejiTs", - ) - .unwrap(), + stake_authority: staker1, + withdraw_authority: withdrawer2, claim: 99999, proof: None, }, TreeNode { - stake_authority: Pubkey::from_str("yrWTX1AuJRqziVpdhg3eAWYhDcY6z1kmEaG4sn1uDDj") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "CgoqXy3e1hsnuNw6bJ8iuzqZwr93CA4jsRa1AnsseJ53", - ) - .unwrap(), + stake_authority: staker2, + withdraw_authority: withdrawer3, claim: 212121, proof: None, }, + TreeNode { + stake_authority: staker2, + withdraw_authority: withdrawer4, + claim: LAMPORTS_PER_SOL, + proof: None, + }, + TreeNode { + stake_authority: staker3, + withdraw_authority: withdrawer4, + claim: LAMPORTS_PER_SOL * 42, + proof: None, + }, ]; let mut items_vote_account2: Vec = vec![ TreeNode { - stake_authority: Pubkey::from_str("yrWTX1AuJRqziVpdhg3eAWYhDcY6z1kmEaG4sn1uDDj") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "3vGstFWWyQbDknu9WKr9vbTn2Kw5qgorP7UkRXVrfe9t", - ) - .unwrap(), + stake_authority: staker2, + withdraw_authority: withdrawer1, claim: 69, proof: None, }, TreeNode { - stake_authority: Pubkey::from_str("121WqnefAgXvLZdW42LsGUbkFjv7LVUqvcpkskxyVgeu") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "DBnWKq1Ln9y8HtGwYxFMqMWLY1Ld9xpB28ayKfHejiTs", - ) - .unwrap(), + stake_authority: staker3, + withdraw_authority: withdrawer2, claim: 111111, proof: None, }, ]; let mut items_operator: Vec = vec![ TreeNode { - stake_authority: Pubkey::from_str("yrWTX1AuJRqziVpdhg3eAWYhDcY6z1kmEaG4sn1uDDj") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "DBnWKq1Ln9y8HtGwYxFMqMWLY1Ld9xpB28ayKfHejiTs", - ) - .unwrap(), + stake_authority: staker2, + withdraw_authority: withdrawer2, claim: 556677, proof: None, }, TreeNode { - stake_authority: Pubkey::from_str("121WqnefAgXvLZdW42LsGUbkFjv7LVUqvcpkskxyVgeu") - .unwrap(), - withdraw_authority: Pubkey::from_str( - "CgoqXy3e1hsnuNw6bJ8iuzqZwr93CA4jsRa1AnsseJ53", - ) - .unwrap(), + stake_authority: staker3, + withdraw_authority: withdrawer3, claim: 996677, proof: None, }, @@ -226,12 +218,13 @@ mod tests { ); assert_eq!( merkle_tree_vote_account1_root.to_string(), - "EnBJg4qV4GjH3Sgigsi8wkWz966QYgSQkgPMCmWto51f" + "6H5xisVj8r1aYRX2B2PyeG62ofF9aUiy8qHzwwkJCqqH" ); for (i, tree_node) in items_vote_account1.iter_mut().enumerate() { tree_node.proof = Some(get_proof(&merkle_tree_vote_account1, i)); println!( - "vote account1: proof: {:?}, hash tree node: {}", + "vote account1[claim:{}]: proof: {:?}, hash tree node: {}", + tree_node.claim, tree_node.proof, tree_node.hash() ) @@ -259,7 +252,8 @@ mod tests { for (i, tree_node) in items_vote_account2.iter_mut().enumerate() { tree_node.proof = Some(get_proof(&merkle_tree_vote_account2, i)); println!( - "vote account2: proof: {:?}, hash tree node: {}", + "vote account2[claim:{}]: proof: {:?}, hash tree node: {}", + tree_node.claim, tree_node.proof, tree_node.hash() )