diff --git a/Cargo.lock b/Cargo.lock index 05390cb2..26f2fb29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,6 +27,42 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + [[package]] name = "ahash" version = "0.4.7" @@ -425,6 +461,15 @@ dependencies = [ "chrono", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "clap" version = "2.34.0" @@ -607,6 +652,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -616,6 +670,7 @@ dependencies = [ "byteorder", "digest", "rand_core 0.5.1", + "serde", "subtle", "zeroize", ] @@ -1570,6 +1625,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.5.1", + "zeroize", +] + [[package]] name = "metaplex-token-metadata" version = "0.0.1" @@ -1913,6 +1980,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.15" @@ -3310,6 +3389,36 @@ dependencies = [ "thiserror", ] +[[package]] +name = "solana-zk-token-sdk" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2243307ab81fedd2f7e3b4b2b541ab0314cc4a26aebbf7ed6ddb839808cb244" +dependencies = [ + "aes-gcm-siv", + "arrayref", + "base64 0.13.0", + "bincode", + "bytemuck", + "byteorder", + "cipher", + "curve25519-dalek", + "getrandom 0.1.16", + "lazy_static", + "merlin", + "num-derive", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + [[package]] name = "solana_rbpf" version = "0.2.19" @@ -3706,6 +3815,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", + "solana-zk-token-sdk", "thiserror", ] @@ -4309,6 +4419,16 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "unreachable" version = "1.0.0" @@ -4567,9 +4687,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.4.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" dependencies = [ "zeroize_derive", ] diff --git a/token/program-2022/Cargo.toml b/token/program-2022/Cargo.toml index d1b4e5a4..b1ebd06f 100644 --- a/token/program-2022/Cargo.toml +++ b/token/program-2022/Cargo.toml @@ -19,6 +19,7 @@ num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" solana-program = "1.9.2" +solana-zk-token-sdk = "0.1.0" thiserror = "1.0" [dev-dependencies] diff --git a/token/program-2022/src/extension/confidential_transfer/instruction.rs b/token/program-2022/src/extension/confidential_transfer/instruction.rs new file mode 100644 index 00000000..741012d2 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/instruction.rs @@ -0,0 +1,56 @@ +use { + crate::{id, instruction::TokenInstruction, pod::*}, + bytemuck::Pod, + num_derive::{FromPrimitive, ToPrimitive}, + num_traits::{FromPrimitive, ToPrimitive}, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + }, +}; + +/// Confidential Transfer extension instructions +#[derive(Clone, Copy, Debug, FromPrimitive, ToPrimitive)] +#[repr(u8)] +pub enum ConfidentialTransferInstruction { + /// TODO: inline `zk_token_program::instructions::ZkTokenInstruction` here + Todo, +} + +pub(crate) fn decode_instruction_type( + input: &[u8], +) -> Result { + if input.is_empty() { + Err(ProgramError::InvalidInstructionData) + } else { + FromPrimitive::from_u8(input[0]).ok_or(ProgramError::InvalidInstructionData) + } +} + +pub(crate) fn decode_instruction_data(input: &[u8]) -> Result<&T, ProgramError> { + if input.is_empty() { + Err(ProgramError::InvalidInstructionData) + } else { + pod_from_bytes(&input[1..]) + } +} + +fn encode_instruction( + accounts: Vec, + instruction_type: ConfidentialTransferInstruction, + instruction_data: &T, +) -> Instruction { + let mut data = TokenInstruction::ConfidentialTransferExtension.pack(); + data.push(ToPrimitive::to_u8(&instruction_type).unwrap()); + data.extend_from_slice(bytemuck::bytes_of(instruction_data)); + Instruction { + program_id: id(), + accounts, + data, + } +} + +/// Create a `Todo` instruction +pub fn todo() -> Instruction { + encode_instruction(vec![], ConfidentialTransferInstruction::Todo, &()) +} diff --git a/token/program-2022/src/extension/confidential_transfer/mod.rs b/token/program-2022/src/extension/confidential_transfer/mod.rs new file mode 100644 index 00000000..e11f0fb7 --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/mod.rs @@ -0,0 +1,34 @@ +use { + crate::extension::{AccountType, Extension, ExtensionType}, + bytemuck::{Pod, Zeroable}, +}; + +/// Confidential Transfer Extension instructions +pub mod instruction; + +/// Confidential Transfer Extension processor +pub mod processor; + +/// Transfer auditor configuration +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct MintConfidentialTransferAuditor { + // TODO: inline `zk_token_program::state::Auditor` here +} + +impl Extension for MintConfidentialTransferAuditor { + const TYPE: ExtensionType = ExtensionType::MintConfidentialTransferAuditor; + const ACCOUNT_TYPE: AccountType = AccountType::Mint; +} + +/// Confidential account state +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] +pub struct AccountConfidentialState { + // TODO: inline `zk_token_program::state::ZkAccount` here +} + +impl Extension for AccountConfidentialState { + const TYPE: ExtensionType = ExtensionType::AccountConfidentialState; + const ACCOUNT_TYPE: AccountType = AccountType::Account; +} diff --git a/token/program-2022/src/extension/confidential_transfer/processor.rs b/token/program-2022/src/extension/confidential_transfer/processor.rs new file mode 100644 index 00000000..ce387ccf --- /dev/null +++ b/token/program-2022/src/extension/confidential_transfer/processor.rs @@ -0,0 +1,19 @@ +use { + super::instruction::*, + solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey}, +}; + +/// TODO: inline `zk_token_program::processor.rs` here +pub fn process_instruction( + _program_id: &Pubkey, + _accounts: &[AccountInfo], + input: &[u8], +) -> ProgramResult { + match decode_instruction_type(input)? { + ConfidentialTransferInstruction::Todo => { + let todo_data = decode_instruction_data::<()>(input)?; + msg!("Todo: {:?}", todo_data); + Ok(()) + } + } +} diff --git a/token/program-2022/src/extension/mod.rs b/token/program-2022/src/extension/mod.rs index 069fba08..55d7f285 100644 --- a/token/program-2022/src/extension/mod.rs +++ b/token/program-2022/src/extension/mod.rs @@ -3,6 +3,7 @@ use { crate::{ extension::{ + confidential_transfer::{AccountConfidentialState, MintConfidentialTransferAuditor}, mint_close_authority::MintCloseAuthority, transfer_fee::{AccountTransferFee, MintTransferFee}, }, @@ -21,8 +22,12 @@ use { }, }; -mod mint_close_authority; -mod transfer_fee; +/// Confidential Transfer extension +pub mod confidential_transfer; +/// Mint Close Authority extension +pub mod mint_close_authority; +/// Transfer Fee extension +pub mod transfer_fee; /// Length in TLV structure #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] @@ -332,6 +337,10 @@ pub enum ExtensionType { AccountTransferFee, /// Includes an optional mint close authority MintCloseAuthority, + /// Auditor configuration for confidential transfers + MintConfidentialTransferAuditor, + /// Confidential Account state + AccountConfidentialState, /// Padding extension used to make an account exactly Multisig::LEN, used for testing #[cfg(test)] AccountPaddingTest = u16::MAX - 1, @@ -361,6 +370,12 @@ impl ExtensionType { ExtensionType::MintTransferFee => pod_get_packed_len::(), ExtensionType::AccountTransferFee => pod_get_packed_len::(), ExtensionType::MintCloseAuthority => pod_get_packed_len::(), + ExtensionType::MintConfidentialTransferAuditor => { + pod_get_packed_len::() + } + ExtensionType::AccountConfidentialState => { + pod_get_packed_len::() + } #[cfg(test)] ExtensionType::AccountPaddingTest => pod_get_packed_len::(), #[cfg(test)] diff --git a/token/program-2022/src/instruction.rs b/token/program-2022/src/instruction.rs index 89f7c002..9e82da34 100644 --- a/token/program-2022/src/instruction.rs +++ b/token/program-2022/src/instruction.rs @@ -569,6 +569,12 @@ pub enum TokenInstruction { /// Maximum fee assessed on transfers maximum_fee: u64, }, + + /// The common instruction prefix for Confidential Transfer extension instructions. + /// + /// See `extension::confidential_transfer::instruction::ConfidentialTransferInstruction` for + /// further details about the extended instructions that share this instruction prefix + ConfidentialTransferExtension, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -755,6 +761,7 @@ impl TokenInstruction { maximum_fee, } } + 29 => Self::ConfidentialTransferExtension, _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -900,6 +907,9 @@ impl TokenInstruction { buf.extend_from_slice(&transfer_fee_basis_points.to_le_bytes()); buf.extend_from_slice(&maximum_fee.to_le_bytes()); } + &Self::ConfidentialTransferExtension => { + buf.push(29); + } }; buf } diff --git a/token/program-2022/src/processor.rs b/token/program-2022/src/processor.rs index bdefd9ad..c7462ed5 100644 --- a/token/program-2022/src/processor.rs +++ b/token/program-2022/src/processor.rs @@ -851,6 +851,13 @@ impl Processor { TokenInstruction::SetTransferFee { .. } => { unimplemented!(); } + TokenInstruction::ConfidentialTransferExtension => { + crate::extension::confidential_transfer::processor::process_instruction( + program_id, + accounts, + &input[1..], + ) + } } }