From 20b6ee04a37b12aed1e5fe665a677712c71c18d5 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 26 Aug 2020 22:08:27 -0700 Subject: [PATCH] Remove token 1.0 --- token/program/Cargo.toml | 34 - token/program/Xargo.toml | 2 - token/program/build.rs | 65 - token/program/inc/token.h | 359 ----- token/program/program-id.md | 1 - token/program/src/entrypoint.rs | 24 - token/program/src/error.rs | 59 - token/program/src/instruction.rs | 708 --------- token/program/src/lib.rs | 28 - token/program/src/native_mint.rs | 24 - token/program/src/option.rs | 965 ------------ token/program/src/processor.rs | 2349 ------------------------------ token/program/src/state.rs | 92 -- 13 files changed, 4710 deletions(-) delete mode 100644 token/program/Cargo.toml delete mode 100644 token/program/Xargo.toml delete mode 100644 token/program/build.rs delete mode 100644 token/program/inc/token.h delete mode 100644 token/program/program-id.md delete mode 100644 token/program/src/entrypoint.rs delete mode 100644 token/program/src/error.rs delete mode 100644 token/program/src/instruction.rs delete mode 100644 token/program/src/lib.rs delete mode 100644 token/program/src/native_mint.rs delete mode 100644 token/program/src/option.rs delete mode 100644 token/program/src/processor.rs delete mode 100644 token/program/src/state.rs diff --git a/token/program/Cargo.toml b/token/program/Cargo.toml deleted file mode 100644 index cc045fc3..00000000 --- a/token/program/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ - -# Note: This crate must be built using do.sh - -[package] -name = "spl-token" -version = "1.0.8" -description = "Solana Program Library Token" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2018" -exclude = ["js/**"] - -[features] -no-entrypoint = [] -skip-no-mangle = ["solana-sdk/skip-no-mangle"] -program = ["solana-sdk/program"] -default = ["solana-sdk/default"] - -[dependencies] -num-derive = "0.3" -num-traits = "0.2" -remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.4", default-features = false, optional = true } -thiserror = "1.0" - -[dev-dependencies] -rand = { version = "0.7.0"} - -[build-dependencies] -cbindgen = "=0.14.2" - -[lib] -crate-type = ["cdylib", "lib"] diff --git a/token/program/Xargo.toml b/token/program/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/token/program/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/token/program/build.rs b/token/program/build.rs deleted file mode 100644 index 48620754..00000000 --- a/token/program/build.rs +++ /dev/null @@ -1,65 +0,0 @@ -extern crate cbindgen; - -use std::env; - -fn main() { - println!("cargo:rerun-if-env-changed=SPL_CBINDGEN"); - println!("cargo:rerun-if-changed=inc/token.h"); - if std::path::Path::new("inc/token.h").exists() && env::var("SPL_CBINDGEN").is_err() { - return; - } - - println!("cargo:warning=Generating inc/token.h"); - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let config = cbindgen::Config { - header: Some("/* Autogenerated SPL Token program C Bindings */".to_string()), - after_includes: Some(format!( - "{}{}{}", - format!( - "\n#define TOKEN_MAJOR_VERSION {}", - env!("CARGO_PKG_VERSION_MAJOR") - ), - format!( - "\n#define TOKEN_MINOR_VERSION {}", - env!("CARGO_PKG_VERSION_MINOR") - ), - format!( - "\n#define TOKEN_PATCH_VERSION {}", - env!("CARGO_PKG_VERSION_PATCH") - ) - )), - language: cbindgen::Language::C, - line_length: 80, - style: cbindgen::Style::Both, - tab_width: 4, - cpp_compat: true, - pragma_once: true, - enumeration: cbindgen::EnumConfig { - prefix_with_name: true, - ..cbindgen::EnumConfig::default() - }, - export: cbindgen::ExportConfig { - prefix: Some("Token_".to_string()), - include: vec![ - "TokenInstruction".to_string(), - "Mint".to_string(), - "Account".to_string(), - "Multisig".to_string(), - ], - exclude: vec!["DECIMALS".to_string()], - ..cbindgen::ExportConfig::default() - }, - parse: cbindgen::ParseConfig { - parse_deps: true, - include: Some(vec!["solana-sdk".to_string()]), - ..cbindgen::ParseConfig::default() - }, - ..cbindgen::Config::default() - }; - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_config(config) - .generate() - .unwrap() - .write_to_file("inc/token.h"); -} diff --git a/token/program/inc/token.h b/token/program/inc/token.h deleted file mode 100644 index d9ef5d29..00000000 --- a/token/program/inc/token.h +++ /dev/null @@ -1,359 +0,0 @@ -/* Autogenerated SPL Token program C Bindings */ - -#pragma once - -#include -#include -#include -#include - -#define TOKEN_MAJOR_VERSION 1 -#define TOKEN_MINOR_VERSION 0 -#define TOKEN_PATCH_VERSION 8 - -/** - * Maximum number of multisignature signers (max N) - */ -#define Token_MAX_SIGNERS 11 - -/** - * Minimum number of multisignature signers (min N) - */ -#define Token_MIN_SIGNERS 1 - -/** - * Instructions supported by the token program. - */ -typedef enum Token_TokenInstruction_Tag { - /** - * Initializes a new mint and optionally deposits all the newly minted tokens in an account. - * - * The `InitializeMint` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The mint to initialize. - * 1. - * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - * present then further minting is supported. - * - */ - Token_TokenInstruction_InitializeMint, - /** - * Initializes a new account to hold tokens. If this account is associated with the native mint - * then the token balance of the initialized account will be equal to the amount of SOL in the account. - * - * The `InitializeAccount` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The account to initialize. - * 1. `[]` The mint this account will be associated with. - * 2. `[]` The new account's owner/multisignature. - */ - Token_TokenInstruction_InitializeAccount, - /** - * Initializes a multisignature account with N provided signers. - * - * Multisignature accounts can used in place of any single owner/delegate accounts in any - * token instruction that require an owner/delegate to be present. The variant field represents the - * number of signers (M) required to validate this multisignature account. - * - * The `InitializeMultisig` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The multisignature account to initialize. - * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - */ - Token_TokenInstruction_InitializeMultisig, - /** - * Transfers tokens from one account to another either directly or via a delegate. If this - * account is associated with the native mint then equal amounts of SOL and Tokens will be - * transferred to the destination account. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The source account. - * 1. `[writable]` The destination account. - * 2. '[signer]' The source account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The source account. - * 1. `[writable]` The destination account. - * 2. '[]' The source account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Transfer, - /** - * Approves a delegate. A delegate is given the authority over - * tokens on behalf of the source account's owner. - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The source account. - * 1. `[]` The delegate. - * 2. `[signer]` The source account owner. - * - * * Multisignature owner - * 0. `[writable]` The source account. - * 1. `[]` The delegate. - * 2. '[]' The source account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts - */ - Token_TokenInstruction_Approve, - /** - * Revokes the delegate's authority. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The source account. - * 1. `[signer]` The source account owner. - * - * * Multisignature owner - * 0. `[writable]` The source account. - * 1. '[]' The source account's multisignature owner. - * 2. ..2+M '[signer]' M signer accounts - */ - Token_TokenInstruction_Revoke, - /** - * Sets a new owner of a mint or account. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[signer]` The owner of the mint or account. - * - * * Multisignature owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[]` The mint's or account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts - */ - Token_TokenInstruction_SetOwner, - /** - * Mints new tokens to an account. The native mint does not support minting. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's owner. - * - * * Multisignature owner - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_MintTo, - /** - * Burns tokens by removing them from an account. `Burn` does not support accounts - * associated with the native mint, use `CloseAccount` instead. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The account to burn from. - * 1. `[signer]` The account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The account to burn from. - * 1. `[]` The account's multisignature owner/delegate. - * 2. ..2+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Burn, - /** - * Close an account by transferring all its SOL to the destination account. - * Non-native accounts may only be closed if its token amount is zero. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. - * 2. `[signer]` The account's owner. - * - * * Multisignature owner - * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. - * 2. `[]` The account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_CloseAccount, -} Token_TokenInstruction_Tag; - -typedef struct Token_TokenInstruction_Token_InitializeMint_Body { - /** - * Initial amount of tokens to mint. - */ - uint64_t amount; - /** - * Number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_InitializeMint_Body; - -typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { - /** - * The number of signers (M) required to validate this multisignature account. - */ - uint8_t m; -} Token_TokenInstruction_Token_InitializeMultisig_Body; - -typedef struct Token_TokenInstruction_Token_Transfer_Body { - /** - * The amount of tokens to transfer. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Transfer_Body; - -typedef struct Token_TokenInstruction_Token_Approve_Body { - /** - * The amount of tokens the delegate is approved for. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Approve_Body; - -typedef struct Token_TokenInstruction_Token_MintTo_Body { - /** - * The amount of new tokens to mint. - */ - uint64_t amount; -} Token_TokenInstruction_Token_MintTo_Body; - -typedef struct Token_TokenInstruction_Token_Burn_Body { - /** - * The amount of tokens to burn. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Burn_Body; - -typedef struct Token_TokenInstruction { - Token_TokenInstruction_Tag tag; - union { - Token_TokenInstruction_Token_InitializeMint_Body initialize_mint; - Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; - Token_TokenInstruction_Token_Transfer_Body transfer; - Token_TokenInstruction_Token_Approve_Body approve; - Token_TokenInstruction_Token_MintTo_Body mint_to; - Token_TokenInstruction_Token_Burn_Body burn; - }; -} Token_TokenInstruction; - -typedef uint8_t Token_Pubkey[32]; - -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_Pubkey_Tag { - /** - * No value - */ - Token_COption_Pubkey_None_Pubkey, - /** - * Some value `T` - */ - Token_COption_Pubkey_Some_Pubkey, -} Token_COption_Pubkey_Tag; - -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - -typedef struct Token_COption_Pubkey { - Token_COption_Pubkey_Tag tag; - union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; - }; -} Token_COption_Pubkey; - -/** - * Mint data. - */ -typedef struct Token_Mint { - /** - * Optional owner, used to mint new tokens. The owner may only - * be provided during mint creation. If no owner is present then the mint - * has a fixed supply and no further tokens may be minted. - */ - Token_COption_Pubkey owner; - /** - * Number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; -} Token_Mint; - -/** - * Account data. - */ -typedef struct Token_Account { - /** - * The mint associated with this account - */ - Token_Pubkey mint; - /** - * The owner of this account. - */ - Token_Pubkey owner; - /** - * The amount of tokens this account holds. - */ - uint64_t amount; - /** - * If `delegate` is `Some` then `delegated_amount` represents - * the amount authorized by the delegate - */ - Token_COption_Pubkey delegate; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; - /** - * Is this a native token - */ - bool is_native; - /** - * The amount delegated - */ - uint64_t delegated_amount; -} Token_Account; - -/** - * Multisignature data. - */ -typedef struct Token_Multisig { - /** - * Number of signers required - */ - uint8_t m; - /** - * Number of valid signers - */ - uint8_t n; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; - /** - * Signer public keys - */ - Token_Pubkey signers[Token_MAX_SIGNERS]; -} Token_Multisig; diff --git a/token/program/program-id.md b/token/program/program-id.md deleted file mode 100644 index 77df2a04..00000000 --- a/token/program/program-id.md +++ /dev/null @@ -1 +0,0 @@ -TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o diff --git a/token/program/src/entrypoint.rs b/token/program/src/entrypoint.rs deleted file mode 100644 index b5594ca8..00000000 --- a/token/program/src/entrypoint.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Program entrypoint - -#![cfg(feature = "program")] -#![cfg(not(feature = "no-entrypoint"))] - -use crate::{error::TokenError, processor::Processor}; -use solana_sdk::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, - program_error::PrintProgramError, pubkey::Pubkey, -}; - -entrypoint!(process_instruction); -fn process_instruction<'a>( - program_id: &Pubkey, - accounts: &'a [AccountInfo<'a>], - instruction_data: &[u8], -) -> ProgramResult { - if let Err(error) = Processor::process(program_id, accounts, instruction_data) { - // catch the error so we can print it - error.print::(); - return Err(error); - } - Ok(()) -} diff --git a/token/program/src/error.rs b/token/program/src/error.rs deleted file mode 100644 index 46bf59c6..00000000 --- a/token/program/src/error.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Error types - -use num_derive::FromPrimitive; -use solana_sdk::{decode_error::DecodeError, program_error::ProgramError}; -use thiserror::Error; - -/// Errors that may be returned by the Token program. -#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] -pub enum TokenError { - /// Insufficient funds for the operation requested. - #[error("Insufficient funds")] - InsufficientFunds, - /// Account not associated with this Mint. - #[error("Account not associated with this Mint")] - MintMismatch, - /// Owner does not match. - #[error("Owner does not match")] - OwnerMismatch, - /// This token's supply is fixed and new tokens cannot be minted. - #[error("Fixed supply")] - FixedSupply, - /// The account cannot be initialized because it is already being used. - #[error("AlreadyInUse")] - AlreadyInUse, - /// An owner is required if initial supply is zero. - #[error("An owner is required if supply is zero")] - OwnerRequiredIfNoInitialSupply, - /// Invalid number of provided signers. - #[error("Invalid number of provided signers")] - InvalidNumberOfProvidedSigners, - /// Invalid number of required signers. - #[error("Invalid number of required signers")] - InvalidNumberOfRequiredSigners, - /// State is uninitialized. - #[error("State is unititialized")] - UninitializedState, - /// Instruction does not support native tokens - #[error("Instruction does not support native tokens")] - NativeNotSupported, - /// Instruction does not support non-native tokens - #[error("Instruction does not support non-native tokens")] - NonNativeNotSupported, - /// Invalid instruction - #[error("Invalid instruction")] - InvalidInstruction, - /// Operation overflowed - #[error("Operation overflowed")] - Overflow, -} -impl From for ProgramError { - fn from(e: TokenError) -> Self { - ProgramError::Custom(e as u32) - } -} -impl DecodeError for TokenError { - fn type_of() -> &'static str { - "TokenError" - } -} diff --git a/token/program/src/instruction.rs b/token/program/src/instruction.rs deleted file mode 100644 index fc8d493f..00000000 --- a/token/program/src/instruction.rs +++ /dev/null @@ -1,708 +0,0 @@ -//! Instruction types - -use crate::error::TokenError; -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - pubkey::Pubkey, -}; -use std::mem::size_of; - -/// Minimum number of multisignature signers (min N) -pub const MIN_SIGNERS: usize = 1; -/// Maximum number of multisignature signers (max N) -pub const MAX_SIGNERS: usize = 11; - -/// Instructions supported by the token program. -#[repr(C)] -#[derive(Clone, Debug, PartialEq)] -pub enum TokenInstruction { - /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. - /// - /// The `InitializeMint` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The mint to initialize. - /// 1. - /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - /// * If supply is zero: `[]` The owner/multisignature of the mint. - /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - /// present then further minting is supported. - /// - InitializeMint { - /// Initial amount of tokens to mint. - amount: u64, - /// Number of base 10 digits to the right of the decimal place. - decimals: u8, - }, - /// Initializes a new account to hold tokens. If this account is associated with the native mint - /// then the token balance of the initialized account will be equal to the amount of SOL in the account. - /// - /// The `InitializeAccount` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The account to initialize. - /// 1. `[]` The mint this account will be associated with. - /// 2. `[]` The new account's owner/multisignature. - InitializeAccount, - /// Initializes a multisignature account with N provided signers. - /// - /// Multisignature accounts can used in place of any single owner/delegate accounts in any - /// token instruction that require an owner/delegate to be present. The variant field represents the - /// number of signers (M) required to validate this multisignature account. - /// - /// The `InitializeMultisig` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The multisignature account to initialize. - /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - InitializeMultisig { - /// The number of signers (M) required to validate this multisignature account. - m: u8, - }, - /// Transfers tokens from one account to another either directly or via a delegate. If this - /// account is associated with the native mint then equal amounts of SOL and Tokens will be - /// transferred to the destination account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[signer]' The source account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[]' The source account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. - Transfer { - /// The amount of tokens to transfer. - amount: u64, - }, - /// Approves a delegate. A delegate is given the authority over - /// tokens on behalf of the source account's owner. - - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. '[]' The source account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - Approve { - /// The amount of tokens the delegate is approved for. - amount: u64, - }, - /// Revokes the delegate's authority. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. '[]' The source account's multisignature owner. - /// 2. ..2+M '[signer]' M signer accounts - Revoke, - /// Sets a new owner of a mint or account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[signer]` The owner of the mint or account. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[]` The mint's or account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - SetOwner, - /// Mints new tokens to an account. The native mint does not support minting. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[signer]` The mint's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[]` The mint's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - MintTo { - /// The amount of new tokens to mint. - amount: u64, - }, - /// Burns tokens by removing them from an account. `Burn` does not support accounts - /// associated with the native mint, use `CloseAccount` instead. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[signer]` The account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[]` The account's multisignature owner/delegate. - /// 2. ..2+M '[signer]' M signer accounts. - Burn { - /// The amount of tokens to burn. - amount: u64, - }, - /// Close an account by transferring all its SOL to the destination account. - /// Non-native accounts may only be closed if its token amount is zero. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[signer]` The account's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[]` The account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - CloseAccount, -} -impl TokenInstruction { - /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). - pub fn unpack(input: &[u8]) -> Result { - if input.len() < size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - Ok(match input[0] { - 0 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - let decimals = - unsafe { *(&input[size_of::() + size_of::()] as *const u8) }; - Self::InitializeMint { amount, decimals } - } - 1 => Self::InitializeAccount, - 2 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let m = unsafe { *(&input[1] as *const u8) }; - Self::InitializeMultisig { m } - } - 3 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Transfer { amount } - } - 4 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Approve { amount } - } - 5 => Self::Revoke, - 6 => Self::SetOwner, - 7 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::MintTo { amount } - } - 8 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Burn { amount } - } - 9 => Self::CloseAccount, - _ => return Err(TokenError::InvalidInstruction.into()), - }) - } - - /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. - pub fn pack(&self) -> Result, ProgramError> { - let mut output = vec![0u8; size_of::()]; - let mut output_len = 0; - match self { - Self::InitializeMint { amount, decimals } => { - output[output_len] = 0; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); - } - Self::InitializeAccount => { - output[output_len] = 1; - output_len += size_of::(); - } - Self::InitializeMultisig { m } => { - output[output_len] = 2; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) }; - *value = *m; - output_len += size_of::(); - } - Self::Transfer { amount } => { - output[output_len] = 3; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Approve { amount } => { - output[output_len] = 4; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Revoke => { - output[output_len] = 5; - output_len += size_of::(); - } - Self::SetOwner => { - output[output_len] = 6; - output_len += size_of::(); - } - Self::MintTo { amount } => { - output[output_len] = 7; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Burn { amount } => { - output[output_len] = 8; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::CloseAccount => { - output[output_len] = 9; - output_len += size_of::(); - } - } - - output.truncate(output_len); - Ok(output) - } -} - -/// Creates a 'InitializeMint' instruction. -pub fn initialize_mint( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: Option<&Pubkey>, - owner_pubkey: Option<&Pubkey>, - amount: u64, - decimals: u8, -) -> Result { - let data = TokenInstruction::InitializeMint { amount, decimals }.pack()?; - - let mut accounts = vec![AccountMeta::new(*mint_pubkey, false)]; - if amount != 0 { - match account_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new(*pubkey, false)), - None => { - return Err(ProgramError::NotEnoughAccountKeys); - } - } - } - match owner_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new_readonly(*pubkey, false)), - None => { - if amount == 0 { - return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); - } - } - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeAccount` instruction. -pub fn initialize_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - mint_pubkey: &Pubkey, - owner_pubkey: &Pubkey, -) -> Result { - let data = TokenInstruction::InitializeAccount.pack()?; - - let accounts = vec![ - AccountMeta::new(*account_pubkey, false), - AccountMeta::new_readonly(*mint_pubkey, false), - AccountMeta::new_readonly(*owner_pubkey, false), - ]; - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeMultisig` instruction. -pub fn initialize_multisig( - token_program_id: &Pubkey, - multisig_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - m: u8, -) -> Result { - if !is_valid_signer_index(m as usize) - || !is_valid_signer_index(signer_pubkeys.len()) - || m as usize > signer_pubkeys.len() - { - return Err(ProgramError::MissingRequiredSignature); - } - let data = TokenInstruction::InitializeMultisig { m }.pack()?; - - let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*multisig_pubkey, false)); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Transfer` instruction. -pub fn transfer( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - destination_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Transfer { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*source_pubkey, false)); - accounts.push(AccountMeta::new(*destination_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `Approve` instruction. -pub fn approve( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - delegate_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Approve { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*source_pubkey, false)); - accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Revoke` instruction. -pub fn revoke( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::Revoke.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `SetOwner` instruction. -pub fn set_owner( - token_program_id: &Pubkey, - owned_pubkey: &Pubkey, - new_owner_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::SetOwner.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*owned_pubkey, false)); - accounts.push(AccountMeta::new_readonly(*new_owner_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `MintTo` instruction. -pub fn mint_to( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::MintTo { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*mint_pubkey, false)); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Burn` instruction. -pub fn burn( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Burn { amount }.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `CloseAccount` instruction. -pub fn close_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - destination_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::CloseAccount.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new(*destination_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS -pub fn is_valid_signer_index(index: usize) -> bool { - !(index < MIN_SIGNERS || index > MAX_SIGNERS) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_instruction_packing() { - let check = TokenInstruction::InitializeMint { - amount: 1, - decimals: 2, - }; - let packed = check.pack().unwrap(); - let expect = Vec::from([0u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::InitializeAccount; - let packed = check.pack().unwrap(); - let expect = Vec::from([1u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::InitializeMultisig { m: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([2u8, 1]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Transfer { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Approve { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Revoke; - let packed = check.pack().unwrap(); - let expect = Vec::from([5u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::SetOwner; - let packed = check.pack().unwrap(); - let expect = Vec::from([6u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::MintTo { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Burn { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::CloseAccount; - let packed = check.pack().unwrap(); - let expect = Vec::from([9u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - } -} diff --git a/token/program/src/lib.rs b/token/program/src/lib.rs deleted file mode 100644 index bc39c05a..00000000 --- a/token/program/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![deny(missing_docs)] - -//! An ERC20-like Token program for the Solana blockchain - -pub mod entrypoint; -pub mod error; -pub mod instruction; -pub mod native_mint; -pub mod option; -pub mod processor; -pub mod state; - -// Export current solana-sdk types for downstream users who may also be building with a different -// solana-sdk version -pub use solana_sdk; - -/// Convert the UI representation of a token amount (using the decimals field defined in its mint) -/// to the raw amount -pub fn ui_amount_to_amount(ui_amount: f64, decimals: u8) -> u64 { - (ui_amount * 10_usize.pow(decimals as u32) as f64) as u64 -} - -/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) -pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { - amount as f64 / 10_usize.pow(decimals as u32) as f64 -} - -solana_sdk::declare_id!("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); diff --git a/token/program/src/native_mint.rs b/token/program/src/native_mint.rs deleted file mode 100644 index 41307b34..00000000 --- a/token/program/src/native_mint.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! The Mint that represents the native token - -/// There are 10^9 lamports in one SOL -pub const DECIMALS: u8 = 9; - -// The Mint for native SOL Token accounts -solana_sdk::declare_id!("So11111111111111111111111111111111111111111"); - -#[cfg(test)] -mod tests { - use super::*; - use solana_sdk::native_token::*; - - #[test] - fn test_decimals() { - assert!( - lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS).abs() < f64::EPSILON - ); - assert_eq!( - sol_to_lamports(42.), - crate::ui_amount_to_amount(42., DECIMALS) - ); - } -} diff --git a/token/program/src/option.rs b/token/program/src/option.rs deleted file mode 100644 index 914bb51c..00000000 --- a/token/program/src/option.rs +++ /dev/null @@ -1,965 +0,0 @@ -//! A C representation of Rust's `std::option::Option` used accross the FFI -//! boundary for Solana program interfaces -//! -//! This implementation mostly matches `std::option` except iterators since the iteration -//! trait requires returning `std::option::Option` - -use std::pin::Pin; -use std::{ - convert, hint, mem, - ops::{Deref, DerefMut}, -}; - -/// A C representation of Rust's `std::option::Option` -#[repr(C)] -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -pub enum COption { - /// No value - None, - /// Some value `T` - Some(T), -} - -///////////////////////////////////////////////////////////////////////////// -// Type implementation -///////////////////////////////////////////////////////////////////////////// - -impl COption { - ///////////////////////////////////////////////////////////////////////// - // Querying the contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns `true` if the option is a [`COption::Some`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_some(), true); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_some(), false); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] - #[inline] - pub fn is_some(&self) -> bool { - match *self { - COption::Some(_) => true, - COption::None => false, - } - } - - /// Returns `true` if the option is a [`COption::None`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_none(), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_none(), true); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - #[must_use = "if you intended to assert that this doesn't have a value, consider \ - `.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"] - #[inline] - pub fn is_none(&self) -> bool { - !self.is_some() - } - - /// Returns `true` if the option is a [`COption::Some`] value containing the given value. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(option_result_contains)] - /// - /// let x: COption = COption::Some(2); - /// assert_eq!(x.contains(&2), true); - /// - /// let x: COption = COption::Some(3); - /// assert_eq!(x.contains(&2), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.contains(&2), false); - /// ``` - #[must_use] - #[inline] - pub fn contains(&self, x: &U) -> bool - where - U: PartialEq, - { - match self { - COption::Some(y) => x == y, - COption::None => false, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for working with references - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `&COption` to `COption<&T>`. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, preserving the original. - /// The [`map`] method takes the `self` argument by value, consuming the original, - /// so this technique uses `as_ref` to first take an `COption` to a reference - /// to the value inside the original. - /// - /// [`map`]: enum.COption.html#method.map - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let text: COption = COption::Some("Hello, world!".to_string()); - /// // First, cast `COption` to `COption<&String>` with `as_ref`, - /// // then consume *that* with `map`, leaving `text` on the stack. - /// let text_length: COption = text.as_ref().map(|s| s.len()); - /// println!("still can print text: {:?}", text); - /// ``` - #[inline] - pub fn as_ref(&self) -> COption<&T> { - match *self { - COption::Some(ref x) => COption::Some(x), - COption::None => COption::None, - } - } - - /// Converts from `&mut COption` to `COption<&mut T>`. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// match x.as_mut() { - /// COption::Some(v) => *v = 42, - /// COption::None => {}, - /// } - /// assert_eq!(x, COption::Some(42)); - /// ``` - #[inline] - pub fn as_mut(&mut self) -> COption<&mut T> { - match *self { - COption::Some(ref mut x) => COption::Some(x), - COption::None => COption::None, - } - } - - /// Converts from [`Pin`]`<&COption>` to `COption<`[`Pin`]`<&T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_ref(self: Pin<&Self>) -> COption> { - unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } - } - - /// Converts from [`Pin`]`<&mut COption>` to `COption<`[`Pin`]`<&mut T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_mut(self: Pin<&mut Self>) -> COption> { - unsafe { - Pin::get_unchecked_mut(self) - .as_mut() - .map(|x| Pin::new_unchecked(x)) - } - } - - ///////////////////////////////////////////////////////////////////////// - // Getting to contained values - ///////////////////////////////////////////////////////////////////////// - - /// Unwraps an option, yielding the content of a [`COption::Some`]. - /// - /// # Panics - /// - /// Panics if the value is a [`COption::None`] with a custom panic message provided by - /// `msg`. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("value"); - /// assert_eq!(x.expect("the world is ending"), "value"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// x.expect("the world is ending"); // panics with `the world is ending` - /// ``` - #[inline] - pub fn expect(self, msg: &str) -> T { - match self { - COption::Some(val) => val, - COption::None => expect_failed(msg), - } - } - - /// Moves the value `v` out of the `COption` if it is [`COption::Some(v)`]. - /// - /// In general, because this function may panic, its use is discouraged. - /// Instead, prefer to use pattern matching and handle the [`COption::None`] - /// case explicitly. - /// - /// # Panics - /// - /// Panics if the self value equals [`COption::None`]. - /// - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("air"); - /// assert_eq!(x.unwrap(), "air"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.unwrap(), "air"); // fails - /// ``` - #[inline] - pub fn unwrap(self) -> T { - match self { - COption::Some(val) => val, - COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"), - } - } - - /// Returns the contained value or a default. - /// - /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`unwrap_or_else`], - /// which is lazily evaluated. - /// - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// - /// # Examples - /// - /// ```ignore - /// assert_eq!(COption::Some("car").unwrap_or("bike"), "car"); - /// assert_eq!(COption::None.unwrap_or("bike"), "bike"); - /// ``` - #[inline] - pub fn unwrap_or(self, def: T) -> T { - match self { - COption::Some(x) => x, - COption::None => def, - } - } - - /// Returns the contained value or computes it from a closure. - /// - /// # Examples - /// - /// ```ignore - /// let k = 10; - /// assert_eq!(COption::Some(4).unwrap_or_else(|| 2 * k), 4); - /// assert_eq!(COption::None.unwrap_or_else(|| 2 * k), 20); - /// ``` - #[inline] - pub fn unwrap_or_else T>(self, f: F) -> T { - match self { - COption::Some(x) => x, - COption::None => f(), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Transforming contained values - ///////////////////////////////////////////////////////////////////////// - - /// Maps an `COption` to `COption` by applying a function to a contained value. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, consuming the original: - /// - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let maybe_some_string = COption::Some(String::from("Hello, World!")); - /// // `COption::map` takes self *by value*, consuming `maybe_some_string` - /// let maybe_some_len = maybe_some_string.map(|s| s.len()); - /// - /// assert_eq!(maybe_some_len, COption::Some(13)); - /// ``` - #[inline] - pub fn map U>(self, f: F) -> COption { - match self { - COption::Some(x) => COption::Some(f(x)), - COption::None => COption::None, - } - } - - /// Applies a function to the contained value (if any), - /// or returns the provided default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or(42, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or(42, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or U>(self, default: U, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default, - } - } - - /// Applies a function to the contained value (if any), - /// or computes a default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let k = 21; - /// - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default(), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err)`]. - /// - /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`ok_or_else`], which is - /// lazily evaluated. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`ok_or_else`]: #method.ok_or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or(0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or(0), Err(0)); - /// ``` - #[inline] - pub fn ok_or(self, err: E) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err())`]. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or_else(|| 0), Err(0)); - /// ``` - #[inline] - pub fn ok_or_else E>(self, err: F) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err()), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Boolean operations on the values, eager and lazy - ///////////////////////////////////////////////////////////////////////// - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise returns `optb`. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::None); - /// - /// let x = COption::Some(2); - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::Some("foo")); - /// - /// let x: COption = COption::None; - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// ``` - #[inline] - pub fn and(self, optb: COption) -> COption { - match self { - COption::Some(_) => optb, - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `f` with the - /// wrapped value and returns the result. - /// - /// COption::Some languages call this operation flatmap. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// fn sq(x: u32) -> COption { COption::Some(x * x) } - /// fn nope(_: u32) -> COption { COption::None } - /// - /// assert_eq!(COption::Some(2).and_then(sq).and_then(sq), COption::Some(16)); - /// assert_eq!(COption::Some(2).and_then(sq).and_then(nope), COption::None); - /// assert_eq!(COption::Some(2).and_then(nope).and_then(sq), COption::None); - /// assert_eq!(COption::None.and_then(sq).and_then(sq), COption::None); - /// ``` - #[inline] - pub fn and_then COption>(self, f: F) -> COption { - match self { - COption::Some(x) => f(x), - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `predicate` - /// with the wrapped value and returns: - /// - /// - [`COption::Some(t)`] if `predicate` returns `true` (where `t` is the wrapped - /// value), and - /// - [`COption::None`] if `predicate` returns `false`. - /// - /// This function works similar to [`Iterator::filter()`]. You can imagine - /// the `COption` being an iterator over one or zero elements. `filter()` - /// lets you decide which elements to keep. - /// - /// # Examples - /// - /// ```ignore - /// fn is_even(n: &i32) -> bool { - /// n % 2 == 0 - /// } - /// - /// assert_eq!(COption::None.filter(is_even), COption::None); - /// assert_eq!(COption::Some(3).filter(is_even), COption::None); - /// assert_eq!(COption::Some(4).filter(is_even), COption::Some(4)); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(t)`]: #variant.COption::Some - /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter - #[inline] - pub fn filter bool>(self, predicate: P) -> Self { - if let COption::Some(x) = self { - if predicate(&x) { - return COption::Some(x); - } - } - COption::None - } - - /// Returns the option if it contains a value, otherwise returns `optb`. - /// - /// Arguments passed to `or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`or_else`], which is - /// lazily evaluated. - /// - /// [`or_else`]: #method.or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x = COption::None; - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(100)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::None); - /// ``` - #[inline] - pub fn or(self, optb: COption) -> COption { - match self { - COption::Some(_) => self, - COption::None => optb, - } - } - - /// Returns the option if it contains a value, otherwise calls `f` and - /// returns the result. - /// - /// # Examples - /// - /// ```ignore - /// fn nobody() -> COption<&'static str> { COption::None } - /// fn vikings() -> COption<&'static str> { COption::Some("vikings") } - /// - /// assert_eq!(COption::Some("barbarians").or_else(vikings), COption::Some("barbarians")); - /// assert_eq!(COption::None.or_else(vikings), COption::Some("vikings")); - /// assert_eq!(COption::None.or_else(nobody), COption::None); - /// ``` - #[inline] - pub fn or_else COption>(self, f: F) -> COption { - match self { - COption::Some(_) => self, - COption::None => f(), - } - } - - /// Returns [`COption::Some`] if exactly one of `self`, `optb` is [`COption::Some`], otherwise returns [`COption::None`]. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::None); - /// ``` - #[inline] - pub fn xor(self, optb: COption) -> COption { - match (self, optb) { - (COption::Some(a), COption::None) => COption::Some(a), - (COption::None, COption::Some(b)) => COption::Some(b), - _ => COption::None, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Entry-like operations to insert if COption::None and return a reference - ///////////////////////////////////////////////////////////////////////// - - /// Inserts `v` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert(5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) - } - - /// Inserts a value computed from `f` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert_with(|| 5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { - if let COption::None = *self { - *self = COption::Some(f()) - } - - match *self { - COption::Some(ref mut v) => v, - COption::None => unsafe { hint::unreachable_unchecked() }, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Misc - ///////////////////////////////////////////////////////////////////////// - - /// Replaces the actual value in the option by the value given in parameter, - /// returning the old value if present, - /// leaving a [`COption::Some`] in its place without deinitializing either one. - /// - /// [`COption::Some`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// let old = x.replace(5); - /// assert_eq!(x, COption::Some(5)); - /// assert_eq!(old, COption::Some(2)); - /// - /// let mut x = COption::None; - /// let old = x.replace(3); - /// assert_eq!(x, COption::Some(3)); - /// assert_eq!(old, COption::None); - /// ``` - #[inline] - pub fn replace(&mut self, value: T) -> COption { - mem::replace(self, COption::Some(value)) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&t| t) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&mut t| t) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption { - /// Returns the contained value or a default - /// - /// Consumes the `self` argument then, if [`COption::Some`], returns the contained - /// value, otherwise if [`COption::None`], returns the [default value] for that - /// type. - /// - /// # Examples - /// - /// Converts a string to an integer, turning poorly-formed strings - /// into 0 (the default value for integers). [`parse`] converts - /// a string to any other type that implements [`FromStr`], returning - /// [`COption::None`] on error. - /// - /// ```ignore - /// let good_year_from_input = "1909"; - /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); - /// - /// assert_eq!(1909, good_year); - /// assert_eq!(0, bad_year); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// [default value]: ../default/trait.Default.html#tymethod.default - /// [`parse`]: ../../std/primitive.str.html#method.parse - /// [`FromStr`]: ../../std/str/trait.FromStr.html - #[inline] - pub fn unwrap_or_default(self) -> T { - match self { - COption::Some(x) => x, - COption::None => Default::default(), - } - } -} - -impl COption { - /// Converts from `COption` (or `&COption`) to `COption<&T::Target>`. - /// - /// Leaves the original COption in-place, creating a new one with a reference - /// to the original one, additionally coercing the contents via [`Deref`]. - /// - /// [`Deref`]: ../../std/ops/trait.Deref.html - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref(), COption::Some("hey")); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.as_deref(), COption::None); - /// ``` - pub fn as_deref(&self) -> COption<&T::Target> { - self.as_ref().map(|t| t.deref()) - } -} - -impl COption { - /// Converts from `COption` (or `&mut COption`) to `COption<&mut T::Target>`. - /// - /// Leaves the original `COption` in-place, creating a new one containing a mutable reference to - /// the inner type's `Deref::Target` type. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let mut x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref_mut().map(|x| { - /// x.make_ascii_uppercase(); - /// x - /// }), COption::Some("HEY".to_owned().as_mut_str())); - /// ``` - pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> { - self.as_mut().map(|t| t.deref_mut()) - } -} - -impl COption> { - /// Transposes an `COption` of a [`Result`] into a [`Result`] of an `COption`. - /// - /// [`COption::None`] will be mapped to [`Ok`]`(`[`COption::None`]`)`. - /// [`COption::Some`]`(`[`Ok`]`(_))` and [`COption::Some`]`(`[`Err`]`(_))` will be mapped to - /// [`Ok`]`(`[`COption::Some`]`(_))` and [`Err`]`(_)`. - /// - /// [`COption::None`]: #variant.COption::None - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`COption::Some`]: #variant.COption::Some - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ```ignore - /// #[derive(Debug, Eq, PartialEq)] - /// struct COption::SomeErr; - /// - /// let x: Result, COption::SomeErr> = Ok(COption::Some(5)); - /// let y: COption> = COption::Some(Ok(5)); - /// assert_eq!(x, y.transpose()); - /// ``` - #[inline] - pub fn transpose(self) -> Result, E> { - match self { - COption::Some(Ok(x)) => Ok(COption::Some(x)), - COption::Some(Err(e)) => Err(e), - COption::None => Ok(COption::None), - } - } -} - -// This is a separate function to reduce the code size of .expect() itself. -#[inline(never)] -#[cold] -fn expect_failed(msg: &str) -> ! { - panic!("{}", msg) -} - -// // This is a separate function to reduce the code size of .expect_none() itself. -// #[inline(never)] -// #[cold] -// fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { -// panic!("{}: {:?}", msg, value) -// } - -///////////////////////////////////////////////////////////////////////////// -// Trait implementations -///////////////////////////////////////////////////////////////////////////// - -impl Clone for COption { - #[inline] - fn clone(&self) -> Self { - match self { - COption::Some(x) => COption::Some(x.clone()), - COption::None => COption::None, - } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (COption::Some(to), COption::Some(from)) => to.clone_from(from), - (to, from) => *to = from.clone(), - } - } -} - -impl Default for COption { - /// Returns [`COption::None`][COption::COption::None]. - /// - /// # Examples - /// - /// ```ignore - /// let opt: COption = COption::default(); - /// assert!(opt.is_none()); - /// ``` - #[inline] - fn default() -> COption { - COption::None - } -} - -impl From for COption { - fn from(val: T) -> COption { - COption::Some(val) - } -} - -impl<'a, T> From<&'a COption> for COption<&'a T> { - fn from(o: &'a COption) -> COption<&'a T> { - o.as_ref() - } -} - -impl<'a, T> From<&'a mut COption> for COption<&'a mut T> { - fn from(o: &'a mut COption) -> COption<&'a mut T> { - o.as_mut() - } -} - -impl COption> { - /// Converts from `COption>` to `COption` - /// - /// # Examples - /// Basic usage: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption> = COption::Some(COption::Some(6)); - /// assert_eq!(COption::Some(6), x.flatten()); - /// - /// let x: COption> = COption::Some(COption::None); - /// assert_eq!(COption::None, x.flatten()); - /// - /// let x: COption> = COption::None; - /// assert_eq!(COption::None, x.flatten()); - /// ``` - /// Flattening once only removes one level of nesting: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption>> = COption::Some(COption::Some(COption::Some(6))); - /// assert_eq!(COption::Some(COption::Some(6)), x.flatten()); - /// assert_eq!(COption::Some(6), x.flatten().flatten()); - /// ``` - #[inline] - pub fn flatten(self) -> COption { - self.and_then(convert::identity) - } -} diff --git a/token/program/src/processor.rs b/token/program/src/processor.rs deleted file mode 100644 index 238c0d13..00000000 --- a/token/program/src/processor.rs +++ /dev/null @@ -1,2349 +0,0 @@ -//! Program state processor - -#![cfg(feature = "program")] - -use crate::{ - error::TokenError, - instruction::{is_valid_signer_index, TokenInstruction}, - option::COption, - state::{self, Account, Mint, Multisig}, -}; -use num_traits::FromPrimitive; -use solana_sdk::{ - account_info::{next_account_info, AccountInfo}, - decode_error::DecodeError, - entrypoint::ProgramResult, - info, - program_error::{PrintProgramError, ProgramError}, - pubkey::Pubkey, -}; -use std::mem::size_of; - -/// Program state handler. -pub struct Processor {} -impl Processor { - /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. - pub fn process_initialize_mint( - accounts: &[AccountInfo], - amount: u64, - decimals: u8, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; - if mint.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - let owner = if amount != 0 { - let dest_account_info = next_account_info(account_info_iter)?; - let mut dest_account_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; - - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - dest_account.amount = amount; - - if let Ok(owner_info) = next_account_info(account_info_iter) { - COption::Some(*owner_info.key) - } else { - COption::None - } - } else if let Ok(owner_info) = next_account_info(account_info_iter) { - COption::Some(*owner_info.key) - } else { - return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); - }; - - mint.owner = owner; - mint.decimals = decimals; - mint.is_initialized = true; - - Ok(()) - } - - /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let new_account_info = next_account_info(account_info_iter)?; - let mint_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - let mut new_account_data = new_account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; - if account.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - account.mint = *mint_info.key; - account.owner = *owner_info.key; - account.delegate = COption::None; - account.delegated_amount = 0; - account.is_initialized = true; - if *mint_info.key == crate::native_mint::id() { - account.is_native = true; - account.amount = new_account_info.lamports(); - } else { - account.is_native = false; - account.amount = 0; - }; - - Ok(()) - } - - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. - pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let multisig_info = next_account_info(account_info_iter)?; - let mut multisig_account_data = multisig_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; - if multisig.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - let signer_infos = account_info_iter.as_slice(); - multisig.m = m; - multisig.n = signer_infos.len() as u8; - if !is_valid_signer_index(multisig.n as usize) { - return Err(TokenError::InvalidNumberOfProvidedSigners.into()); - } - if !is_valid_signer_index(multisig.m as usize) { - return Err(TokenError::InvalidNumberOfRequiredSigners.into()); - } - for (i, signer_info) in signer_infos.iter().enumerate() { - multisig.signers[i] = *signer_info.key; - } - multisig.is_initialized = true; - - Ok(()) - } - - /// Processes a [Transfer](enum.TokenInstruction.html) instruction. - pub fn process_transfer( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let mut dest_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_data)?; - - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.mint != dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount -= amount; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - }; - - source_account.amount -= amount; - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - - if source_account.is_native { - **source_account_info.lamports.borrow_mut() -= amount; - **dest_account_info.lamports.borrow_mut() += amount; - } - - Ok(()) - } - - /// Processes an [Approve](enum.TokenInstruction.html) instruction. - pub fn process_approve( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let delegate_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; - - source_account.delegate = COption::Some(*delegate_info.key); - source_account.delegated_amount = amount; - - Ok(()) - } - - /// Processes an [Revoke](enum.TokenInstruction.html) instruction. - pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let owner_info = next_account_info(account_info_iter)?; - - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; - - source_account.delegate = COption::None; - source_account.delegated_amount = 0; - - Ok(()) - } - - /// Processes a [SetOwner](enum.TokenInstruction.html) instruction. - pub fn process_set_owner(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let account_info = next_account_info(account_info_iter)?; - let new_owner_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - if account_info.data_len() == size_of::() { - let mut account_data = account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack(&mut account_data)?; - - Self::validate_owner( - program_id, - &account.owner, - authority_info, - account_info_iter.as_slice(), - )?; - - account.owner = *new_owner_info.key; - } else if account_info.data_len() == size_of::() { - let mut account_data = account_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack(&mut account_data)?; - - match mint.owner { - COption::Some(ref owner) => { - Self::validate_owner( - program_id, - owner, - authority_info, - account_info_iter.as_slice(), - )?; - } - COption::None => return Err(TokenError::FixedSupply.into()), - } - mint.owner = COption::Some(*new_owner_info.key); - } else { - return Err(ProgramError::InvalidArgument); - } - - Ok(()) - } - - /// Processes a [MintTo](enum.TokenInstruction.html) instruction. - pub fn process_mint_to( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - let mut dest_account_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; - - if dest_account.is_native { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mint: &mut Mint = state::unpack(&mut mint_info_data)?; - - match mint.owner { - COption::Some(owner) => { - Self::validate_owner(program_id, &owner, owner_info, account_info_iter.as_slice())?; - } - COption::None => { - return Err(TokenError::FixedSupply.into()); - } - } - - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - - Ok(()) - } - - /// Processes a [Burn](enum.TokenInstruction.html) instruction. - pub fn process_burn( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if source_account.is_native { - return Err(TokenError::NativeNotSupported.into()); - } - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount -= amount; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - } - - source_account.amount -= amount; - - Ok(()) - } - - /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. - pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if !source_account.is_native { - return Err(TokenError::NonNativeNotSupported.into()); - } - - Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?; - - **dest_account_info.lamports.borrow_mut() += source_account_info.lamports(); - **source_account_info.lamports.borrow_mut() = 0; - source_account.amount = 0; - - Ok(()) - } - - /// Processes an [Instruction](enum.Instruction.html). - pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { - let instruction = TokenInstruction::unpack(input)?; - - match instruction { - TokenInstruction::InitializeMint { amount, decimals } => { - info!("Instruction: InitializeMint"); - Self::process_initialize_mint(accounts, amount, decimals) - } - TokenInstruction::InitializeAccount => { - info!("Instruction: InitializeAccount"); - Self::process_initialize_account(accounts) - } - TokenInstruction::InitializeMultisig { m } => { - info!("Instruction: InitializeMultisig"); - Self::process_initialize_multisig(accounts, m) - } - TokenInstruction::Transfer { amount } => { - info!("Instruction: Transfer"); - Self::process_transfer(program_id, accounts, amount) - } - TokenInstruction::Approve { amount } => { - info!("Instruction: Approve"); - Self::process_approve(program_id, accounts, amount) - } - TokenInstruction::Revoke => { - info!("Instruction: Revoke"); - Self::process_revoke(program_id, accounts) - } - TokenInstruction::SetOwner => { - info!("Instruction: SetOwner"); - Self::process_set_owner(program_id, accounts) - } - TokenInstruction::MintTo { amount } => { - info!("Instruction: MintTo"); - Self::process_mint_to(program_id, accounts, amount) - } - TokenInstruction::Burn { amount } => { - info!("Instruction: Burn"); - Self::process_burn(program_id, accounts, amount) - } - TokenInstruction::CloseAccount => { - info!("Instruction: CloseAccount"); - Self::process_close_account(program_id, accounts) - } - } - } - - /// Validates owner(s) are present - pub fn validate_owner( - program_id: &Pubkey, - expected_owner: &Pubkey, - owner_account_info: &AccountInfo, - signers: &[AccountInfo], - ) -> ProgramResult { - if expected_owner != owner_account_info.key { - return Err(TokenError::OwnerMismatch.into()); - } - if program_id == owner_account_info.owner - && owner_account_info.data_len() == std::mem::size_of::() - { - let mut owner_data = owner_account_info.data.borrow_mut(); - let multisig: &mut Multisig = state::unpack(&mut owner_data)?; - let mut num_signers = 0; - for signer in signers.iter() { - if multisig.signers[0..multisig.n as usize].contains(signer.key) { - if !signer.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - num_signers += 1; - } - } - if num_signers < multisig.m { - return Err(ProgramError::MissingRequiredSignature); - } - } else if !owner_account_info.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - Ok(()) - } -} - -impl PrintProgramError for TokenError { - fn print(&self) - where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, - { - match self { - TokenError::InsufficientFunds => info!("Error: insufficient funds"), - TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), - TokenError::OwnerMismatch => info!("Error: owner does not match"), - TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), - TokenError::AlreadyInUse => info!("Error: account or token already in use"), - TokenError::OwnerRequiredIfNoInitialSupply => { - info!("Error: An owner is required if supply is zero") - } - TokenError::InvalidNumberOfProvidedSigners => { - info!("Error: Invalid number of provided signers") - } - TokenError::InvalidNumberOfRequiredSigners => { - info!("Error: Invalid number of required signers") - } - TokenError::UninitializedState => info!("Error: State is uninitialized"), - TokenError::NativeNotSupported => { - info!("Error: Instruction does not support native tokens") - } - TokenError::NonNativeNotSupported => { - info!("Error: Instruction does not support non-native tokens") - } - TokenError::InvalidInstruction => info!("Error: Invalid instruction"), - TokenError::Overflow => info!("Error: Operation overflowed"), - } - } -} - -// Pull in syscall stubs when building for non-BPF targets -#[cfg(not(target_arch = "bpf"))] -solana_sdk::program_stubs!(); - -#[cfg(test)] -mod tests { - use super::*; - use crate::instruction::{ - approve, burn, close_account, initialize_account, initialize_mint, initialize_multisig, - mint_to, revoke, set_owner, transfer, MAX_SIGNERS, - }; - use solana_sdk::{ - account::Account as SolanaAccount, account_info::create_is_signer_account_infos, - clock::Epoch, instruction::Instruction, - }; - - fn pubkey_rand() -> Pubkey { - Pubkey::new(&rand::random::<[u8; 32]>()) - } - - fn do_process_instruction( - instruction: Instruction, - accounts: Vec<&mut SolanaAccount>, - ) -> ProgramResult { - let mut meta = instruction - .accounts - .iter() - .zip(accounts) - .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) - .collect::>(); - - let account_infos = create_is_signer_account_infos(&mut meta); - Processor::process(&instruction.program_id, &account_infos, &instruction.data) - } - - fn return_token_error_as_program_error() -> ProgramError { - TokenError::MintMismatch.into() - } - - #[test] - fn test_print_error() { - let error = return_token_error_as_program_error(); - error.print::(); - } - - #[test] - #[should_panic(expected = "Custom(1)")] - fn test_error_unwrap() { - Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); - } - - #[test] - fn test_unique_account_sizes() { - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); - } - - #[test] - fn test_initialize_mint() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // account not created - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account] - ) - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // mismatch account - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2) - .unwrap(), - vec![&mut mint2_account, &mut account2_account] - ) - ); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account] - ) - ); - } - - #[test] - fn test_initialize_mint_account() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_transfer() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // mismatch mint - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &mismatch_key, - &owner_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut mismatch_account, - &mut owner_account, - ], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner2_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // transfer - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // transfer half back - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // transfer rest - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // transfer via delegate - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - .unwrap(); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - - // transfer rest - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 900, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds in source account via delegate - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - } - - #[test] - fn test_mintable_token_with_zero_supply() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create mint-able token without owner - let mut instruction = - initialize_mint(&program_id, &mint_key, None, Some(&owner_key), 0, 2).unwrap(); - instruction.accounts.pop(); - assert_eq!( - Err(TokenError::OwnerRequiredIfNoInitialSupply.into()), - do_process_instruction(instruction, vec![&mut mint_account]) - ); - - // create mint-able token with zero supply - let amount = 0; - let decimals = 2; - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - None, - Some(&owner_key), - amount, - decimals, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!( - *mint, - Mint { - owner: COption::Some(owner_key), - decimals, - is_initialized: true, - } - ); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); - } - - #[test] - fn test_approve() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // no owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner2_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner2_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // revoke delegate - do_process_instruction( - revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - } - - #[test] - fn test_set_owner() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let owner3_key = pubkey_rand(); - let mut owner3_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // invalid account - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &account_key, &owner_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner_account, - &mut owner2_account, - ], - ) - ); - - // owner did not sign - let mut instruction = - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - ); - - // set owner - do_process_instruction( - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&owner_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // wrong account - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &mint_key, &owner3_key, &owner2_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner3_account, &mut owner2_account], - ) - ); - - // owner did not sign - let mut instruction = - set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - ); - - // set owner - do_process_instruction( - set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - .unwrap(); - - // create new mint without owner - do_process_instruction( - initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2).unwrap(), - vec![&mut mint2_account, &mut account2_account], - ) - .unwrap(); - - // set owner for non-mint-able token - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &mint2_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_mint_to() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - let uninitialized_key = pubkey_rand(); - let mut uninitialized_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&owner_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); - - // missing signer - let mut instruction = - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - ); - - // mismatch account - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut mismatch_account, &mut owner_account], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // uninitialized destination account - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &uninitialized_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![ - &mut mint_account, - &mut uninitialized_account, - &mut owner_account, - ], - ) - ); - } - - #[test] - fn test_burn() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = burn(&program_id, &account_key, &delegate_key, &[], 42).unwrap(); - instruction.accounts[1].is_signer = false; - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - instruction, - vec![&mut account_account, &mut delegate_account], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner2_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner2_account], - ) - ); - - // burn - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 84, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // not a delegate of source account - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // burn via delegate - do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 84).unwrap(), - vec![&mut account_account, &mut delegate_account], - ) - .unwrap(); - - // match - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42 - 84); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 100).unwrap(), - vec![&mut account_account, &mut delegate_account], - ) - ); - } - - #[test] - fn test_multisig() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let multisig_key = pubkey_rand(); - let mut multisig_account = SolanaAccount::new(0, size_of::(), &program_id); - let multisig_delegate_key = pubkey_rand(); - let mut multisig_delegate_account = - SolanaAccount::new(0, size_of::(), &program_id); - let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; - let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); - let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; - - // single signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // multiple signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig( - &program_id, - &multisig_delegate_key, - &signer_key_refs, - MAX_SIGNERS as u8, - ) - .unwrap(), - vec![ - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // create account with multisig owner - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), - vec![&mut account, &mut mint_account, &mut multisig_account], - ) - .unwrap(); - - // create another account with multisig owner - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &mint_key, - &multisig_delegate_key, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut multisig_account, - ], - ) - .unwrap(); - - // create new m int with multisig owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&multisig_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account, &mut multisig_account], - ) - .unwrap(); - - // approve - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - approve( - &program_id, - &account_key, - &multisig_delegate_key, - &multisig_key, - &[&signer_keys[0]], - 100, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_delegate_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // mint to - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetOwner on mint - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_owner( - &program_id, - &mint_key, - &owner_key, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut mint_account, - &mut owner_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetOwner on account - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_owner( - &program_id, - &account_key, - &owner_key, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut account, - &mut owner_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - } - - #[test] - fn test_validate_owner() { - let program_id = pubkey_rand(); - let owner_key = pubkey_rand(); - let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; - for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { - *signer_key = pubkey_rand(); - } - let mut signer_lamports = 0; - let mut signer_data = vec![]; - let mut signers = vec![ - AccountInfo::new( - &owner_key, - true, - false, - &mut signer_lamports, - &mut signer_data, - &program_id, - false, - Epoch::default(), - ); - MAX_SIGNERS + 1 - ]; - for (signer, key) in signers.iter_mut().zip(&signer_keys) { - signer.key = key; - } - let mut lamports = 0; - let mut data = vec![0; size_of::()]; - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut data).unwrap(); - multisig.m = MAX_SIGNERS as u8; - multisig.n = MAX_SIGNERS as u8; - multisig.signers = signer_keys; - multisig.is_initialized = true; - let owner_account_info = AccountInfo::new( - &owner_key, - false, - false, - &mut lamports, - &mut data, - &program_id, - false, - Epoch::default(), - ); - - // full 11 of 11 - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 1 of 11 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 1; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 2:1 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 1; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) - ); - - // 0:11 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 0; - multisig.n = 11; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 2:11 but 0 provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[]) - ); - // 2:11 but 1 provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1]) - ); - - // 2:11, 2 from middle provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) - .unwrap(); - - // 11:11, one is not a signer - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - signers[5].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) - ); - signers[5].is_signer = true; - } - - #[test] - fn test_close_account() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - - // uninitialized - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - ); - - // initialize non-native account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 2); - - // close non-native account - assert_eq!( - Err(TokenError::NonNativeNotSupported.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - ); - assert_eq!(account_account.lamports, 42); - - // close native account - do_process_instruction( - close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account2_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 4); - } - - #[test] - fn test_native_token() { - let program_id = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, 0, &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 42); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 2); - - // mint_to unsupported - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - mint_to( - &program_id, - &crate::native_mint::id(), - &account_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - - // burn unsupported - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // initialize native account - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 40, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account_account.lamports, 2); - assert_eq!(account.amount, 2); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account2_account.lamports, 42); - assert_eq!(account.amount, 42); - - // close native account - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 4); - } - - #[test] - fn test_overflow() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_owner_key = pubkey_rand(); - let mut mint_owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create victim account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner2_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, None, Some(&mint_owner_key), 0, 2).unwrap(), - vec![&mut mint_account, &mut mint_owner_account], - ) - .unwrap(); - - // mint the max to attacker - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &mint_owner_key, - &[], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint the max to victum - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // mint one more - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - ); - - // mint back to large amount - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.amount = 0; - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // transfer to burn victim - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner2_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner2_account, - ], - ) - ); - } -} diff --git a/token/program/src/state.rs b/token/program/src/state.rs deleted file mode 100644 index 3b63f4fb..00000000 --- a/token/program/src/state.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! State transition types - -use crate::{error::TokenError, instruction::MAX_SIGNERS, option::COption}; -use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; -use std::mem::size_of; - -/// Mint data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Mint { - /// Optional owner, used to mint new tokens. The owner may only - /// be provided during mint creation. If no owner is present then the mint - /// has a fixed supply and no further tokens may be minted. - pub owner: COption, - /// Number of base 10 digits to the right of the decimal place. - pub decimals: u8, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, -} -impl IsInitialized for Mint { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Account data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Account { - /// The mint associated with this account - pub mint: Pubkey, - /// The owner of this account. - pub owner: Pubkey, - /// The amount of tokens this account holds. - pub amount: u64, - /// If `delegate` is `Some` then `delegated_amount` represents - /// the amount authorized by the delegate - pub delegate: COption, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, - /// Is this a native token - pub is_native: bool, - /// The amount delegated - pub delegated_amount: u64, -} -impl IsInitialized for Account { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Multisignature data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Multisig { - /// Number of signers required - pub m: u8, - /// Number of valid signers - pub n: u8, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, - /// Signer public keys - pub signers: [Pubkey; MAX_SIGNERS], -} -impl IsInitialized for Multisig { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Check is a token state is initialized -pub trait IsInitialized { - /// Is initialized - fn is_initialized(&self) -> bool; -} - -/// Unpacks a token state from a bytes buffer while assuring that the state is initialized. -pub fn unpack(input: &mut [u8]) -> Result<&mut T, ProgramError> { - let mut_ref: &mut T = unpack_unchecked(input)?; - if !mut_ref.is_initialized() { - return Err(TokenError::UninitializedState.into()); - } - Ok(mut_ref) -} -/// Unpacks a token state from a bytes buffer without checking that the state is initialized. -pub fn unpack_unchecked(input: &mut [u8]) -> Result<&mut T, ProgramError> { - if input.len() != size_of::() { - return Err(ProgramError::InvalidAccountData); - } - #[allow(clippy::cast_ptr_alignment)] - Ok(unsafe { &mut *(&mut input[0] as *mut u8 as *mut T) }) -}