Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

program: introduce core BPF implementation #7

Merged
merged 14 commits into from
Mar 13, 2024
Merged
506 changes: 269 additions & 237 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
resolver = "2"
members = ["clients/rust"]
members = ["clients/rust", "program"]
34 changes: 34 additions & 0 deletions program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "scbpf-address-lookup-table"
version = "0.1.0"
edition = "2021"
readme = "./README.md"
license-file = "../LICENSE"
publish = false

[package.metadata.solana]
program-id = "AddressLookupTab1e1111111111111111111111111"

[features]
bpf-entrypoint = []
test-sbf = []

[dependencies]
bincode = "1.3.3"
buffalojoec marked this conversation as resolved.
Show resolved Hide resolved
bytemuck = "1.14.1"
log = "0.4.20"
buffalojoec marked this conversation as resolved.
Show resolved Hide resolved
serde = { version = "1.0.193", features = ["derive"] }
solana-frozen-abi = "1.18.2"
solana-frozen-abi-macro = "1.18.2"
solana-program = "1.18.2"
spl-program-error = "0.3.1"

[dev-dependencies]
solana-sdk = "1.18.2"
test-case = "3.3.1"

[lib]
crate-type = ["cdylib", "lib"]

[build-dependencies]
rustc_version = "0.4"
23 changes: 23 additions & 0 deletions program/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! Required for `solana-frozen-abi-macro` to work.
extern crate rustc_version;
use rustc_version::{version_meta, Channel};

fn main() {
// Copied and adapted from
// https://github.com/Kimundi/rustc-version-rs/blob/1d692a965f4e48a8cb72e82cda953107c0d22f47/README.md#example
// Licensed under Apache-2.0 + MIT
match version_meta().unwrap().channel {
Channel::Stable => {
println!("cargo:rustc-cfg=RUSTC_WITHOUT_SPECIALIZATION");
}
Channel::Beta => {
println!("cargo:rustc-cfg=RUSTC_WITHOUT_SPECIALIZATION");
}
Channel::Nightly => {
println!("cargo:rustc-cfg=RUSTC_WITH_SPECIALIZATION");
}
Channel::Dev => {
println!("cargo:rustc-cfg=RUSTC_WITH_SPECIALIZATION");
}
}
}
22 changes: 22 additions & 0 deletions program/src/entrypoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//! Program entrypoint

use {
crate::{error::AddressLookupError, processor},
solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, program_error::PrintProgramError,
pubkey::Pubkey,
},
};

solana_program::entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
if let Err(error) = processor::process(program_id, accounts, instruction_data) {
error.print::<AddressLookupError>();
return Err(error);
}
Ok(())
}
19 changes: 19 additions & 0 deletions program/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Program error types

use spl_program_error::*;

#[spl_program_error]
pub enum AddressLookupError {
/// Attempted to lookup addresses from a table that does not exist
#[error("Attempted to lookup addresses from a table that does not exist")]
LookupTableAccountNotFound,
/// Attempted to lookup addresses from an account owned by the wrong program
#[error("Attempted to lookup addresses from an account owned by the wrong program")]
InvalidAccountOwner,
/// Attempted to lookup addresses from an invalid account
#[error("Attempted to lookup addresses from an invalid account")]
InvalidAccountData,
/// Address lookup contains an invalid index
#[error("Address lookup contains an invalid index")]
InvalidLookupIndex,
}
193 changes: 193 additions & 0 deletions program/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
//! Program instruction types

use {
serde::{Deserialize, Serialize},
solana_program::{
clock::Slot,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
system_program,
},
};

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum AddressLookupTableInstruction {
/// Create an address lookup table
///
/// # Account references
/// 0. `[WRITE]` Uninitialized address lookup table account
/// 1. `[SIGNER]` Account used to derive and control the new address
/// lookup table.
/// 2. `[SIGNER, WRITE]` Account that will fund the new address lookup
/// table.
/// 3. `[]` System program for CPI.
CreateLookupTable {
/// A recent slot must be used in the derivation path
/// for each initialized table. When closing table accounts,
/// the initialization slot must no longer be "recent" to prevent
/// address tables from being recreated with reordered or
/// otherwise malicious addresses.
recent_slot: Slot,
/// Address tables are always initialized at program-derived
/// addresses using the funding address, recent blockhash, and
/// the user-passed `bump_seed`.
bump_seed: u8,
},

/// Permanently freeze an address lookup table, making it immutable.
///
/// # Account references
/// 0. `[WRITE]` Address lookup table account to freeze
/// 1. `[SIGNER]` Current authority
FreezeLookupTable,

/// Extend an address lookup table with new addresses. Funding account and
/// system program account references are only required if the lookup table
/// account requires additional lamports to cover the rent-exempt balance
/// after being extended.
///
/// # Account references
/// 0. `[WRITE]` Address lookup table account to extend
/// 1. `[SIGNER]` Current authority
/// 2. `[SIGNER, WRITE, OPTIONAL]` Account that will fund the table
/// reallocation
/// 3. `[OPTIONAL]` System program for CPI.
ExtendLookupTable { new_addresses: Vec<Pubkey> },

/// Deactivate an address lookup table, making it unusable and
/// eligible for closure after a short period of time.
///
/// # Account references
/// 0. `[WRITE]` Address lookup table account to deactivate
/// 1. `[SIGNER]` Current authority
DeactivateLookupTable,

/// Close an address lookup table account
///
/// # Account references
/// 0. `[WRITE]` Address lookup table account to close
/// 1. `[SIGNER]` Current authority
/// 2. `[WRITE]` Recipient of closed account lamports
CloseLookupTable,
}

/// Derives the address of an address table account from a wallet address and a
/// recent block's slot.
pub fn derive_lookup_table_address(
authority_address: &Pubkey,
recent_block_slot: Slot,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[authority_address.as_ref(), &recent_block_slot.to_le_bytes()],
&crate::id(),
)
}

// [Core BPF]: `create_lookup_table_signed` has been removed, since feature
// "FKAcEvNgSY79RpqsPNUV5gDyumopH4cEHqUxyfm8b8Ap"
// (relax_authority_signer_check_for_lookup_table_creation) has been activated
// on all clusters.
buffalojoec marked this conversation as resolved.
Show resolved Hide resolved

/// Constructs an instruction to create a table account and returns
/// the instruction and the table account's derived address.
pub fn create_lookup_table(
authority_address: Pubkey,
payer_address: Pubkey,
recent_slot: Slot,
) -> (Instruction, Pubkey) {
let (lookup_table_address, bump_seed) =
derive_lookup_table_address(&authority_address, recent_slot);

let instruction = Instruction::new_with_bincode(
crate::id(),
&AddressLookupTableInstruction::CreateLookupTable {
recent_slot,
bump_seed,
},
vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, false),
AccountMeta::new(payer_address, true),
AccountMeta::new_readonly(system_program::id(), false),
],
);

(instruction, lookup_table_address)
}

/// Constructs an instruction that freezes an address lookup
/// table so that it can never be closed or extended again. Empty
/// lookup tables cannot be frozen.
pub fn freeze_lookup_table(lookup_table_address: Pubkey, authority_address: Pubkey) -> Instruction {
Instruction::new_with_bincode(
crate::id(),
&AddressLookupTableInstruction::FreezeLookupTable,
vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, true),
],
)
}

/// Constructs an instruction which extends an address lookup
/// table account with new addresses.
pub fn extend_lookup_table(
lookup_table_address: Pubkey,
authority_address: Pubkey,
payer_address: Option<Pubkey>,
new_addresses: Vec<Pubkey>,
) -> Instruction {
let mut accounts = vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, true),
];

if let Some(payer_address) = payer_address {
accounts.extend([
AccountMeta::new(payer_address, true),
AccountMeta::new_readonly(system_program::id(), false),
]);
}

Instruction::new_with_bincode(
crate::id(),
&AddressLookupTableInstruction::ExtendLookupTable { new_addresses },
accounts,
)
}

/// Constructs an instruction that deactivates an address lookup
/// table so that it cannot be extended again and will be unusable
/// and eligible for closure after a short amount of time.
pub fn deactivate_lookup_table(
lookup_table_address: Pubkey,
authority_address: Pubkey,
) -> Instruction {
Instruction::new_with_bincode(
crate::id(),
&AddressLookupTableInstruction::DeactivateLookupTable,
vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, true),
],
)
}

/// Returns an instruction that closes an address lookup table
/// account. The account will be deallocated and the lamports
/// will be drained to the recipient address.
pub fn close_lookup_table(
lookup_table_address: Pubkey,
authority_address: Pubkey,
recipient_address: Pubkey,
) -> Instruction {
Instruction::new_with_bincode(
crate::id(),
&AddressLookupTableInstruction::CloseLookupTable,
vec![
AccountMeta::new(lookup_table_address, false),
AccountMeta::new_readonly(authority_address, true),
AccountMeta::new(recipient_address, false),
],
)
}
16 changes: 16 additions & 0 deletions program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! Address Lookup Table Program
// [Core BPF]: Required for `solana-frozen-abi-macro` to work.
#![allow(incomplete_features)]
#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))]

#[cfg(all(target_os = "solana", feature = "bpf-entrypoint"))]
mod entrypoint;
pub mod error;
pub mod instruction;
pub mod processor;
pub mod state;

// [Core BPF]: TODO: Program-test will not overwrite existing built-ins.
// See https://github.com/solana-labs/solana/pull/35233
// solana_program::declare_id!("AddressLookupTab1e1111111111111111111111111");
solana_program::declare_id!("AaoNx79M6YE3DcXfrRN4nmBcQvQPqdpowi6uEESuJdnm");
Loading