Skip to content
Merged
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
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"
Comment thread
buffalojoec marked this conversation as resolved.
bytemuck = "1.14.1"
log = "0.4.20"
Comment thread
buffalojoec marked this conversation as 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.
Comment thread
buffalojoec marked this conversation as 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