Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions programs/magic-root-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
name = "magic-root-interface"
repository.workspace = true
rust-version.workspace = true
version.workspace = true

[dependencies]
wincode = { workspace = true }

solana-account = { workspace = true, features = ["wincode"] }
solana-instruction = { workspace = true, features = ["wincode"] }
solana-pubkey = { workspace = true }


[lints]
workspace = true
16 changes: 16 additions & 0 deletions programs/magic-root-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# `magic-root-interface`

This crate defines the MagicRoot program id and `MagicRootInstruction` wire
schema shared by `magic-root-program` and `engine`. Instruction builders can
depend on the interface without linking the native program runtime.

`MagicRootInstruction::compose` prepends the writable target account and
serializes the instruction with wincode. `PostFinalize` also appends each
follow-up program id and its account metas. Signer bits are cleared in the outer
instruction; the native program supplies the declared follow-up signers when it
invokes each action.

`MagicRootInstruction::compose_account` builds the ordered patch sequence for a
complete account and appends its finalization instruction. The underlying patch
sequence applies mode before slot so the program can validate lifecycle-aware
slot progression.
71 changes: 71 additions & 0 deletions programs/magic-root-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#![doc = include_str!("../README.md")]

use solana_account::{AccountFieldPatch, OwnedAccount};
use solana_instruction::{AccountMeta, Instruction, error::InstructionError};
use solana_pubkey::{Pubkey, declare_id};
use wincode::{SchemaRead, SchemaWrite};

declare_id!("MagicRootDRJ5atQjSJUxFjXzjeZXMADHUDznbk22gy");

/// Instructions accepted by the MagicRoot built-in program.
#[derive(SchemaRead, SchemaWrite)]
pub enum MagicRootInstruction {
/// Apply a single-field patch to the target account.
Patch(AccountFieldPatch),
/// Finalize the target account: if it is executable, load it into the
/// transaction's program cache. Does not change lamports.
Finalize,
/// Close the target account.
Delete,
/// Run follow-up instructions once the account is finalized (e.g.
/// initializing a freshly created account); each is invoked via CPI against
/// the accounts it declares.
PostFinalize(Vec<Instruction>),
}

impl MagicRootInstruction {
/// Composes this variant into an [`Instruction`] targeting the MagicRoot
/// program: prepends the target `account` meta, appends any metas the
/// variant requires, and serializes the instruction data.
pub fn compose(&self, account: Pubkey) -> Result<Instruction, InstructionError> {
let mut accounts = vec![AccountMeta::new(account, false)];
self.extend_metas(&mut accounts);
// NOTE this code can never error, wincode serialization for instruction is infallible
let data = wincode::serialize(self).map_err(|_| InstructionError::BorshIoError)?;
Ok(Instruction { program_id: ID, accounts, data })
}

/// Composes `account` into the ordered instruction sequence that patches
/// every field on `target`, then finalizes it.
pub fn compose_account(
target: Pubkey,
account: OwnedAccount,
) -> Result<Vec<Instruction>, InstructionError> {
let patches = AccountFieldPatch::sequence(account);
let mut instructions = Vec::with_capacity(patches.len() + 1);
for patch in patches {
instructions.push(Self::Patch(patch).compose(target)?);
}
instructions.push(Self::Finalize.compose(target)?);
Ok(instructions)
}

/// Appends the extra account metas a variant requires. Only [`PostFinalize`]
/// contributes any: the program id and de-signed accounts of each follow-up
/// instruction.
///
/// [`PostFinalize`]: MagicRootInstruction::PostFinalize
fn extend_metas(&self, accounts: &mut Vec<AccountMeta>) {
let Self::PostFinalize(ixs) = self else {
return;
};
for ix in ixs {
accounts.push(AccountMeta::new_readonly(ix.program_id, false));
let metas = ix.accounts.clone().into_iter().map(|mut meta| {
meta.is_signer = false;
meta
});
accounts.extend(metas)
}
}
}
32 changes: 32 additions & 0 deletions programs/magic-root-program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "magic-root-program"

authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true

[dependencies]
magic-root-interface = { workspace = true }

nucleus = { workspace = true, features = ["tls"] }
wincode = { workspace = true }

solana-account = { workspace = true }
solana-instruction = { workspace = true }
solana-instruction-error = { workspace = true }
solana-program-runtime = { workspace = true }
solana-svm-log-collector = { workspace = true }
solana-transaction-context = { workspace = true }

[dev-dependencies]
solana-pubkey = { workspace = true }
solana-sdk-ids = { workspace = true }
solana-svm-callback = { workspace = true, features = ["agave-unstable-api"] }
solana-svm-feature-set = { workspace = true, features = ["agave-unstable-api"] }

[lints]
workspace = true
37 changes: 37 additions & 0 deletions programs/magic-root-program/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# `magic-root-program`

MagicRoot is the engine's privileged native program for patching, finalizing,
and closing accounts. Its wire schema and program id are defined by
`magic-root-interface`.

Every invocation must use the engine `AUTHORITY` as transaction payer/signer.
Direct transaction instructions are accepted. CPI is accepted only when the
immediate caller is registered in the transaction program cache as a builtin;
native-loader account metadata alone does not grant access. Authorization and
decoding complete before the target account is borrowed. The SVM's separate
top-level-only privilege rule is unchanged.

## Instructions

- `Patch` applies one `AccountFieldPatch`. Lamport changes are balanced against
the authority account, including no-op balance patches. Slot patches must
advance the stored slot; an equal slot is accepted only when an earlier patch
in the same transaction genuinely changed the account mode. Older slots are
always rejected. Account mode transitions use the policy owned by
`AccountSharedData`: read-only and placeholder accounts may transition to any
mode except transient, transient accounts may resolve only to read-only, and
delegated accounts may transition only to transient. Ephemeral accounts may
close. Other mode changes are rejected.
- `Finalize` loads an executable target into the transaction program cache. It
does not change lamports.
- `Delete` transitions read-only, placeholder, or ephemeral targets to
`AccountMode::Closed`; accountsdb removes them during writeback. Delegated and
transient targets remain protected, and modes that cannot transition to
closed are rejected.
- `PostFinalize` invokes follow-up instructions through native CPI. It rejects
any immutable instruction account marked writable.

Complete-account patch sequences apply mode before slot. `AccountSharedData`
marks mode dirty only when its value changes, so a no-op mode patch cannot make
an equal-slot replacement appear fresh. Rejection aborts the transaction, and
therefore rolls back every earlier field patch in that sequence.
74 changes: 74 additions & 0 deletions programs/magic-root-program/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use solana_account::{AccountFieldPatch, AccountMode, ReadableAccount, WritableAccount};
use solana_instruction_error::InstructionError;
use solana_program_runtime::{invoke_context::InvokeContext, loaded_programs::ProgramCacheEntry};
use solana_svm_log_collector::ic_msg;
use solana_transaction_context::IndexOfAccount;

/// Applies a single-field patch to the target account.
pub(crate) fn patch(
ctx: &InvokeContext<'_, '_>,
target: IndexOfAccount,
patch: AccountFieldPatch,
) -> Result<(), InstructionError> {
let mut account = ctx.transaction_context.accounts().try_borrow_mut(target)?;
ic_msg!(ctx, "MagicRoot: patch {:?}", patch);
let old = account.lamports();
if let Err(error) = patch.apply(&mut account) {
ic_msg!(ctx, "MagicRoot: {}", error);
return Err(InstructionError::InvalidArgument);
}
let new = account.lamports();
let mut authority = ctx.transaction_context.accounts().try_borrow_mut(0)?;
// Balance target changes against the authority to preserve total lamports.
if new > old {
authority.checked_sub_lamports(new - old)?;
} else if new < old {
authority.checked_add_lamports(old - new)?;
}
Ok(())
}

/// Loads an executable target into the transaction's program cache.
pub(crate) fn finalize(
ctx: &mut InvokeContext<'_, '_>,
target: IndexOfAccount,
) -> Result<(), InstructionError> {
let account = ctx.transaction_context.accounts().try_borrow(target)?;
if !account.executable() {
return Ok(());
}
let pubkey = *ctx.transaction_context.get_key_of_account_at_index(target)?;
let entry = ProgramCacheEntry::new(
ctx.environment_config
.program_runtime_environments_for_execution
.get_env_for_execution()
.clone(),
account.data(),
)
.map_err(|_| {
ic_msg!(ctx, "MagicRoot: program load failed {}", pubkey);
InstructionError::ProgramEnvironmentSetupFailure
})?
.into();
ctx.program_cache_for_tx_batch.store_modified_entry(pubkey, entry);
ic_msg!(ctx, "MagicRoot: finalized program load");
Ok(())
}

/// Marks the target account closed for removal from storage.
pub(crate) fn delete(
ctx: &InvokeContext<'_, '_>,
target: IndexOfAccount,
) -> Result<(), InstructionError> {
let mut acc = ctx.transaction_context.accounts().try_borrow_mut(target)?;
if matches!(acc.mode(), AccountMode::Delegated | AccountMode::Transient) {
ic_msg!(ctx, "MagicRoot: tried to delete protected account");
return Err(InstructionError::PrivilegeEscalation);
}
if let Err(error) = acc.set_mode(AccountMode::Closed) {
ic_msg!(ctx, "MagicRoot: {}", error);
return Err(InstructionError::InvalidArgument);
}
ic_msg!(ctx, "MagicRoot: removed");
Ok(())
}
25 changes: 25 additions & 0 deletions programs/magic-root-program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![doc = include_str!("../README.md")]

mod account;
mod post_finalize;
mod processor;
#[cfg(test)]
mod tests;

use solana_instruction_error::InstructionError;
use solana_program_runtime::invoke_context::InvokeContext;

/// Instruction-account index of the account a MagicRoot instruction operates on.
pub const TARGET_ACCOUNT_IDX: u16 = 0;

/// Executes a MagicRoot instruction: authorizes the caller, decodes the
/// instruction, then dispatches it to the matching handler.
pub fn process(ctx: &mut InvokeContext<'_, '_>) -> Result<(), InstructionError> {
processor::process(ctx)
}

#[allow(missing_docs)]
pub mod entrypoint {
use solana_program_runtime::declare_process_instruction;
declare_process_instruction!(MagicRootEntrypoint, 150, |ctx| { super::process(ctx) });
}
36 changes: 36 additions & 0 deletions programs/magic-root-program/src/post_finalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use solana_instruction::Instruction;
use solana_instruction_error::InstructionError;
use solana_program_runtime::invoke_context::InvokeContext;
use solana_svm_log_collector::ic_msg;

/// Runs the post-finalize follow-up instructions: rejects any writable
/// instruction account that is not mutable, then invokes each action via CPI,
/// vouching for exactly the signers that action itself declares.
pub(crate) fn process(
ctx: &mut InvokeContext<'_, '_>,
actions: Vec<Instruction>,
) -> Result<(), InstructionError> {
ic_msg!(ctx, "MagicRoot: post-finalize {} action(s)", actions.len());
let instruction = ctx.transaction_context.get_current_instruction_context()?;
let count = instruction.get_number_of_instruction_accounts();
for i in 0..count {
let index = instruction.get_index_of_instruction_account_in_transaction(i)?;
let account = ctx.transaction_context.accounts().try_borrow(index)?;
if instruction.is_instruction_account_writable(i)? && !account.mutable() {
ic_msg!(
ctx,
"MagicRoot: post-finalize rejected immutable writable account"
);
return Err(InstructionError::Immutable);
}
}
for action in actions {
let signers: Vec<_> = action
.accounts
.iter()
.filter_map(|meta| meta.is_signer.then_some(meta.pubkey))
.collect();
ctx.native_invoke(action, &signers)?;
}
Ok(())
}
76 changes: 76 additions & 0 deletions programs/magic-root-program/src/processor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use magic_root_interface::MagicRootInstruction;
use nucleus::tls::AUTHORITY;
use solana_instruction::TRANSACTION_LEVEL_STACK_HEIGHT;
use solana_instruction_error::InstructionError;
use solana_program_runtime::{
invoke_context::InvokeContext, loaded_programs::ProgramCacheEntryType,
};
use solana_svm_log_collector::ic_msg;

use crate::{TARGET_ACCOUNT_IDX, account, post_finalize};

pub(crate) fn process(ctx: &mut InvokeContext<'_, '_>) -> Result<(), InstructionError> {
authorize(ctx)?;
let instruction = decode(ctx)?;
dispatch(ctx, instruction)
}

/// Rejects non-builtin CPI callers and callers other than [`AUTHORITY`].
///
/// MagicRoot is engine-internal: it must run either at the transaction level or
/// directly below a builtin program. The transaction must be signed by the
/// authority in either case. Both conditions are checked before any account is
/// touched.
///
/// Authorizing on account 0's key alone is sound because the engine verifies the
/// fee-payer signature at its transaction ingress (`engine::transaction`), so a
/// transaction reaching execution with `AUTHORITY` as account 0 was signed by it.
pub(crate) fn authorize(ctx: &InvokeContext<'_, '_>) -> Result<(), InstructionError> {
let height = ctx.get_stack_height();
if height != TRANSACTION_LEVEL_STACK_HEIGHT {
let instruction = ctx.transaction_context.get_current_instruction_context()?;
let caller = ctx
.transaction_context
.get_instruction_context_at_index_in_trace(instruction.get_index_of_caller())?;
let caller_id = caller.get_program_key()?;
let Some(entry) = ctx.program_cache_for_tx_batch.find(caller_id) else {
ic_msg!(ctx, "MagicRoot: non-builtin caller {}", caller_id);
return Err(InstructionError::CallDepth);
};
if !matches!(&entry.program, ProgramCacheEntryType::Builtin(_)) {
ic_msg!(ctx, "MagicRoot: non-builtin caller {}", caller_id);
return Err(InstructionError::CallDepth);
}
}
let signer = *ctx.transaction_context.get_key_of_account_at_index(0)?;
if signer != AUTHORITY.get() {
ic_msg!(ctx, "MagicRoot: unauthorized signer {}", signer);
return Err(InstructionError::MissingRequiredSignature);
}
Ok(())
}

/// Decodes the current instruction's data into a [`MagicRootInstruction`].
fn decode(ctx: &InvokeContext<'_, '_>) -> Result<MagicRootInstruction, InstructionError> {
let instruction = ctx.transaction_context.get_current_instruction_context()?;
wincode::deserialize(instruction.get_instruction_data()).map_err(|_| {
ic_msg!(ctx, "MagicRoot: malformed instruction data");
InstructionError::InvalidInstructionData
})
}

/// Resolves the target account index and dispatches to the matching handler.
fn dispatch(
ctx: &mut InvokeContext<'_, '_>,
instruction: MagicRootInstruction,
) -> Result<(), InstructionError> {
let instruction_context = ctx.transaction_context.get_current_instruction_context()?;
let target =
instruction_context.get_index_of_instruction_account_in_transaction(TARGET_ACCOUNT_IDX)?;
match instruction {
MagicRootInstruction::Patch(patch) => account::patch(ctx, target, patch),
MagicRootInstruction::Finalize => account::finalize(ctx, target),
MagicRootInstruction::Delete => account::delete(ctx, target),
MagicRootInstruction::PostFinalize(actions) => post_finalize::process(ctx, actions),
}
}
Loading