Skip to content

Commit

Permalink
feat(solana): rm dispatch_slot in favour of dispatch_root within debr…
Browse files Browse the repository at this point in the history
…idge_reporter and rm slots
  • Loading branch information
allemanfredi committed Dec 18, 2024
1 parent 3f611be commit a1b35a0
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 98 deletions.
9 changes: 1 addition & 8 deletions packages/solana/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions packages/solana/programs/debridge-reporter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ idl-build = ["anchor-lang/idl-build"]
[dependencies]
anchor-lang = "0.30.1"
debridge-solana-sdk = { git = "ssh://[email protected]/debridge-finance/debridge-solana-sdk.git", version = "1.0.2" }
slots = { path = "../../shared/slots" }
message = { path = "../../shared/message" }
message = { path = "../../shared/message" }
snapshotter = { path = "../snapshotter", default-features = false, features = ["cpi"] }
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use anchor_lang::prelude::*;

use crate::{error::ErrorCode, state::Config};

#[derive(Accounts)]
pub struct DispatchRoot<'info> {
/// The `config` account holds the snapshotter's configuration and state.
///
/// - **PDA Derivation**: The account is a PDA derived using the `Config::SEED_PREFIX` as the seed. This ensures
/// that the account address is uniquely identified and securely accessed by the program.
///
/// - **Bump Seed**: The `bump` attribute is used to derive the PDA alongside the seeds. It guarantees that the PDA
/// is valid and helps prevent collisions with other accounts.
///
/// - **Read-Only**: The absence of the `mut` keyword indicates that this account will not be modified during
/// the instruction execution.
#[account(
seeds = [Config::SEED_PREFIX],
bump,
)]
/// Config account.
pub config: Account<'info, Config>,

/// The `snapshotter_config` account represents the snapshotter's configuration.
///
/// - **Mutability**: Marked as `mut` to allow modifications during the instruction execution.
/// - **Address Constraint**: Ensures that the provided `snapshotter_config` account matches the
/// `snapshotter_config` specified within the `config` account. If there is a mismatch,
/// the program will throw the `InvalidSnapshotterConfig` error.
///
/// - **CHECK Annotation**:
/// - **Purpose**: Indicates to Anchor that this account requires manual validation.
/// - **Reason**: Since `UncheckedAccount` bypasses Anchor's type and ownership checks, the developer
/// must ensure that the account is valid and correctly matches the expected configuration.
///
/// ## Security Considerations:
/// - **Manual Validation**: Developers must ensure that the `snapshotter_config` account is trustworthy
/// and correctly corresponds to the one stored in `config`. Failing to do so can lead to unauthorized
/// modifications or inconsistencies in the program's state.
///
/// ## Error Handling:
/// - **`ErrorCode::InvalidSnapshotterConfig`**:
/// - **Trigger**: Thrown when the `snapshotter_config` account's public key does not match the one specified in `config`.
/// - **Purpose**: Prevents unauthorized or incorrect accounts from being used, ensuring the integrity of the snapshotter's configuration.
#[account(
mut,
address = config.snapshotter_config @ ErrorCode::InvalidSnapshotterConfig
)]
/// CHECK: Snapshotter program
pub snapshotter_config: UncheckedAccount<'info>,
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use anchor_lang::prelude::*;

use crate::state::Config;

#[derive(Accounts)]
pub struct Initialize<'info> {
/// The `owner` is a Signer account, indicating that whoever calls this instruction
/// must sign the transaction with their private key. This ensures that the initializer
/// has authority.
///
/// `#[account(mut)]` is used here because the `owner` account's lamports are used
/// to fund the creation of the `config` account.
///
/// ## Attributes
///
/// - **`mut`**: Marks the `owner` account as mutable, allowing its lamports to be debited.
#[account(mut)]
/// Whoever initializes the config will be the owner of the program. Signer
/// for creating the [`Config`] account.
pub owner: Signer<'info>,

/// The `config` account will be created (initialized) by this instruction.
///
/// The `init` attribute indicates that this account should be created (allocated and assigned)
/// and initialized with the given seeds, bump, and space.
///
/// - **`payer = owner`**: The `owner` account pays for the rent and creation cost of this account.
/// - **`seeds = [Config::SEED_PREFIX]`**: Sets the PDA (program-derived address) seed that
/// uniquely identifies the `config` account. This ensures that the correct PDA is referenced.
/// - **`bump`**: Used in combination with the seeds to find the PDA. The `bump` ensures a valid PDA
/// that is not already taken by another account.
/// - **`space = Config::MAXIMUM_SIZE`**: Allocates the appropriate amount of space for the `config` account's data based on the `Config` structure.
///
/// ## Attributes
///
/// - **`init`**: Indicates that this account should be initialized by the instruction.
/// - **`payer = owner`**: Specifies that the `owner` account will pay for the account's rent and creation.
/// - **`seeds = [Config::SEED_PREFIX]`**: Defines the seed used to derive the PDA for the `config` account.
/// - **`bump`**: Provides the bump seed necessary for PDA derivation.
/// - **`space = Config::MAXIMUM_SIZE`**: Allocates sufficient space for the `config` account's data.
#[account(
init,
payer = owner,
seeds = [Config::SEED_PREFIX],
bump,
space = Config::MAXIMUM_SIZE,
)]
/// Config account, which saves program data useful for other instructions.
///
/// It is created on initialization, ensuring that all subsequent operations
/// have a consistent place to store relevant configuration information.
pub config: Account<'info, Config>,

/// The `snapshotter_config` account represents the snapshotter's configuration.
///
/// - **UncheckedAccount**: The `snapshotter_config` is marked as `UncheckedAccount`, meaning that Anchor will
/// not perform any type or ownership checks on this account. It is the developer's responsibility to ensure
/// that this account is valid and correctly matches the expected configuration.
///
/// - **Purpose**: Represents the snapshotter's configuration. Although marked as `UncheckedAccount`, it should be correctly associated with the `Config` account to maintain program integrity.
/// CHECK: Snapshotter program
pub snapshotter_config: UncheckedAccount<'info>,

/// The `system_program` is the program responsible for creating and allocating accounts.
///
/// It is necessary whenever new accounts need to be created and funded.
///
/// ## Attributes
///
/// - **Standard System Program**: Required here to create and fund the `config` account.
pub system_program: Program<'info, System>,
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod dispatch_slot;
pub mod dispatch_root;
pub mod initialize;

pub use dispatch_slot::*;
pub use dispatch_root::*;
pub use initialize::*;
12 changes: 12 additions & 0 deletions packages/solana/programs/debridge-reporter/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use anchor_lang::prelude::error_code;

#[error_code]
pub enum ErrorCode {
#[msg("InvalidSnapshotterConfig")]
/// Invalid Snapshotter config
InvalidSnapshotterConfig,

#[msg("RootNotFinalized")]
/// Snapshotter root not finalized
RootNotFinalized,
}
83 changes: 75 additions & 8 deletions packages/solana/programs/debridge-reporter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,94 @@
use anchor_lang::prelude::*;

declare_id!("7GPwvvhq33fTYuDyfA1JVz4fp3XkdcmjerJWpUkxjrC2");
declare_id!("DkJFdedMeprFUzymuZpvXCTqcUt1zc8uz45UgajsKbWH");

pub mod contexts;
pub mod error;
pub mod state;

pub use contexts::*;
pub use error::ErrorCode;
pub use state::*;

#[program]
pub mod debridge_reporter {
use super::*;
use debridge_solana_sdk::sending;
use message::Message;
use slots::get_slot;
use snapshotter::state::Config as SnapshotterConfig;

pub fn dispatch_slot(
ctx: Context<DispatchSlot>,
/// Initializes the `snapshotter_config` within the `Config` account.
///
/// This function sets the `snapshotter_config` field in the `Config` account to point to the `Config` account's own public key.
/// This establishes a direct reference, ensuring that the `snapshotter_config` is correctly linked.
///
/// ## Arguments
///
/// - `ctx`: The context containing the `Initialize` accounts.
///
/// ## Returns
///
/// - `Result<()>`: Returns `Ok(())` if successful, or an error otherwise.
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let config = &mut ctx.accounts.config;

// Set the `snapshotter_config` field in the `Config` account to the public key of the `Config` account itself.
// This creates a self-reference, establishing a direct link to the snapshotter's configuration.
config.snapshotter_config = ctx.accounts.snapshotter_config.key();
Ok(())
}

/// Dispatches a slot by sending a message via DeBridge to a target chain.
///
/// This function performs the following steps:
/// 1. Deserializes the `snapshotter_config` data to access the current configuration.
/// 2. Checks if the current `root` is finalized. If not, it returns an error.
/// 3. Creates a DeBridge `Message` containing the `nonce` and `root`.
/// 4. Serializes the `Message` into a byte payload.
/// 5. Invokes the DeBridge `send_message` function to dispatch the message to the target chain.
///
/// ## Arguments
///
/// - `ctx`: The context containing the `DispatchRoot` accounts.
/// - `target_chain_id`: A 32-byte identifier representing the target blockchain where the message should be sent.
/// - `receiver`: A vector of bytes representing the receiver's address on the target chain.
///
/// ## Returns
///
/// - `Result<()>`: Returns `Ok(())` if the message is successfully dispatched, or an error otherwise.
pub fn dispatch_root(
ctx: Context<DispatchRoot>,
target_chain_id: [u8; 32],
receiver: Vec<u8>,
slot_number: u64,
) -> Result<()> {
let slot_hash = get_slot(&ctx.accounts.slot_hashes, slot_number)?;
let message = Message::from((slot_number, slot_hash));
// Deserialize the snapshotter configuration data from the `snapshotter_config` account.
// This allows access to fields like `nonce` and `root`.
let mut data_slice: &[u8] = &ctx.accounts.snapshotter_config.data.borrow();
let snapshotter_config: SnapshotterConfig =
AccountDeserialize::try_deserialize(&mut data_slice)?;

// Ensure that the `root` has been finalized before proceeding.
// If the root is not finalized, return an error to prevent dispatching incomplete or unstable data.
if !snapshotter_config.root_finalized {
return Err(error!(ErrorCode::RootNotFinalized));
}

// Create a DeBridge `Message` from the snapshotter's `nonce` and `root`.
// This message serves as a data payload to be sent to the target chain.
let message = Message::from((snapshotter_config.nonce, snapshotter_config.root));

// Serialize the `Message` into a byte vector (`payload`) for transmission.
let mut payload: Vec<u8> = Vec::new();
message.serialize(&mut payload)?;

// Invoke the DeBridge `send_message` function to dispatch the serialized message.
// Parameters:
// - `payload`: The serialized message to be sent.
// - `target_chain_id`: The identifier of the target blockchain.
// - `receiver`: The address on the target chain that should receive the message.
// - `0`: Execution fee. A value of `0` typically means that the fee is automatically handled or claimed.
// - `vec![0u8; 32]`: Fallback address. This can be used in case the message fails to be processed on the target chain.
// - `ctx.remaining_accounts`: Any additional accounts required for the DeBridge CPI (Cross-Program Invocation).
sending::invoke_send_message(
payload,
target_chain_id,
Expand All @@ -31,7 +97,8 @@ pub mod debridge_reporter {
vec![0u8; 32], // fallback address
ctx.remaining_accounts,
)
.map_err(ProgramError::from)?;
.map_err(ProgramError::from)?; // Convert any errors from the CPI into Anchor's error type.

Ok(())
}
}
42 changes: 42 additions & 0 deletions packages/solana/programs/debridge-reporter/src/state/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use anchor_lang::prelude::*;

#[account]
#[derive(Default)]
pub struct Config {
/// Snapshotter config address.
///
/// - **Type**: `Pubkey`
/// - **Purpose**:
/// - Stores the public key of the `snapshotter_config` account.
/// - This address is used to reference and interact with the snapshotter's configuration.
/// - Ensures that the program can locate and validate the snapshotter's configuration during operations.
pub snapshotter_config: Pubkey,
}

impl Config {
/// `MAXIMUM_SIZE` defines the total byte size allocated for the `Config` account.
///
/// Anchor requires specifying the exact account size to allocate storage on the blockchain.
/// This constant ensures that the account is allocated enough space for all its fields.
///
/// **Breakdown of `MAXIMUM_SIZE`:**
/// - `8` bytes: Account discriminator (used by Anchor to identify account types).
/// - `32` bytes: The `snapshotter_config` field, which is a `Pubkey` (32 bytes).
///
/// **Total `MAXIMUM_SIZE`**: `8 + 32 = 40` bytes.
///
/// **Note**: Currently, the `Config` struct contains only one field. If additional fields are added in the future,
/// update the `MAXIMUM_SIZE` accordingly to accommodate the new data.
pub const MAXIMUM_SIZE: usize = 8 // discriminator
+ 32; // snapshotter_config

/// `SEED_PREFIX` is a static byte array used as a seed for deriving the Program Derived Address (PDA) of the `Config` account.
///
/// - **Type**: `&'static [u8; 6]`
/// - **Value**: `b"config"`
/// - **Purpose**:
/// - Provides a unique and consistent seed used in PDA derivation.
/// - Ensures that the PDA for the `Config` account is deterministic and can be reliably regenerated.
/// - Helps prevent address collisions by using a well-known, stable prefix.
pub const SEED_PREFIX: &'static [u8; 6] = b"config";
}
3 changes: 3 additions & 0 deletions packages/solana/programs/debridge-reporter/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod config;

pub use config::*;
1 change: 0 additions & 1 deletion packages/solana/programs/snapshotter/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anchor_lang::prelude::*;

#[error_code]
/// Errors relevant to this program's malfunction.
pub enum ErrorCode {
#[msg("AccountAlreadySubscribed")]
/// An account is already subscribed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::state::{Config, WormholeEmitter};
pub const SEED_PREFIX_SENT: &[u8; 4] = b"sent";

#[derive(Accounts)]
/// Context used to initialize program data (i.e. config).
pub struct Initialize<'info> {
#[account(mut)]
/// Whoever initializes the config will be the owner of the program. Signer
Expand Down
9 changes: 4 additions & 5 deletions packages/solana/programs/wormhole-reporter/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anchor_lang::prelude::error_code;

#[error_code]
/// Errors relevant to this program's malfunction.
pub enum ErrorCode {
#[msg("InvalidWormholeConfig")]
/// Specified Wormhole bridge data PDA is wrong.
Expand All @@ -19,11 +18,11 @@ pub enum ErrorCode {
/// Specified sysvar is wrong.
InvalidSysvar,

#[msg("RootNotFinalized")]
/// Snapshotter root not finalized
RootNotFinalized,

#[msg("InvalidSnapshotterConfig")]
/// Invalid Snapshotter config
InvalidSnapshotterConfig,

#[msg("RootNotFinalized")]
/// Snapshotter root not finalized
RootNotFinalized,
}
Loading

0 comments on commit a1b35a0

Please sign in to comment.