diff --git a/Cargo.lock b/Cargo.lock index 0d522ff3580112..b52669b747014e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7476,6 +7476,7 @@ dependencies = [ "solana-slot-hashes", "solana-slot-history", "solana-stable-layout", + "solana-system-instruction", "solana-sysvar-id", "solana-transaction-error", "static_assertions", @@ -8460,6 +8461,30 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-system-instruction" +version = "2.2.0" +dependencies = [ + "anyhow", + "borsh 1.5.1", + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-instruction", + "solana-logger", + "solana-program", + "solana-program-error", + "solana-pubkey", + "static_assertions", + "strum", + "strum_macros", + "wasm-bindgen", +] + [[package]] name = "solana-system-program" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index 51b9aa1c42fb09..9c01c93fc9bebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -143,6 +143,7 @@ members = [ "sdk/slot-hashes", "sdk/slot-history", "sdk/stable-layout", + "sdk/system-instruction", "sdk/sysvar-id", "sdk/time-utils", "sdk/transaction-error", @@ -484,6 +485,7 @@ solana-sha256-hasher = { path = "sdk/sha256-hasher", version = "=2.2.0" } solana-signature = { path = "sdk/signature", version = "=2.2.0", default-features = false } solana-slot-hashes = { path = "sdk/slot-hashes", version = "=2.2.0" } solana-slot-history = { path = "sdk/slot-history", version = "=2.2.0" } +solana-system-instruction = { path = "sdk/system-instruction", version = "=2.2.0" } solana-time-utils = { path = "sdk/time-utils", version = "=2.2.0" } solana-timings = { path = "timings", version = "=2.2.0" } solana-unified-scheduler-logic = { path = "unified-scheduler-logic", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 17ca6b9ee419b9..5d8af314d8519d 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5839,6 +5839,7 @@ dependencies = [ "solana-slot-hashes", "solana-slot-history", "solana-stable-layout", + "solana-system-instruction", "solana-sysvar-id", "solana-transaction-error", "thiserror", @@ -7080,6 +7081,20 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-system-instruction" +version = "2.2.0" +dependencies = [ + "js-sys", + "num-traits", + "serde", + "serde_derive", + "solana-decode-error", + "solana-instruction", + "solana-pubkey", + "wasm-bindgen", +] + [[package]] name = "solana-system-program" version = "2.2.0" diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 4252838b59c10f..66da747edbb492 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -74,6 +74,7 @@ solana-short-vec = { workspace = true } solana-slot-hashes = { workspace = true, features = ["serde", "sysvar"] } solana-slot-history = { workspace = true, features = ["serde", "sysvar"] } solana-stable-layout = { workspace = true } +solana-system-instruction = { workspace = true } solana-sysvar-id = { workspace = true } thiserror = { workspace = true } @@ -148,7 +149,8 @@ frozen-abi = [ "solana-instruction/frozen-abi", "solana-pubkey/frozen-abi", "solana-rent/frozen-abi", - "solana-short-vec/frozen-abi" + "solana-short-vec/frozen-abi", + "solana-system-instruction/frozen-abi" ] [lints] diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 8a757d72e7c87e..e6bcd2095cd173 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -504,7 +504,6 @@ pub mod slot_history; pub mod stake; pub mod stake_history; pub mod syscalls; -pub mod system_instruction; pub mod system_program; pub mod sysvar; pub mod vote; @@ -551,6 +550,7 @@ pub use { entrypoint_no_alloc, }, solana_program_option as program_option, solana_pubkey as pubkey, solana_rent as rent, + solana_system_instruction as system_instruction, }; /// The [config native program][np]. /// diff --git a/sdk/program/src/wasm/system_instruction.rs b/sdk/program/src/wasm/system_instruction.rs index 94dd636788092c..e320db37177a6f 100644 --- a/sdk/program/src/wasm/system_instruction.rs +++ b/sdk/program/src/wasm/system_instruction.rs @@ -1,112 +1,3 @@ -//! `SystemInstruction` Javascript interface -#![cfg(target_arch = "wasm32")] -#![allow(non_snake_case)] -use { - crate::{instruction::Instruction, pubkey::Pubkey, system_instruction::*}, - wasm_bindgen::prelude::*, -}; - -#[wasm_bindgen] -impl SystemInstruction { - pub fn createAccount( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - space: u64, - owner: &Pubkey, - ) -> Instruction { - create_account(from_pubkey, to_pubkey, lamports, space, owner) - } - - pub fn createAccountWithSeed( - from_pubkey: &Pubkey, - to_pubkey: &Pubkey, - base: &Pubkey, - seed: &str, - lamports: u64, - space: u64, - owner: &Pubkey, - ) -> Instruction { - create_account_with_seed(from_pubkey, to_pubkey, base, seed, lamports, space, owner) - } - - pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { - assign(pubkey, owner) - } - - pub fn assignWithSeed( - pubkey: &Pubkey, - base: &Pubkey, - seed: &str, - owner: &Pubkey, - ) -> Instruction { - assign_with_seed(pubkey, base, seed, owner) - } - - pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { - transfer(from_pubkey, to_pubkey, lamports) - } - - pub fn transferWithSeed( - from_pubkey: &Pubkey, - from_base: &Pubkey, - from_seed: String, - from_owner: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - ) -> Instruction { - transfer_with_seed( - from_pubkey, - from_base, - from_seed, - from_owner, - to_pubkey, - lamports, - ) - } - - pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { - allocate(pubkey, space) - } - - pub fn allocateWithSeed( - address: &Pubkey, - base: &Pubkey, - seed: &str, - space: u64, - owner: &Pubkey, - ) -> Instruction { - allocate_with_seed(address, base, seed, space, owner) - } - - pub fn createNonceAccount( - from_pubkey: &Pubkey, - nonce_pubkey: &Pubkey, - authority: &Pubkey, - lamports: u64, - ) -> js_sys::Array { - let instructions = create_nonce_account(from_pubkey, nonce_pubkey, authority, lamports); - instructions.into_iter().map(JsValue::from).collect() - } - - pub fn advanceNonceAccount(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { - advance_nonce_account(nonce_pubkey, authorized_pubkey) - } - - pub fn withdrawNonceAccount( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - to_pubkey: &Pubkey, - lamports: u64, - ) -> Instruction { - withdraw_nonce_account(nonce_pubkey, authorized_pubkey, to_pubkey, lamports) - } - - pub fn authorizeNonceAccount( - nonce_pubkey: &Pubkey, - authorized_pubkey: &Pubkey, - new_authority: &Pubkey, - ) -> Instruction { - authorize_nonce_account(nonce_pubkey, authorized_pubkey, new_authority) - } -} +//! Left empty because deleting this module would technically be a breaking change, +//! but there was never anything you could import from here. +// TODO: delete this in next breaking change. diff --git a/sdk/system-instruction/Cargo.toml b/sdk/system-instruction/Cargo.toml new file mode 100644 index 00000000000000..33c1526694dfdb --- /dev/null +++ b/sdk/system-instruction/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "solana-system-instruction" +description = "Instructions and constructors for the Solana system program." +documentation = "https://docs.rs/solana-system-instruction" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[dependencies] +num-traits = { workspace = true } +serde = { workspace = true } +serde_derive = { workspace = true } +solana-decode-error = { workspace = true } +solana-frozen-abi = { workspace = true, optional = true } +solana-frozen-abi-macro = { workspace = true, optional = true } +solana-instruction = { workspace = true, features = ["bincode", "std"] } +solana-logger = { workspace = true, optional = true } +solana-pubkey = { workspace = true, default-features = false, features = ["serde"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +js-sys = { workspace = true } +wasm-bindgen = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +borsh = { workspace = true } +solana-instruction = { workspace = true } +solana-program = { path = "../program", default-features = false } +solana-program-error = { workspace = true, features = ["borsh"] } +static_assertions = { workspace = true } +strum = { workspace = true } +strum_macros = { workspace = true } + +[features] +frozen-abi = [ + "dep:solana-frozen-abi", + "dep:solana-frozen-abi-macro", + "dep:solana-logger" +] + +[lints] +workspace = true diff --git a/sdk/program/src/system_instruction.rs b/sdk/system-instruction/src/lib.rs similarity index 85% rename from sdk/program/src/system_instruction.rs rename to sdk/system-instruction/src/lib.rs index bd0fcb0b37e0a6..915c86daf578b3 100644 --- a/sdk/program/src/system_instruction.rs +++ b/sdk/system-instruction/src/lib.rs @@ -19,7 +19,7 @@ //! or they can be [program derived addresses][pda], //! where write access to accounts is granted by an owning program. //! -//! [pda]: crate::pubkey::Pubkey::find_program_address +//! [pda]: https://docs.rs/solana-pubkey/latest/solana_pubkey/struct.Pubkey.html#method.find_program_address //! //! The system program ID is defined in [`system_program`]. //! @@ -34,47 +34,139 @@ //! and these variants are linked from the documentation for their constructors. //! //! [`RpcClient`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html -//! [cpi]: crate::program -//! [`invoke`]: crate::program::invoke -//! [`invoke_signed`]: crate::program::invoke_signed -//! [`AccountInfo`]: crate::account_info::AccountInfo +//! [cpi]: https://docs.rs/solana-program/latest/solana_program/program/ +//! [`invoke`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html +//! [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html +//! [`AccountInfo`]: https://docs.rs/solana-account-info/latest/solana_account_info/struct.AccountInfo.html +//! [`system_program`]: https://docs.rs/solana-program/latest/solana_program/system_program/index.html +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #[allow(deprecated)] use { - crate::{ - instruction::{AccountMeta, Instruction}, - nonce, - pubkey::Pubkey, - system_program, - sysvar::{recent_blockhashes, rent}, - }, - num_derive::{FromPrimitive, ToPrimitive}, + core::fmt, + num_traits::{FromPrimitive, ToPrimitive}, + serde_derive::{Deserialize, Serialize}, solana_decode_error::DecodeError, - thiserror::Error, + solana_instruction::{AccountMeta, Instruction}, + solana_pubkey::Pubkey, }; +#[cfg(target_arch = "wasm32")] +mod wasm; + +// inline some constants to avoid dependencies +const RECENT_BLOCKHASHES_ID: Pubkey = + Pubkey::from_str_const("SysvarRecentB1ockHashes11111111111111111111"); +const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111"); +const SYSTEM_PROGRAM_ID: Pubkey = Pubkey::from_str_const("11111111111111111111111111111111"); +const NONCE_STATE_SIZE: usize = 80; +#[cfg(test)] +static_assertions::const_assert_eq!(solana_program::nonce::State::size(), NONCE_STATE_SIZE); -#[derive(Error, Debug, Serialize, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] +// Use strum when testing to ensure our FromPrimitive +// impl is exhaustive +#[cfg_attr(test, derive(strum_macros::FromRepr, strum_macros::EnumIter))] +#[derive(Debug, Serialize, Clone, PartialEq, Eq)] pub enum SystemError { - #[error("an account with the same address already exists")] AccountAlreadyInUse, - #[error("account does not have enough SOL to perform the operation")] ResultWithNegativeLamports, - #[error("cannot assign account to this program id")] InvalidProgramId, - #[error("cannot allocate account data of this length")] InvalidAccountDataLength, - #[error("length of requested seed is too long")] MaxSeedLengthExceeded, - #[error("provided address does not match addressed derived from seed")] AddressWithSeedMismatch, - #[error("advancing stored nonce requires a populated RecentBlockhashes sysvar")] NonceNoRecentBlockhashes, - #[error("stored nonce is still in recent_blockhashes")] NonceBlockhashNotExpired, - #[error("specified nonce does not match stored nonce")] NonceUnexpectedBlockhashValue, } +impl FromPrimitive for SystemError { + #[inline] + fn from_i64(n: i64) -> Option { + if n == Self::AccountAlreadyInUse as i64 { + Some(Self::AccountAlreadyInUse) + } else if n == Self::ResultWithNegativeLamports as i64 { + Some(Self::ResultWithNegativeLamports) + } else if n == Self::InvalidProgramId as i64 { + Some(Self::InvalidProgramId) + } else if n == Self::InvalidAccountDataLength as i64 { + Some(Self::InvalidAccountDataLength) + } else if n == Self::MaxSeedLengthExceeded as i64 { + Some(Self::MaxSeedLengthExceeded) + } else if n == Self::AddressWithSeedMismatch as i64 { + Some(Self::AddressWithSeedMismatch) + } else if n == Self::NonceNoRecentBlockhashes as i64 { + Some(Self::NonceNoRecentBlockhashes) + } else if n == Self::NonceBlockhashNotExpired as i64 { + Some(Self::NonceBlockhashNotExpired) + } else if n == Self::NonceUnexpectedBlockhashValue as i64 { + Some(Self::NonceUnexpectedBlockhashValue) + } else { + None + } + } + #[inline] + fn from_u64(n: u64) -> Option { + Self::from_i64(n as i64) + } +} + +impl ToPrimitive for SystemError { + #[inline] + fn to_i64(&self) -> Option { + Some(match *self { + Self::AccountAlreadyInUse => Self::AccountAlreadyInUse as i64, + Self::ResultWithNegativeLamports => Self::ResultWithNegativeLamports as i64, + Self::InvalidProgramId => Self::InvalidProgramId as i64, + Self::InvalidAccountDataLength => Self::InvalidAccountDataLength as i64, + Self::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded as i64, + Self::AddressWithSeedMismatch => Self::AddressWithSeedMismatch as i64, + Self::NonceNoRecentBlockhashes => Self::NonceNoRecentBlockhashes as i64, + Self::NonceBlockhashNotExpired => Self::NonceBlockhashNotExpired as i64, + Self::NonceUnexpectedBlockhashValue => Self::NonceUnexpectedBlockhashValue as i64, + }) + } + #[inline] + fn to_u64(&self) -> Option { + self.to_i64().map(|x| x as u64) + } +} + +impl std::error::Error for SystemError {} + +impl fmt::Display for SystemError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SystemError::AccountAlreadyInUse => { + f.write_str("an account with the same address already exists") + } + SystemError::ResultWithNegativeLamports => { + f.write_str("account does not have enough SOL to perform the operation") + } + SystemError::InvalidProgramId => { + f.write_str("cannot assign account to this program id") + } + SystemError::InvalidAccountDataLength => { + f.write_str("cannot allocate account data of this length") + } + SystemError::MaxSeedLengthExceeded => { + f.write_str("length of requested seed is too long") + } + SystemError::AddressWithSeedMismatch => { + f.write_str("provided address does not match addressed derived from seed") + } + SystemError::NonceNoRecentBlockhashes => { + f.write_str("advancing stored nonce requires a populated RecentBlockhashes sysvar") + } + SystemError::NonceBlockhashNotExpired => { + f.write_str("stored nonce is still in recent_blockhashes") + } + SystemError::NonceUnexpectedBlockhashValue => { + f.write_str("specified nonce does not match stored nonce") + } + } + } +} + impl DecodeError for SystemError { fn type_of() -> &'static str { "SystemError" @@ -103,8 +195,11 @@ static_assertions::const_assert_eq!(MAX_PERMITTED_DATA_LENGTH, 10_485_760); /// An instruction to the system program. #[cfg_attr( feature = "frozen-abi", - frozen_abi(digest = "2LnVTnJg7LxB1FawNZLoQEY8yiYx3MT3paTdx4s5kAXU"), - derive(AbiExample, AbiEnumVisitor) + solana_frozen_abi_macro::frozen_abi(digest = "2LnVTnJg7LxB1FawNZLoQEY8yiYx3MT3paTdx4s5kAXU"), + derive( + solana_frozen_abi_macro::AbiExample, + solana_frozen_abi_macro::AbiEnumVisitor + ) )] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub enum SystemInstruction { @@ -286,7 +381,7 @@ pub enum SystemInstruction { /// [`SystemInstruction::CreateAccount`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// Account creation typically involves three steps: [`allocate`] space, /// [`transfer`] lamports for rent, [`assign`] to its owning program. The @@ -363,8 +458,8 @@ pub enum SystemInstruction { /// virtually by the program itself via [`invoke_signed`], `payer` being signed /// for by the client that submitted the transaction. /// -/// [pda]: Pubkey::find_program_address -/// [`invoke_signed`]: crate::program::invoke_signed +/// [pda]: https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; @@ -450,7 +545,7 @@ pub fn create_account( AccountMeta::new(*to_pubkey, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::CreateAccount { lamports, space, @@ -478,7 +573,7 @@ pub fn create_account_with_seed( ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::CreateAccountWithSeed { base: *base, seed: seed.to_string(), @@ -497,7 +592,7 @@ pub fn create_account_with_seed( /// [`SystemInstruction::Assign`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// # Required signers /// @@ -580,8 +675,8 @@ pub fn create_account_with_seed( /// itself via [`invoke_signed`], `payer` being signed for by the client that /// submitted the transaction. /// -/// [pda]: Pubkey::find_program_address -/// [`invoke_signed`]: crate::program::invoke_signed +/// [pda]: https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; @@ -674,7 +769,7 @@ pub fn create_account_with_seed( pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { let account_metas = vec![AccountMeta::new(*pubkey, true)]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::Assign { owner: *owner }, account_metas, ) @@ -691,7 +786,7 @@ pub fn assign_with_seed( AccountMeta::new_readonly(*base, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::AssignWithSeed { base: *base, seed: seed.to_string(), @@ -708,7 +803,7 @@ pub fn assign_with_seed( /// [`SystemInstruction::Transfer`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// # Required signers /// @@ -791,8 +886,8 @@ pub fn assign_with_seed( /// itself via [`invoke_signed`], `payer` being signed for by the client that /// submitted the transaction. /// -/// [pda]: Pubkey::find_program_address -/// [`invoke_signed`]: crate::program::invoke_signed +/// [pda]: https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; @@ -888,7 +983,7 @@ pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Inst AccountMeta::new(*to_pubkey, false), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::Transfer { lamports }, account_metas, ) @@ -908,7 +1003,7 @@ pub fn transfer_with_seed( AccountMeta::new(*to_pubkey, false), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::TransferWithSeed { lamports, from_seed, @@ -925,7 +1020,7 @@ pub fn transfer_with_seed( /// [`SystemInstruction::Allocate`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// The transaction will fail if the account already has size greater than 0, /// or if the requested size is greater than [`MAX_PERMITTED_DATA_LENGTH`]. @@ -1011,8 +1106,8 @@ pub fn transfer_with_seed( /// itself via [`invoke_signed`], `payer` being signed for by the client that /// submitted the transaction. /// -/// [pda]: Pubkey::find_program_address -/// [`invoke_signed`]: crate::program::invoke_signed +/// [pda]: https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; @@ -1105,7 +1200,7 @@ pub fn transfer_with_seed( pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { let account_metas = vec![AccountMeta::new(*pubkey, true)]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::Allocate { space }, account_metas, ) @@ -1123,7 +1218,7 @@ pub fn allocate_with_seed( AccountMeta::new_readonly(*base, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::AllocateWithSeed { base: *base, seed: seed.to_string(), @@ -1141,7 +1236,7 @@ pub fn allocate_with_seed( /// [`SystemInstruction::Transfer`]s. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// # Required signers /// @@ -1206,8 +1301,8 @@ pub fn allocate_with_seed( /// [`invoke_signed`], `payer` being signed for by the client that submitted the /// transaction. /// -/// [pda]: Pubkey::find_program_address -/// [`invoke_signed`]: crate::program::invoke_signed +/// [pda]: https://docs.rs/solana-program/latest/solana_program/pubkey/struct.Pubkey.html#method.find_program_address +/// [`invoke_signed`]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke_signed.html /// /// ``` /// # use borsh::{BorshDeserialize, BorshSerialize}; @@ -1297,17 +1392,17 @@ pub fn create_nonce_account_with_seed( base, seed, lamports, - nonce::State::size() as u64, - &system_program::id(), + NONCE_STATE_SIZE as u64, + &SYSTEM_PROGRAM_ID, ), Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::InitializeNonceAccount(*authority), vec![ AccountMeta::new(*nonce_pubkey, false), #[allow(deprecated)] - AccountMeta::new_readonly(recent_blockhashes::id(), false), - AccountMeta::new_readonly(rent::id(), false), + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), ], ), ] @@ -1321,7 +1416,7 @@ pub fn create_nonce_account_with_seed( /// [`SystemInstruction::InitializeNonceAccount`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// A [durable transaction nonce][dtn] is a special account that enables /// execution of transactions that have been signed in the past. @@ -1335,7 +1430,7 @@ pub fn create_nonce_account_with_seed( /// minutes, then successfully execute that transaction. /// /// [dtn]: https://docs.solanalabs.com/implemented-proposals/durable-tx-nonces -/// [rbh]: crate::message::Message::recent_blockhash +/// [rbh]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash /// [nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce /// /// Durable transaction nonces are an alternative to the standard recent @@ -1350,8 +1445,8 @@ pub fn create_nonce_account_with_seed( /// the [`blockhash`] field of [`nonce::state::Data`], which is deserialized /// from the nonce account data. /// -/// [`blockhash`]: crate::nonce::state::Data::blockhash -/// [`nonce::state::Data`]: crate::nonce::state::Data +/// [`blockhash`]: https://docs.rs/solana-program/latest/solana_program/nonce/state/struct.Data.html#method.blockhash +/// [`nonce::state::Data`]: https://docs.rs/solana-program/latest/solana_program/nonce/state/struct.Data.html /// /// The basic durable transaction nonce lifecycle is /// @@ -1435,17 +1530,17 @@ pub fn create_nonce_account( from_pubkey, nonce_pubkey, lamports, - nonce::State::size() as u64, - &system_program::id(), + NONCE_STATE_SIZE as u64, + &SYSTEM_PROGRAM_ID, ), Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::InitializeNonceAccount(*authority), vec![ AccountMeta::new(*nonce_pubkey, false), #[allow(deprecated)] - AccountMeta::new_readonly(recent_blockhashes::id(), false), - AccountMeta::new_readonly(rent::id(), false), + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), ], ), ] @@ -1458,7 +1553,7 @@ pub fn create_nonce_account( /// [`SystemInstruction::AdvanceNonceAccount`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// Every transaction that relies on a durable transaction nonce must contain a /// [`SystemInstruction::AdvanceNonceAccount`] instruction as the first @@ -1478,9 +1573,9 @@ pub fn create_nonce_account( /// For further description of durable transaction nonces see /// [`create_nonce_account`]. /// -/// [`Message`]: crate::message::Message -/// [`Message::new_with_nonce`]: crate::message::Message::new_with_nonce -/// [`recent_blockhash`]: crate::message::Message::recent_blockhash +/// [`Message`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html +/// [`Message::new_with_nonce`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#method.new_with_nonce +/// [`recent_blockhash`]: https://docs.rs/solana-program/latest/solana_program/message/legacy/struct.Message.html#structfield.recent_blockhash /// [dfa]: https://docs.rs/solana-rpc-client-nonce-utils/latest/solana_rpc_client_nonce_utils/fn.data_from_account.html /// /// # Required signers @@ -1578,11 +1673,11 @@ pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) let account_metas = vec![ AccountMeta::new(*nonce_pubkey, false), #[allow(deprecated)] - AccountMeta::new_readonly(recent_blockhashes::id(), false), + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), AccountMeta::new_readonly(*authorized_pubkey, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::AdvanceNonceAccount, account_metas, ) @@ -1595,7 +1690,7 @@ pub fn advance_nonce_account(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) /// [`SystemInstruction::WithdrawNonceAccount`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// Withdrawing the entire balance of a nonce account will cause the runtime to /// destroy it upon successful completion of the transaction. @@ -1670,12 +1765,12 @@ pub fn withdraw_nonce_account( AccountMeta::new(*nonce_pubkey, false), AccountMeta::new(*to_pubkey, false), #[allow(deprecated)] - AccountMeta::new_readonly(recent_blockhashes::id(), false), - AccountMeta::new_readonly(rent::id(), false), + AccountMeta::new_readonly(RECENT_BLOCKHASHES_ID, false), + AccountMeta::new_readonly(RENT_ID, false), AccountMeta::new_readonly(*authorized_pubkey, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::WithdrawNonceAccount(lamports), account_metas, ) @@ -1688,7 +1783,7 @@ pub fn withdraw_nonce_account( /// [`SystemInstruction::AuthorizeNonceAccount`]. /// /// [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html -/// [invoked]: crate::program::invoke +/// [invoked]: https://docs.rs/solana-program/latest/solana_program/program/fn.invoke.html /// /// This constructor creates a [`SystemInstruction::AuthorizeNonceAccount`] /// instruction. @@ -1752,7 +1847,7 @@ pub fn authorize_nonce_account( AccountMeta::new_readonly(*authorized_pubkey, true), ]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::AuthorizeNonceAccount(*new_authority), account_metas, ) @@ -1763,7 +1858,7 @@ pub fn authorize_nonce_account( pub fn upgrade_nonce_account(nonce_pubkey: Pubkey) -> Instruction { let account_metas = vec![AccountMeta::new(nonce_pubkey, /*is_signer:*/ false)]; Instruction::new_with_bincode( - system_program::id(), + SYSTEM_PROGRAM_ID, &SystemInstruction::UpgradeNonceAccount, account_metas, ) @@ -1771,7 +1866,7 @@ pub fn upgrade_nonce_account(nonce_pubkey: Pubkey) -> Instruction { #[cfg(test)] mod tests { - use {super::*, crate::instruction::Instruction}; + use {super::*, strum::IntoEnumIterator}; fn get_keys(instruction: &Instruction) -> Vec { instruction.accounts.iter().map(|x| x.pubkey).collect() @@ -1798,9 +1893,31 @@ mod tests { let ixs = create_nonce_account(&from_pubkey, &nonce_pubkey, &authorized, 42); assert_eq!(ixs.len(), 2); let ix = &ixs[0]; - assert_eq!(ix.program_id, system_program::id()); + assert_eq!(ix.program_id, SYSTEM_PROGRAM_ID); let pubkeys: Vec<_> = ix.accounts.iter().map(|am| am.pubkey).collect(); assert!(pubkeys.contains(&from_pubkey)); assert!(pubkeys.contains(&nonce_pubkey)); } + + #[test] + fn test_inline_consts() { + assert_eq!( + solana_program::sysvar::recent_blockhashes::ID, + RECENT_BLOCKHASHES_ID + ); + assert_eq!(solana_program::sysvar::rent::ID, RENT_ID); + assert_eq!(solana_program::system_program::ID, SYSTEM_PROGRAM_ID); + } + + #[test] + fn test_system_error_from_primitive_exhaustive() { + for variant in SystemError::iter() { + let variant_i64 = variant.clone() as i64; + assert_eq!( + SystemError::from_repr(variant_i64 as usize), + SystemError::from_i64(variant_i64) + ); + assert_eq!(SystemError::from_i64(variant_i64).unwrap(), variant); + } + } } diff --git a/sdk/system-instruction/src/wasm.rs b/sdk/system-instruction/src/wasm.rs new file mode 100644 index 00000000000000..e9d4475a60b1d0 --- /dev/null +++ b/sdk/system-instruction/src/wasm.rs @@ -0,0 +1,117 @@ +//! `SystemInstruction` Javascript interface +#![allow(non_snake_case)] +use { + crate::{ + advance_nonce_account, allocate, allocate_with_seed, assign, assign_with_seed, + authorize_nonce_account, create_account, create_account_with_seed, create_nonce_account, + transfer, transfer_with_seed, withdraw_nonce_account, SystemInstruction, + }, + solana_instruction::Instruction, + solana_pubkey::Pubkey, + wasm_bindgen::prelude::*, +}; + +#[wasm_bindgen] +impl SystemInstruction { + pub fn createAccount( + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, + space: u64, + owner: &Pubkey, + ) -> Instruction { + create_account(from_pubkey, to_pubkey, lamports, space, owner) + } + + pub fn createAccountWithSeed( + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + base: &Pubkey, + seed: &str, + lamports: u64, + space: u64, + owner: &Pubkey, + ) -> Instruction { + create_account_with_seed(from_pubkey, to_pubkey, base, seed, lamports, space, owner) + } + + pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction { + assign(pubkey, owner) + } + + pub fn assignWithSeed( + pubkey: &Pubkey, + base: &Pubkey, + seed: &str, + owner: &Pubkey, + ) -> Instruction { + assign_with_seed(pubkey, base, seed, owner) + } + + pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { + transfer(from_pubkey, to_pubkey, lamports) + } + + pub fn transferWithSeed( + from_pubkey: &Pubkey, + from_base: &Pubkey, + from_seed: String, + from_owner: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, + ) -> Instruction { + transfer_with_seed( + from_pubkey, + from_base, + from_seed, + from_owner, + to_pubkey, + lamports, + ) + } + + pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction { + allocate(pubkey, space) + } + + pub fn allocateWithSeed( + address: &Pubkey, + base: &Pubkey, + seed: &str, + space: u64, + owner: &Pubkey, + ) -> Instruction { + allocate_with_seed(address, base, seed, space, owner) + } + + pub fn createNonceAccount( + from_pubkey: &Pubkey, + nonce_pubkey: &Pubkey, + authority: &Pubkey, + lamports: u64, + ) -> js_sys::Array { + let instructions = create_nonce_account(from_pubkey, nonce_pubkey, authority, lamports); + instructions.into_iter().map(JsValue::from).collect() + } + + pub fn advanceNonceAccount(nonce_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction { + advance_nonce_account(nonce_pubkey, authorized_pubkey) + } + + pub fn withdrawNonceAccount( + nonce_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + to_pubkey: &Pubkey, + lamports: u64, + ) -> Instruction { + withdraw_nonce_account(nonce_pubkey, authorized_pubkey, to_pubkey, lamports) + } + + pub fn authorizeNonceAccount( + nonce_pubkey: &Pubkey, + authorized_pubkey: &Pubkey, + new_authority: &Pubkey, + ) -> Instruction { + authorize_nonce_account(nonce_pubkey, authorized_pubkey, new_authority) + } +}