From ed747aea08cf28c52585ff34d7aee7326fb42171 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Wed, 23 Oct 2024 11:18:05 +0100 Subject: [PATCH 1/3] feat(svm): relay root bundle event Signed-off-by: Pablo Maldonado --- programs/svm-spoke/src/event.rs | 7 +++ programs/svm-spoke/src/instructions/admin.rs | 16 +++-- test/svm/SvmSpoke.Bundle.ts | 61 +++++++++++++++----- 3 files changed, 67 insertions(+), 17 deletions(-) diff --git a/programs/svm-spoke/src/event.rs b/programs/svm-spoke/src/event.rs index f11d8f4f4..e4cd38b15 100644 --- a/programs/svm-spoke/src/event.rs +++ b/programs/svm-spoke/src/event.rs @@ -23,6 +23,13 @@ pub struct EnabledDepositRoute { pub enabled: bool, } +#[event] +pub struct RelayRootBundleEvent { + pub relayer_refund_root: [u8; 32], + pub slow_relay_root: [u8; 32], + pub root_bundle_id: u32, +} + // Deposit events #[event] pub struct V3FundsDeposited { diff --git a/programs/svm-spoke/src/instructions/admin.rs b/programs/svm-spoke/src/instructions/admin.rs index 2de555922..8ec36772e 100644 --- a/programs/svm-spoke/src/instructions/admin.rs +++ b/programs/svm-spoke/src/instructions/admin.rs @@ -8,7 +8,9 @@ use crate::{ constants::DISCRIMINATOR_SIZE, constraints::is_local_or_remote_owner, error::CustomError, - event::{EnabledDepositRoute, PausedDeposits, PausedFills, SetXDomainAdmin}, + event::{ + EnabledDepositRoute, PausedDeposits, PausedFills, RelayRootBundleEvent, SetXDomainAdmin, + }, initialize_current_time, state::{RootBundle, Route, State}, }; @@ -209,6 +211,7 @@ pub fn set_enable_route( Ok(()) } +#[event_cpi] #[derive(Accounts)] pub struct RelayRootBundle<'info> { #[account( @@ -241,10 +244,15 @@ pub fn relay_root_bundle( let root_bundle = &mut ctx.accounts.root_bundle; root_bundle.relayer_refund_root = relayer_refund_root; root_bundle.slow_relay_root = slow_relay_root; + + emit_cpi!(RelayRootBundleEvent { + relayer_refund_root, + slow_relay_root, + root_bundle_id: state.root_bundle_id, + }); + + // Finally, increment the root bundle id state.root_bundle_id += 1; - // TODO: add event Ok(()) } - -// TODO: add emergency_delete_root_bundle diff --git a/test/svm/SvmSpoke.Bundle.ts b/test/svm/SvmSpoke.Bundle.ts index 7c2880585..e648633bf 100644 --- a/test/svm/SvmSpoke.Bundle.ts +++ b/test/svm/SvmSpoke.Bundle.ts @@ -101,7 +101,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Try to relay root bundle as non-owner - let relayRootBundleAccounts = { state: state, rootBundle, signer: nonOwner.publicKey }; + let relayRootBundleAccounts = { state: state, rootBundle, signer: nonOwner.publicKey, program: program.programId }; try { await program.methods .relayRootBundle(relayerRefundRootArray, slowRelayRootArray) @@ -114,7 +114,7 @@ describe("svm_spoke.bundle", () => { } // Relay root bundle as owner - relayRootBundleAccounts = { state, rootBundle, signer: owner }; + relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods .relayRootBundle(relayerRefundRootArray, slowRelayRootArray) .accounts(relayRootBundleAccounts) @@ -146,7 +146,7 @@ describe("svm_spoke.bundle", () => { const seeds2 = [Buffer.from("root_bundle"), state.toBuffer(), rootBundleIdBuffer2]; const [rootBundle2] = PublicKey.findProgramAddressSync(seeds2, program.programId); - relayRootBundleAccounts = { state, rootBundle: rootBundle2, signer: owner }; + relayRootBundleAccounts = { state, rootBundle: rootBundle2, signer: owner, program: program.programId }; await program.methods .relayRootBundle(relayerRefundRootArray2, slowRelayRootArray2) .accounts(relayRootBundleAccounts) @@ -155,6 +155,41 @@ describe("svm_spoke.bundle", () => { stateAccountData = await program.account.state.fetch(state); assert.isTrue(stateAccountData.rootBundleId.toString() === "2", "Root bundle index should be 2"); }); + + it("Tests Event Emission in Relay Root Bundle", async () => { + const relayerRefundRootBuffer = crypto.randomBytes(32); + const relayerRefundRootArray = Array.from(relayerRefundRootBuffer); + const slowRelayRootBuffer = crypto.randomBytes(32); + const slowRelayRootArray = Array.from(slowRelayRootBuffer); + + let stateAccountData = await program.account.state.fetch(state); + const rootBundleId = stateAccountData.rootBundleId; + const rootBundleIdBuffer = Buffer.alloc(4); + rootBundleIdBuffer.writeUInt32LE(rootBundleId); + const seeds = [Buffer.from("root_bundle"), state.toBuffer(), rootBundleIdBuffer]; + const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); + + // Relay root bundle as owner + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; + const tx = await program.methods + .relayRootBundle(relayerRefundRootArray, slowRelayRootArray) + .accounts(relayRootBundleAccounts) + .rpc(); + + // Wait for event processing + await new Promise((resolve) => setTimeout(resolve, 1000)); + + // Check for the emitted event + const events = await readEvents(connection, tx, [program]); + const event = events.find((event) => event.name === "relayRootBundleEvent").data; + assert.isTrue(event.rootBundleId.toString() === rootBundleId.toString(), "Root bundle ID should match"); + assert.isTrue( + event.relayerRefundRoot.toString() === relayerRefundRootArray.toString(), + "Relayer refund root should match" + ); + assert.isTrue(event.slowRelayRoot.toString() === slowRelayRootArray.toString(), "Slow relay root should match"); + }); + it("Simple Leaf Refunds Relayers", async () => { const relayerRefundLeaves: RelayerRefundLeafType[] = []; const relayerARefund = new BN(400000); @@ -185,7 +220,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [ @@ -316,7 +351,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [ @@ -457,7 +492,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [ @@ -519,7 +554,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [ @@ -581,7 +616,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [{ pubkey: relayerTA, isWritable: true, isSigner: false }]; @@ -741,7 +776,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); // Verify valid leaf @@ -893,7 +928,7 @@ describe("svm_spoke.bundle", () => { rootBundleIdBuffer.writeUInt32LE(rootBundleId); const seeds = [Buffer.from("root_bundle"), state.toBuffer(), rootBundleIdBuffer]; const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); - let relayRootBundleAccounts = { state, rootBundle, signer: owner }; + let relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const proofAsNumbers = proof.map((p) => Array.from(p)); const executeRelayerRefundLeafAccounts = { @@ -969,7 +1004,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [{ pubkey: relayerTA, isWritable: true, isSigner: false }]; @@ -1043,7 +1078,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); const remainingAccounts = [ @@ -1146,7 +1181,7 @@ describe("svm_spoke.bundle", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); // Pass refund addresses in remaining accounts. From c5c252061434d9e26bb6312f6765b54870148235 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Wed, 23 Oct 2024 16:06:09 +0100 Subject: [PATCH 2/3] fix: ts Signed-off-by: Pablo Maldonado --- test/svm/SvmSpoke.RefundClaims.ts | 2 +- test/svm/SvmSpoke.SlowFill.ts | 2 +- test/svm/SvmSpoke.TokenBridge.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/svm/SvmSpoke.RefundClaims.ts b/test/svm/SvmSpoke.RefundClaims.ts index d08a8e6a4..d6f80a06b 100644 --- a/test/svm/SvmSpoke.RefundClaims.ts +++ b/test/svm/SvmSpoke.RefundClaims.ts @@ -78,7 +78,7 @@ describe("svm_spoke.refund_claims", () => { const [rootBundle] = PublicKey.findProgramAddressSync(seeds, program.programId); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods.relayRootBundle(Array.from(root), Array.from(root)).accounts(relayRootBundleAccounts).rpc(); // Pass claim account as relayer refund address. diff --git a/test/svm/SvmSpoke.SlowFill.ts b/test/svm/SvmSpoke.SlowFill.ts index b8a81ad5f..618694580 100644 --- a/test/svm/SvmSpoke.SlowFill.ts +++ b/test/svm/SvmSpoke.SlowFill.ts @@ -120,7 +120,7 @@ describe("svm_spoke.slow_fill", () => { const relayerRefundRoot = crypto.randomBytes(32); // Relay root bundle - const relayRootBundleAccounts = { state, rootBundle, signer: owner }; + const relayRootBundleAccounts = { state, rootBundle, signer: owner, program: program.programId }; await program.methods .relayRootBundle(Array.from(relayerRefundRoot), Array.from(slowRelayRoot)) .accounts(relayRootBundleAccounts) diff --git a/test/svm/SvmSpoke.TokenBridge.ts b/test/svm/SvmSpoke.TokenBridge.ts index 0b37e135c..49beb1812 100644 --- a/test/svm/SvmSpoke.TokenBridge.ts +++ b/test/svm/SvmSpoke.TokenBridge.ts @@ -161,6 +161,7 @@ describe("svm_spoke.token_bridge", () => { state, rootBundle, signer: owner, + program: program.programId, }; await program.methods .relayRootBundle(Array.from(root), Array.from(Buffer.alloc(32))) From 35b24f786eddadce10ca98b8393c84e560519ae7 Mon Sep 17 00:00:00 2001 From: Pablo Maldonado Date: Wed, 23 Oct 2024 16:28:23 +0100 Subject: [PATCH 3/3] fix: fixes Signed-off-by: Pablo Maldonado --- programs/svm-spoke/src/event.rs | 4 ++-- programs/svm-spoke/src/instructions/admin.rs | 8 +++----- programs/svm-spoke/src/state/state.rs | 10 +++++----- test/svm/SvmSpoke.Bundle.ts | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/programs/svm-spoke/src/event.rs b/programs/svm-spoke/src/event.rs index e4cd38b15..fcf583fb7 100644 --- a/programs/svm-spoke/src/event.rs +++ b/programs/svm-spoke/src/event.rs @@ -24,10 +24,10 @@ pub struct EnabledDepositRoute { } #[event] -pub struct RelayRootBundleEvent { +pub struct RelayedRootBundle { + pub root_bundle_id: u32, pub relayer_refund_root: [u8; 32], pub slow_relay_root: [u8; 32], - pub root_bundle_id: u32, } // Deposit events diff --git a/programs/svm-spoke/src/instructions/admin.rs b/programs/svm-spoke/src/instructions/admin.rs index 8ec36772e..a23e48102 100644 --- a/programs/svm-spoke/src/instructions/admin.rs +++ b/programs/svm-spoke/src/instructions/admin.rs @@ -8,9 +8,7 @@ use crate::{ constants::DISCRIMINATOR_SIZE, constraints::is_local_or_remote_owner, error::CustomError, - event::{ - EnabledDepositRoute, PausedDeposits, PausedFills, RelayRootBundleEvent, SetXDomainAdmin, - }, + event::{EnabledDepositRoute, PausedDeposits, PausedFills, RelayedRootBundle, SetXDomainAdmin}, initialize_current_time, state::{RootBundle, Route, State}, }; @@ -245,10 +243,10 @@ pub fn relay_root_bundle( root_bundle.relayer_refund_root = relayer_refund_root; root_bundle.slow_relay_root = slow_relay_root; - emit_cpi!(RelayRootBundleEvent { + emit_cpi!(RelayedRootBundle { + root_bundle_id: state.root_bundle_id, relayer_refund_root, slow_relay_root, - root_bundle_id: state.root_bundle_id, }); // Finally, increment the root bundle id diff --git a/programs/svm-spoke/src/state/state.rs b/programs/svm-spoke/src/state/state.rs index be8cad3e3..81f5dff9b 100644 --- a/programs/svm-spoke/src/state/state.rs +++ b/programs/svm-spoke/src/state/state.rs @@ -8,11 +8,11 @@ pub struct State { pub owner: Pubkey, pub seed: u64, // Add a seed to the state to enable multiple deployments. pub number_of_deposits: u32, - pub chain_id: u64, // Across definition of chainId for Solana. - pub current_time: u32, // Only used in testable mode, else set to 0 on mainnet. - pub remote_domain: u32, // CCTP domain for Mainnet Ethereum. - pub cross_domain_admin: Pubkey, // HubPool on Mainnet Ethereum. - pub root_bundle_id: u32, + pub chain_id: u64, // Across definition of chainId for Solana. + pub current_time: u32, // Only used in testable mode, else set to 0 on mainnet. + pub remote_domain: u32, // CCTP domain for Mainnet Ethereum. + pub cross_domain_admin: Pubkey, // HubPool on Mainnet Ethereum. + pub root_bundle_id: u32, // TODO rename to next_root_bundle_id pub deposit_quote_time_buffer: u32, // Deposit quote times can't be set more than this amount into the past/future. pub fill_deadline_buffer: u32, // Fill deadlines can't be set more than this amount into the future. } diff --git a/test/svm/SvmSpoke.Bundle.ts b/test/svm/SvmSpoke.Bundle.ts index e648633bf..f71cfca77 100644 --- a/test/svm/SvmSpoke.Bundle.ts +++ b/test/svm/SvmSpoke.Bundle.ts @@ -181,7 +181,7 @@ describe("svm_spoke.bundle", () => { // Check for the emitted event const events = await readEvents(connection, tx, [program]); - const event = events.find((event) => event.name === "relayRootBundleEvent").data; + const event = events.find((event) => event.name === "relayedRootBundle").data; assert.isTrue(event.rootBundleId.toString() === rootBundleId.toString(), "Root bundle ID should match"); assert.isTrue( event.relayerRefundRoot.toString() === relayerRefundRootArray.toString(),