diff --git a/Cargo.lock b/Cargo.lock index 0aa2d47c..4bf4b12c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,19 +196,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" -[[package]] -name = "bn" -version = "0.4.4" -source = "git+https://github.com/garious/bn?rev=5c35c737ffabac9921310f53f48725216d59cbf1#5c35c737ffabac9921310f53f48725216d59cbf1" -dependencies = [ - "borsh", - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", -] - [[package]] name = "borsh" version = "0.7.1" @@ -477,12 +464,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" -[[package]] -name = "cpuid-bool" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" - [[package]] name = "crc32fast" version = "1.2.1" @@ -623,19 +604,6 @@ dependencies = [ "subtle 2.2.3", ] -[[package]] -name = "curve25519-dalek" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core", - "subtle 2.2.3", - "zeroize", -] - [[package]] name = "curve25519-dalek" version = "2.1.0" @@ -650,6 +618,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core", + "subtle 2.2.3", + "zeroize", +] + [[package]] name = "derivative" version = "2.1.1" @@ -746,7 +727,7 @@ dependencies = [ "ed25519", "rand", "serde", - "sha2 0.8.2", + "sha2", "zeroize", ] @@ -756,20 +737,6 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -[[package]] -name = "elgamal_bn" -version = "0.1.0" -source = "git+https://github.com/garious/elgamal_bn?rev=ba9bdcdb6cdd6fb8e74d0b8bc1b918bcd1b543a9#ba9bdcdb6cdd6fb8e74d0b8bc1b918bcd1b543a9" -dependencies = [ - "bn", - "borsh", - "clear_on_drop", - "rand", - "rand_core", - "rustc-hex", - "sha2 0.9.1", -] - [[package]] name = "elgamal_ristretto" version = "0.2.4" @@ -781,7 +748,7 @@ dependencies = [ "curve25519-dalek 2.1.0 (git+https://github.com/garious/curve25519-dalek?rev=60efef3553d6bf3d7f3b09b5f97acd54d72529ff)", "rand_core", "serde", - "sha2 0.8.2", + "sha2", "solana-sdk", "zkp", ] @@ -1393,7 +1360,7 @@ dependencies = [ "digest 0.8.1", "hmac-drbg", "rand", - "sha2 0.8.2", + "sha2", "subtle 2.2.3", "typenum", ] @@ -2105,12 +2072,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - [[package]] name = "rustc_version" version = "0.2.3" @@ -2329,19 +2290,6 @@ dependencies = [ "opaque-debug 0.2.3", ] -[[package]] -name = "sha2" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 0.1.10", - "cpuid-bool", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - [[package]] name = "sha3" version = "0.9.1" @@ -2552,7 +2500,7 @@ dependencies = [ "rustc_version", "serde", "serde_derive", - "sha2 0.8.2", + "sha2", "solana-frozen-abi-macro", "solana-logger", "thiserror", @@ -2654,7 +2602,7 @@ dependencies = [ "serde", "serde_bytes", "serde_derive", - "sha2 0.8.2", + "sha2", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", @@ -2773,7 +2721,7 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.8.2", + "sha2", "sha3", "solana-crate-features", "solana-frozen-abi", @@ -2941,22 +2889,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "spl-themis-bn" -version = "0.1.0" -dependencies = [ - "bincode", - "bn", - "borsh", - "elgamal_bn", - "getrandom", - "num-derive", - "num-traits", - "rand", - "solana-program", - "thiserror", -] - [[package]] name = "spl-themis-ristretto" version = "0.1.0" @@ -3238,7 +3170,7 @@ dependencies = [ "pbkdf2", "rand", "rustc-hash", - "sha2 0.8.2", + "sha2", "unicode-normalization", ] diff --git a/Cargo.toml b/Cargo.toml index 1b5162a3..16a7cf88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,10 @@ members = [ "token/cli", "token/program", "token/program-v3", - "themis/program_bn", "themis/program_ristretto", ] exclude = [ "shared-memory/client", "token/perf-monitor", - "themis/client_bn", "themis/client_ristretto", ] diff --git a/ci/script.sh b/ci/script.sh index 2d4114e9..e35b801b 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -56,7 +56,6 @@ done # Run client tests _ cargo test --manifest-path=shared-memory/client/Cargo.toml -- --nocapture _ cargo test --manifest-path=token/perf-monitor/Cargo.toml -- --nocapture -_ cargo test --manifest-path=themis/client_bn/Cargo.toml -- --nocapture _ cargo test --manifest-path=themis/client_ristretto/Cargo.toml -- --nocapture diff --git a/patch.crates-io.sh b/patch.crates-io.sh index 697c24e1..8bd937fc 100755 --- a/patch.crates-io.sh +++ b/patch.crates-io.sh @@ -12,7 +12,6 @@ fi workspace_crates=( Cargo.toml shared-memory/client/Cargo.toml - themis/client_bn/Cargo.toml themis/client_ristretto/Cargo.toml token/perf-monitor/Cargo.toml ) diff --git a/themis/client_bn/Cargo.toml b/themis/client_bn/Cargo.toml deleted file mode 100644 index 34b78f40..00000000 --- a/themis/client_bn/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ - -[package] -name = "spl-themis-bn-client" -version = "0.1.0" -description = "SPL THEMIS client" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2018" -exclude = ["js/**"] - -[dependencies] -bincode = "1.3" -borsh = "0.7.1" -bn = {git = "https://github.com/garious/bn", rev = "5c35c737ffabac9921310f53f48725216d59cbf1", default-features = false, features = ["borsh"]} -elgamal_bn = { git = "https://github.com/garious/elgamal_bn", rev = "ba9bdcdb6cdd6fb8e74d0b8bc1b918bcd1b543a9" } -futures = "0.3" -solana-banks-client = "1.4.3" -solana-cli-config = "1.4.3" -solana-sdk = "1.4.3" -spl-themis-bn = { version = "0.1.0", path = "../program_bn", features = ["exclude_entrypoint"]} -tarpc = { version = "0.21.1", features = ["full"] } -tokio = "0.2" -url = "2.1" - -[dev-dependencies] -separator = "0.4.1" -solana-banks-server = "1.4.3" -solana-bpf-loader-program = "1.4.3" -solana_rbpf = "=0.1.32" -solana-runtime = "1.4.3" - -[lib] -crate-type = ["cdylib", "lib"] diff --git a/themis/client_bn/build.rs b/themis/client_bn/build.rs deleted file mode 100644 index 99fa7f62..00000000 --- a/themis/client_bn/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -use std::process::Command; - -fn main() { - println!("cargo:warning=(not a warning) Building BPF themis program"); - Command::new("cargo") - .arg("build-bpf") - .status() - .expect("Failed to build BPF themis program") - .success(); -} diff --git a/themis/client_bn/examples/tps.rs b/themis/client_bn/examples/tps.rs deleted file mode 100644 index 13f2220e..00000000 --- a/themis/client_bn/examples/tps.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! Themis client - -use bn::Fr; -use solana_banks_client::start_tcp_client; -use solana_cli_config::{Config, CONFIG_FILE}; -use solana_sdk::signature::read_keypair_file; -use spl_themis_bn_client::test_e2e; -use std::path::Path; -use tokio::runtime::Runtime; -use url::Url; - -fn main() { - let config_file = CONFIG_FILE.as_ref().unwrap(); - let config = if Path::new(&config_file).exists() { - Config::load(&config_file).unwrap() - } else { - Config::default() - }; - let rpc_banks_url = Config::compute_rpc_banks_url(&config.json_rpc_url); - let url = Url::parse(&rpc_banks_url).unwrap(); - let host_port = (url.host_str().unwrap(), url.port().unwrap()); - - Runtime::new().unwrap().block_on(async { - let mut banks_client = start_tcp_client(host_port).await.unwrap(); - let policies = vec![Fr::new(1u64.into()).unwrap(), Fr::new(2u64.into()).unwrap()]; - let sender_keypair = read_keypair_file(&config.keypair_path).unwrap(); - test_e2e( - &mut banks_client, - sender_keypair, - policies, - 1_000, - Fr::new(3u64.into()).unwrap(), - ) - .await - .unwrap(); - }); -} diff --git a/themis/client_bn/src/lib.rs b/themis/client_bn/src/lib.rs deleted file mode 100644 index c38a0ec7..00000000 --- a/themis/client_bn/src/lib.rs +++ /dev/null @@ -1,357 +0,0 @@ -//! Themis client -use bn::{Fr, Group, G1}; -use elgamal_bn::{/*ciphertext::Ciphertext,*/ private::SecretKey, public::PublicKey}; -use futures::future::join_all; -use solana_banks_client::{BanksClient, BanksClientExt}; -use solana_sdk::{ - commitment_config::CommitmentLevel, - message::Message, - native_token::sol_to_lamports, - pubkey::Pubkey, - signature::{Keypair, Signer}, - system_instruction, - transaction::Transaction, -}; -use spl_themis_bn::{ - instruction, - state::generate_keys, // recover_scalar, User}, -}; -use std::{io, time::Instant}; -//use tarpc::context; - -/// For a single user, create interactions, calculate the aggregate, submit a proof, and verify it. -async fn run_user_workflow( - mut client: BanksClient, - sender_keypair: Keypair, - (_sk, pk): (SecretKey, PublicKey), - interactions: Vec<(G1, G1)>, - policies_pubkey: Pubkey, - _expected_scalar_aggregate: Fr, -) -> io::Result { - let sender_pubkey = sender_keypair.pubkey(); - let mut num_transactions = 0; - - // Create the users account - let user_keypair = Keypair::new(); - let user_pubkey = user_keypair.pubkey(); - let ixs = - instruction::create_user_account(&sender_pubkey, &user_pubkey, sol_to_lamports(0.001), pk); - let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await?; - let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - num_transactions += 1; - - // Send one interaction at a time to stay under the BPF instruction limit - for (i, interaction) in interactions.into_iter().enumerate() { - let interactions = vec![(i as u8, interaction)]; - let ix = instruction::submit_interactions(&user_pubkey, &policies_pubkey, interactions); - let msg = Message::new(&[ix], Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await?; - let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - num_transactions += 1; - } - - //let user_account = client - // .get_account_with_commitment_and_context( - // context::current(), - // user_pubkey, - // CommitmentLevel::Recent, - // ) - // .await - // .unwrap() - // .unwrap(); - //let user = User::deserialize(&user_account.data).unwrap(); - //let ciphertext = Ciphertext { - // points: user.fetch_encrypted_aggregate(), - // pk, - //}; - - //let decrypted_aggregate = sk.decrypt(&ciphertext); - let decrypted_aggregate = G1::one(); - //let scalar_aggregate = recover_scalar(decrypted_aggregate, 16); - //assert_eq!(scalar_aggregate, expected_scalar_aggregate); - - //let ((announcement_g, announcement_ctx), response) = - // sk.prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate).unwrap(); - let ((announcement_g, announcement_ctx), response) = - ((G1::one(), G1::one()), Fr::new(0.into()).unwrap()); - //sk.prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate).unwrap(); - - let ix = instruction::submit_proof_decryption( - &user_pubkey, - decrypted_aggregate, - announcement_g, - announcement_ctx, - response, - ); - let msg = Message::new(&[ix], Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await?; - let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - num_transactions += 1; - - //let user_account = client.get_account_with_commitment_and_context(context::current(), user_pubkey, CommitmentLevel::Recent).await.unwrap().unwrap(); - //let user = User::deserialize(&user_account.data).unwrap(); - //assert!(user.fetch_proof_verification()); - - Ok(num_transactions) -} - -pub async fn test_e2e( - client: &mut BanksClient, - sender_keypair: Keypair, - policies: Vec, - num_users: u64, - expected_scalar_aggregate: Fr, -) -> io::Result<()> { - let sender_pubkey = sender_keypair.pubkey(); - let policies_keypair = Keypair::new(); - let policies_pubkey = policies_keypair.pubkey(); - let policies_len = policies.len(); - - // Create the policies account - let mut ixs = instruction::create_policies_account( - &sender_pubkey, - &policies_pubkey, - sol_to_lamports(0.01), - policies.len() as u8, - ); - let policies_slice: Vec<_> = policies - .iter() - .enumerate() - .map(|(i, x)| (i as u8, *x)) - .collect(); - ixs.push(instruction::store_policies( - &policies_pubkey, - policies_slice, - )); - - let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await?; - let tx = Transaction::new(&[&sender_keypair, &policies_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - - // Send feepayer_keypairs some SOL - let feepayers: Vec<_> = (0..num_users).map(|_| Keypair::new()).collect(); - for feepayers in feepayers.chunks(20) { - println!("Seeding feepayer accounts..."); - let payments: Vec<_> = feepayers - .iter() - .map(|keypair| (keypair.pubkey(), sol_to_lamports(0.0011))) - .collect(); - let ixs = system_instruction::transfer_many(&sender_pubkey, &payments); - let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await.unwrap(); - let tx = Transaction::new(&[&sender_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - } - - println!("Starting benchmark..."); - let now = Instant::now(); - - let (sk, pk) = generate_keys(); - let interactions: Vec<_> = (0..policies_len) - .map(|_| pk.encrypt(&G1::one()).points) - .collect(); - - let futures: Vec<_> = feepayers - .into_iter() - .map(move |feepayer_keypair| { - run_user_workflow( - client.clone(), - feepayer_keypair, - (sk.clone(), pk), - interactions.clone(), - policies_pubkey, - expected_scalar_aggregate, - ) - }) - .collect(); - let results = join_all(futures).await; - let elapsed = now.elapsed(); - println!("Benchmark complete."); - - let num_transactions = results - .into_iter() - .map(|result| result.unwrap()) - .sum::(); - println!( - "{} transactions in {:?} ({} TPS)", - num_transactions, - elapsed, - num_transactions as f64 / elapsed.as_secs_f64() - ); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use solana_banks_client::start_client; - use solana_banks_server::banks_server::start_local_server; - use solana_runtime::{bank::Bank, bank_forks::BankForks}; - use solana_sdk::{ - account::{Account, KeyedAccount}, - account_info::AccountInfo, - genesis_config::create_genesis_config, - instruction::InstructionError, - program_error::ProgramError, - }; - use spl_themis_bn::processor::process_instruction; - use std::{ - collections::HashMap, - sync::{Arc, RwLock}, - {cell::RefCell, rc::Rc}, - }; - use tokio::runtime::Runtime; - - fn to_instruction_error(error: ProgramError) -> InstructionError { - match error { - ProgramError::Custom(err) => InstructionError::Custom(err), - ProgramError::InvalidArgument => InstructionError::InvalidArgument, - ProgramError::InvalidInstructionData => InstructionError::InvalidInstructionData, - ProgramError::InvalidAccountData => InstructionError::InvalidAccountData, - ProgramError::AccountDataTooSmall => InstructionError::AccountDataTooSmall, - ProgramError::InsufficientFunds => InstructionError::InsufficientFunds, - ProgramError::IncorrectProgramId => InstructionError::IncorrectProgramId, - ProgramError::MissingRequiredSignature => InstructionError::MissingRequiredSignature, - ProgramError::AccountAlreadyInitialized => InstructionError::AccountAlreadyInitialized, - ProgramError::UninitializedAccount => InstructionError::UninitializedAccount, - ProgramError::NotEnoughAccountKeys => InstructionError::NotEnoughAccountKeys, - ProgramError::AccountBorrowFailed => InstructionError::AccountBorrowFailed, - ProgramError::MaxSeedLengthExceeded => InstructionError::MaxSeedLengthExceeded, - ProgramError::InvalidSeeds => InstructionError::InvalidSeeds, - } - } - - // Same as process_instruction, but but can be used as a builtin program. Handy for unit-testing. - pub fn process_instruction_native( - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - input: &[u8], - ) -> Result<(), InstructionError> { - // Copy all the accounts into a HashMap to ensure there are no duplicates - let mut accounts: HashMap = keyed_accounts - .iter() - .map(|ka| (*ka.unsigned_key(), ka.account.borrow().clone())) - .collect(); - - // Create shared references to each account's lamports/data/owner - let account_refs: HashMap<_, _> = accounts - .iter_mut() - .map(|(key, account)| { - ( - *key, - ( - Rc::new(RefCell::new(&mut account.lamports)), - Rc::new(RefCell::new(&mut account.data[..])), - &account.owner, - ), - ) - }) - .collect(); - - // Create AccountInfos - let account_infos: Vec = keyed_accounts - .iter() - .map(|keyed_account| { - let key = keyed_account.unsigned_key(); - let (lamports, data, owner) = &account_refs[key]; - AccountInfo { - key, - is_signer: keyed_account.signer_key().is_some(), - is_writable: keyed_account.is_writable(), - lamports: lamports.clone(), - data: data.clone(), - owner, - executable: keyed_account.executable().unwrap(), - rent_epoch: keyed_account.rent_epoch().unwrap(), - } - }) - .collect(); - - // Execute the BPF entrypoint - process_instruction(program_id, &account_infos, input).map_err(to_instruction_error)?; - - // Commit changes to the KeyedAccounts - for keyed_account in keyed_accounts { - let mut account = keyed_account.account.borrow_mut(); - let key = keyed_account.unsigned_key(); - let (lamports, data, _owner) = &account_refs[key]; - account.lamports = **lamports.borrow(); - account.data = data.borrow().to_vec(); - } - - Ok(()) - } - - #[test] - fn test_local_e2e_2ads() { - let (genesis_config, sender_keypair) = create_genesis_config(sol_to_lamports(9_000_000.0)); - let mut bank = Bank::new(&genesis_config); - bank.add_builtin_program("Themis", spl_themis_bn::id(), process_instruction_native); - let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); - Runtime::new().unwrap().block_on(async { - let transport = start_local_server(&bank_forks).await; - let mut banks_client = start_client(transport).await.unwrap(); - let policies = vec![Fr::new(1u64.into()).unwrap(), Fr::new(2u64.into()).unwrap()]; - test_e2e( - &mut banks_client, - sender_keypair, - policies, - 10, - Fr::new(3u64.into()).unwrap(), - ) - .await - .unwrap(); - }); - } -} diff --git a/themis/client_bn/tests/assert_instruction_count.rs b/themis/client_bn/tests/assert_instruction_count.rs deleted file mode 100644 index e419c585..00000000 --- a/themis/client_bn/tests/assert_instruction_count.rs +++ /dev/null @@ -1,390 +0,0 @@ -use bn::{Fr, Group, G1}; -use borsh::BorshSerialize; -use elgamal_bn::ciphertext::Ciphertext; -use separator::Separatable; -use solana_bpf_loader_program::{ - create_vm, - serialization::{deserialize_parameters, serialize_parameters}, -}; -use solana_rbpf::vm::{EbpfVm, InstructionMeter}; -use solana_runtime::process_instruction::{ - ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction, -}; -use solana_sdk::{ - account::{Account as SolanaAccount, KeyedAccount}, - bpf_loader, - entrypoint::SUCCESS, - instruction::{CompiledInstruction, InstructionError}, - message::Message, - pubkey::Pubkey, -}; -use spl_themis_bn::{ - instruction::ThemisInstruction, - state::{generate_keys, /*recover_scalar,*/ Policies, User}, -}; -use std::{cell::RefCell, fs::File, io::Read, path::PathBuf, rc::Rc, sync::Arc}; - -fn load_program(name: &str) -> Vec { - let mut path = PathBuf::new(); - path.push("../../target/bpfel-unknown-unknown/release"); - path.push(name); - path.set_extension("so"); - let mut file = File::open(path).unwrap(); - - let mut program = Vec::new(); - file.read_to_end(&mut program).unwrap(); - program -} - -fn run_program( - program_id: &Pubkey, - parameter_accounts: &[KeyedAccount], - instruction_data: &[u8], -) -> Result { - let mut program_account = SolanaAccount::default(); - program_account.data = load_program("spl_themis_bn"); - let loader_id = bpf_loader::id(); - let mut invoke_context = MockInvokeContext::default(); - let executable = EbpfVm::::create_executable_from_elf( - &&program_account.data, - None, - ) - .unwrap(); - let (mut vm, heap_region) = create_vm( - &loader_id, - executable.as_ref(), - parameter_accounts, - &mut invoke_context, - ) - .unwrap(); - let mut parameter_bytes = serialize_parameters( - &loader_id, - program_id, - parameter_accounts, - &instruction_data, - ) - .unwrap(); - assert_eq!( - SUCCESS, - vm.execute_program(parameter_bytes.as_mut_slice(), &[], &[heap_region]) - .unwrap() - ); - deserialize_parameters(&loader_id, parameter_accounts, ¶meter_bytes).unwrap(); - Ok(vm.get_total_instruction_count()) -} - -#[test] -fn assert_instruction_count() { - let program_id = Pubkey::new_unique(); - - // Create new policies - let policies_key = Pubkey::new_unique(); - let scalars = vec![Fr::new(1u64.into()).unwrap(), Fr::new(2u64.into()).unwrap()]; - //let scalars = vec![ - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), //10 - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), // 2 * 10 - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), - // Fr::new(1u64.into()).unwrap(), //10 - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), - // Fr::new(2u64.into()).unwrap(), // 2 * 10 - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - // Fr::new(0u64.into()).unwrap(), - //]; - let num_scalars = scalars.len(); - - let (sk, pk) = generate_keys(); - let encrypted_interactions: Vec<_> = (0..num_scalars) - .map(|i| (i as u8, pk.encrypt(&G1::one()).points)) - .collect(); - - let policies_account = SolanaAccount::new_ref( - 0, - Policies { - is_initialized: true, - num_scalars: num_scalars as u8, - scalars: scalars.clone(), - } - .try_to_vec() - .unwrap() - .len(), - &program_id, - ); - let instruction_data = ThemisInstruction::InitializePoliciesAccount { - num_scalars: num_scalars as u8, - } - .serialize() - .unwrap(); - let parameter_accounts = vec![KeyedAccount::new(&policies_key, false, &policies_account)]; - let initialize_policies_count = - run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); - - // Create user account - let user_key = Pubkey::new_unique(); - let user_account = - SolanaAccount::new_ref(0, User::default().try_to_vec().unwrap().len(), &program_id); - let instruction_data = ThemisInstruction::InitializeUserAccount { public_key: pk } - .serialize() - .unwrap(); - let parameter_accounts = vec![KeyedAccount::new(&user_key, false, &user_account)]; - let initialize_user_count = - run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); - - // Calculate Aggregate - let instruction_data = ThemisInstruction::SubmitInteractions { - encrypted_interactions, - } - .serialize() - .unwrap(); - let parameter_accounts = vec![ - KeyedAccount::new(&user_key, true, &user_account), - KeyedAccount::new(&policies_key, false, &policies_account), - ]; - let calculate_aggregate_count = - run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); - - // Submit proof decryption - let user = User::deserialize(&user_account.try_borrow().unwrap().data).unwrap(); - let encrypted_point = user.fetch_encrypted_aggregate(); - let ciphertext = Ciphertext { - points: encrypted_point, - pk, - }; - - let decrypted_aggregate = sk.decrypt(&ciphertext); - //let scalar_aggregate = recover_scalar(decrypted_aggregate, 16); - //let expected_scalar_aggregate = Fr::new(3u64.into()).unwrap(); - //assert_eq!(scalar_aggregate, expected_scalar_aggregate); - - let (announcement, response) = sk - .prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate) - .unwrap(); - - let instruction_data = ThemisInstruction::SubmitProofDecryption { - plaintext: decrypted_aggregate, - announcement: Box::new(announcement), - response, - } - .serialize() - .unwrap(); - let parameter_accounts = vec![KeyedAccount::new(&user_key, true, &user_account)]; - let proof_decryption_count = - run_program(&program_id, ¶meter_accounts[..], &instruction_data).unwrap(); - - const BASELINE_NEW_POLICIES_COUNT: u64 = 80_000; // last known 75,796 @ 128, 4,675 @ 2 - const BASELINE_INITIALIZE_USER_COUNT: u64 = 22_000; // last known 19,868 - const BASELINE_CALCULATE_AGGREGATE_COUNT: u64 = 15_000_000; // last known 13,061,884 - const BASELINE_PROOF_DECRYPTION_COUNT: u64 = 60_000_000; // last known 13,167,140 - - println!("BPF instructions executed"); - println!( - " InitializePolicies({}): {} ({:?})", - num_scalars, - initialize_policies_count.separated_string(), - BASELINE_NEW_POLICIES_COUNT - ); - println!( - " InitializeUserAccount: {} ({:?})", - initialize_user_count.separated_string(), - BASELINE_INITIALIZE_USER_COUNT - ); - println!( - " CalculateAggregate: {} ({:?})", - calculate_aggregate_count.separated_string(), - BASELINE_CALCULATE_AGGREGATE_COUNT - ); - println!( - " SubmitProofDecryption: {} ({:?})", - proof_decryption_count.separated_string(), - BASELINE_PROOF_DECRYPTION_COUNT - ); - - assert!(initialize_policies_count <= BASELINE_NEW_POLICIES_COUNT); - assert!(initialize_user_count <= BASELINE_INITIALIZE_USER_COUNT); - assert!(calculate_aggregate_count <= BASELINE_CALCULATE_AGGREGATE_COUNT); - assert!(proof_decryption_count <= BASELINE_PROOF_DECRYPTION_COUNT); -} - -// Mock InvokeContext - -#[derive(Debug)] -struct MockInvokeContext { - pub key: Pubkey, - pub logger: MockLogger, - pub compute_meter: MockComputeMeter, - compute_budget: ComputeBudget, -} - -impl Default for MockInvokeContext { - fn default() -> Self { - Self { - key: Pubkey::default(), - logger: MockLogger::default(), - compute_meter: MockComputeMeter::default(), - compute_budget: ComputeBudget { - max_invoke_depth: 10, - ..ComputeBudget::default() - }, - } - } -} - -impl InvokeContext for MockInvokeContext { - fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { - Ok(()) - } - fn pop(&mut self) {} - fn verify_and_update( - &mut self, - _message: &Message, - _instruction: &CompiledInstruction, - _accounts: &[Rc>], - ) -> Result<(), InstructionError> { - Ok(()) - } - fn get_caller(&self) -> Result<&Pubkey, InstructionError> { - Ok(&self.key) - } - fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] { - &[] - } - fn get_logger(&self) -> Rc> { - Rc::new(RefCell::new(self.logger.clone())) - } - fn get_compute_budget(&self) -> &ComputeBudget { - &self.compute_budget - } - fn get_compute_meter(&self) -> Rc> { - Rc::new(RefCell::new(self.compute_meter.clone())) - } - fn add_executor(&mut self, _pubkey: &Pubkey, _executor: Arc) {} - fn get_executor(&mut self, _pubkey: &Pubkey) -> Option> { - None - } - fn record_instruction(&self, _: &solana_sdk::instruction::Instruction) { - todo!() - } - fn is_feature_active(&self, _: &solana_sdk::pubkey::Pubkey) -> bool { - true - } -} - -#[derive(Debug, Default, Clone)] -struct MockComputeMeter {} -impl ComputeMeter for MockComputeMeter { - fn consume(&mut self, _amount: u64) -> Result<(), InstructionError> { - Ok(()) - } - fn get_remaining(&self) -> u64 { - u64::MAX - } -} -#[derive(Debug, Default, Clone)] -struct MockLogger {} -impl Logger for MockLogger { - fn log_enabled(&self) -> bool { - true - } - fn log(&mut self, message: &str) { - println!("{}", message); - } -} - -struct TestInstructionMeter {} -impl InstructionMeter for TestInstructionMeter { - fn consume(&mut self, _amount: u64) {} - fn get_remaining(&self) -> u64 { - u64::MAX - } -} diff --git a/themis/client_ristretto/src/lib.rs b/themis/client_ristretto/src/lib.rs index f3ea1f01..3b15a246 100644 --- a/themis/client_ristretto/src/lib.rs +++ b/themis/client_ristretto/src/lib.rs @@ -13,6 +13,7 @@ use solana_sdk::{ signature::{Keypair, Signer}, system_instruction, transaction::Transaction, + transport, }; use spl_themis_ristretto::{ instruction, @@ -21,6 +22,32 @@ use spl_themis_ristretto::{ use std::{io, time::Instant}; //use tarpc::context; +fn assert_transaction_size(tx: &Transaction) { + let tx_size = bincode::serialize(&tx).unwrap().len(); + assert!( + tx_size <= 1200, + "transaction over 1200 bytes: {} bytes", + tx_size + ); +} + +// TODO: Add this to BanksClient +pub async fn process_transactions_with_commitment( + client: &mut BanksClient, + transactions: Vec, + commitment: CommitmentLevel, +) -> transport::Result<()> { + let mut clients: Vec<_> = transactions.iter().map(|_| client.clone()).collect(); + let futures = clients + .iter_mut() + .zip(transactions) + .map(|(client, transaction)| { + client.process_transaction_with_commitment(transaction, commitment) + }); + let statuses = futures::future::join_all(futures).await; + statuses.into_iter().collect() // Convert Vec> to Result> +} + /// For a single user, create interactions, calculate the aggregate, submit a proof, and verify it. async fn run_user_workflow( mut client: BanksClient, @@ -44,15 +71,10 @@ async fn run_user_workflow( sol_to_lamports(0.001), pk, ); - let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); + let msg = Message::new(&ixs, Some(&sender_pubkey)); let recent_blockhash = client.get_recent_blockhash().await?; let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); + assert_transaction_size(&tx); client .process_transaction_with_commitment(tx, CommitmentLevel::Recent) .await @@ -68,15 +90,10 @@ async fn run_user_workflow( &policies_pubkey, interactions, ); - let msg = Message::new(&[ix], Some(&sender_keypair.pubkey())); + let msg = Message::new(&[ix], Some(&sender_pubkey)); let recent_blockhash = client.get_recent_blockhash().await?; let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); + assert_transaction_size(&tx); client .process_transaction_with_commitment(tx, CommitmentLevel::Recent) .await @@ -110,7 +127,6 @@ async fn run_user_workflow( (RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_POINT), 0u64.into(), ); - //sk.prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate).unwrap(); let ix = instruction::submit_proof_decryption( program_id, @@ -120,15 +136,10 @@ async fn run_user_workflow( announcement_ctx, response, ); - let msg = Message::new(&[ix], Some(&sender_keypair.pubkey())); + let msg = Message::new(&[ix], Some(&sender_pubkey)); let recent_blockhash = client.get_recent_blockhash().await?; let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); + assert_transaction_size(&tx); client .process_transaction_with_commitment(tx, CommitmentLevel::Recent) .await @@ -174,43 +185,33 @@ pub async fn test_e2e( policies_slice, )); - let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); + let msg = Message::new(&ixs, Some(&sender_pubkey)); let recent_blockhash = client.get_recent_blockhash().await?; let tx = Transaction::new(&[&sender_keypair, &policies_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); + assert_transaction_size(&tx); client .process_transaction_with_commitment(tx, CommitmentLevel::Recent) .await .unwrap(); // Send feepayer_keypairs some SOL + println!("Seeding feepayer accounts..."); let feepayers: Vec<_> = (0..num_users).map(|_| Keypair::new()).collect(); - for feepayers in feepayers.chunks(20) { - println!("Seeding feepayer accounts..."); + let recent_blockhash = client.get_recent_blockhash().await.unwrap(); + let txs: Vec<_> = feepayers.chunks(20).map(|feepayers| { let payments: Vec<_> = feepayers .iter() .map(|keypair| (keypair.pubkey(), sol_to_lamports(0.0011))) .collect(); let ixs = system_instruction::transfer_many(&sender_pubkey, &payments); let msg = Message::new(&ixs, Some(&sender_keypair.pubkey())); - let recent_blockhash = client.get_recent_blockhash().await.unwrap(); let tx = Transaction::new(&[&sender_keypair], msg, recent_blockhash); - let tx_size = bincode::serialize(&tx).unwrap().len(); - assert!( - tx_size <= 1200, - "transaction over 1200 bytes: {} bytes", - tx_size - ); - client - .process_transaction_with_commitment(tx, CommitmentLevel::Recent) - .await - .unwrap(); - } + assert_transaction_size(&tx); + tx + }).collect(); + process_transactions_with_commitment(client, txs, CommitmentLevel::Recent) + .await + .unwrap(); println!("Starting benchmark..."); let now = Instant::now(); diff --git a/themis/client_ristretto/tests/e2e.rs b/themis/client_ristretto/tests/e2e.rs index 39c8c68f..bc7201e2 100644 --- a/themis/client_ristretto/tests/e2e.rs +++ b/themis/client_ristretto/tests/e2e.rs @@ -12,7 +12,7 @@ use solana_sdk::{ transaction::Transaction, transport, }; -use spl_themis_ristretto_client::test_e2e; +use spl_themis_ristretto_client::{test_e2e, process_transactions_with_commitment}; use std::{ fs::{remove_dir_all, File}, io::Read, @@ -34,23 +34,6 @@ fn load_program(name: &str) -> Vec { program } -// TODO: Add this to BanksClient -async fn process_transactions_with_commitment( - client: &mut BanksClient, - transactions: Vec, - commitment: CommitmentLevel, -) -> transport::Result<()> { - let mut clients: Vec<_> = transactions.iter().map(|_| client.clone()).collect(); - let futures = clients - .iter_mut() - .zip(transactions) - .map(|(client, transaction)| { - client.process_transaction_with_commitment(transaction, commitment) - }); - let statuses = futures::future::join_all(futures).await; - statuses.into_iter().collect() // Convert Vec> to Result> -} - async fn create_program_account_with_commitment( client: &mut BanksClient, loader_id: &Pubkey, diff --git a/themis/program_bn/Cargo.toml b/themis/program_bn/Cargo.toml deleted file mode 100644 index 62826357..00000000 --- a/themis/program_bn/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "spl-themis-bn" -version = "0.1.0" -description = "Solana Program Library THEMIS" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2018" - -[features] -exclude_entrypoint = [] - -[dependencies] -bincode = "1.3" -borsh = "0.7.1" -bn = {git = "https://github.com/garious/bn", rev = "5c35c737ffabac9921310f53f48725216d59cbf1", default-features = false, features = ["borsh"]} -elgamal_bn = { git = "https://github.com/garious/elgamal_bn", rev = "ba9bdcdb6cdd6fb8e74d0b8bc1b918bcd1b543a9" } -getrandom = { version = "0.1.15", features = ["dummy"] } -num-derive = "0.3" -num-traits = "0.2" -rand = "0.7.0" -solana-program = "1.4.3" -thiserror = "1.0" - -[lib] -crate-type = ["cdylib", "lib"] diff --git a/themis/program_bn/Xargo.toml b/themis/program_bn/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/themis/program_bn/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/themis/program_bn/program-id.md b/themis/program_bn/program-id.md deleted file mode 100644 index 14aea5c2..00000000 --- a/themis/program_bn/program-id.md +++ /dev/null @@ -1 +0,0 @@ -F3FWeYPjD1jeR6UykMj1GRbCcmoxtJnDiPuFdTLRGvb6 diff --git a/themis/program_bn/src/entrypoint.rs b/themis/program_bn/src/entrypoint.rs deleted file mode 100644 index 4d589f36..00000000 --- a/themis/program_bn/src/entrypoint.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Program entrypoint - -use solana_program::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, -}; - -entrypoint!(process_instruction); -fn process_instruction<'a>( - program_id: &Pubkey, - accounts: &'a [AccountInfo<'a>], - instruction_data: &[u8], -) -> ProgramResult { - crate::processor::process_instruction(program_id, accounts, instruction_data)?; - Ok(()) -} diff --git a/themis/program_bn/src/error.rs b/themis/program_bn/src/error.rs deleted file mode 100644 index 3cf3d778..00000000 --- a/themis/program_bn/src/error.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Error types - -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; -use solana_program::program_error::PrintProgramError; -use solana_program::{decode_error::DecodeError, program_error::ProgramError}; -use thiserror::Error; - -/// Errors that may be returned by the Themis program. -#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] -pub enum ThemisError { - /// Invalid instruction - #[error("Invalid instruction")] - InvalidInstruction, - - /// Account already in use - #[error("Account in use")] - AccountInUse, -} -impl From for ProgramError { - fn from(e: ThemisError) -> Self { - ProgramError::Custom(e as u32) - } -} -impl DecodeError for ThemisError { - fn type_of() -> &'static str { - "ThemisError" - } -} - -impl PrintProgramError for ThemisError { - fn print(&self) - where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, - { - match self { - ThemisError::InvalidInstruction => println!("Error: Invalid instruction"), - ThemisError::AccountInUse => println!("Error: Account in use"), - } - } -} diff --git a/themis/program_bn/src/instruction.rs b/themis/program_bn/src/instruction.rs deleted file mode 100644 index a998ea6b..00000000 --- a/themis/program_bn/src/instruction.rs +++ /dev/null @@ -1,235 +0,0 @@ -//! Instruction types - -use crate::state::{Policies, User}; -use bn::{Fr, G1}; -use borsh::{BorshDeserialize, BorshSerialize}; -use elgamal_bn::public::PublicKey; -use solana_program::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - pubkey::Pubkey, - system_instruction, -}; - -/// Instructions supported by the Themis program. -#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)] -pub enum ThemisInstruction { - /// Initialize a new user account - /// - /// The `InitializeUserAccount` 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. - InitializeUserAccount { - /// Public key for all encrypted interations - public_key: PublicKey, - }, - - /// Initialize a new policies account - /// - /// The `InitializePoliciesAccount` 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. - InitializePoliciesAccount { - /// Number of policies to be added - num_scalars: u8, - }, - - /// Store policies - /// - /// The `StorePolices` instruction is used to set individual policies. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable, signer]` The policies account. - StorePolicies { - /// Policies to be added - scalars: Vec<(u8, Fr)>, - }, - - /// Calculate aggregate. The length of the `input` vector must equal the - /// number of policies. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable, signer]` The user account - /// 1. `[]` The policies account - SubmitInteractions { - /// Encrypted interactions - encrypted_interactions: Vec<(u8, (G1, G1))>, - }, - - /// Submit proof decryption - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable, signer]` The user account - SubmitProofDecryption { - /// plaintext - plaintext: G1, - - /// (announcement_g, announcement_ctx) - announcement: Box<(G1, G1)>, - - /// response - response: Fr, - }, - - /// Request a payment - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable, signer]` The user account - RequestPayment { - /// Encrypted aggregate - encrypted_aggregate: Box<(G1, G1)>, - - /// Decrypted aggregate - decrypted_aggregate: G1, - - /// Proof correct decryption - proof_correct_decryption: G1, - }, -} - -impl ThemisInstruction { - pub fn serialize(&self) -> Result, ProgramError> { - self.try_to_vec() - .map_err(|_| ProgramError::AccountDataTooSmall) - } - - pub(crate) fn deserialize(data: &[u8]) -> Result { - Self::try_from_slice(&data).map_err(|_| ProgramError::InvalidInstructionData) - } -} - -/// Return an `InitializeUserAccount` instruction. -fn initialize_user_account(user_pubkey: &Pubkey, public_key: PublicKey) -> Instruction { - let data = ThemisInstruction::InitializeUserAccount { public_key }; - - let accounts = vec![AccountMeta::new(*user_pubkey, false)]; - - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} - -/// Return two instructions that create and initialize a user account. -pub fn create_user_account( - from: &Pubkey, - user_pubkey: &Pubkey, - lamports: u64, - public_key: PublicKey, -) -> Vec { - let space = User::default().try_to_vec().unwrap().len() as u64; - vec![ - system_instruction::create_account(from, user_pubkey, lamports, space, &crate::id()), - initialize_user_account(user_pubkey, public_key), - ] -} - -/// Return an `InitializePoliciesAccount` instruction. -fn initialize_policies_account(policies_pubkey: &Pubkey, num_scalars: u8) -> Instruction { - let data = ThemisInstruction::InitializePoliciesAccount { num_scalars }; - let accounts = vec![AccountMeta::new(*policies_pubkey, false)]; - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} - -/// Return two instructions that create and initialize a policies account. -pub fn create_policies_account( - from: &Pubkey, - policies_pubkey: &Pubkey, - lamports: u64, - num_scalars: u8, -) -> Vec { - let space = Policies::new(num_scalars).try_to_vec().unwrap().len() as u64; - vec![ - system_instruction::create_account(from, policies_pubkey, lamports, space, &crate::id()), - initialize_policies_account(policies_pubkey, num_scalars), - ] -} - -/// Return an `InitializePoliciesAccount` instruction. -pub fn store_policies(policies_pubkey: &Pubkey, scalars: Vec<(u8, Fr)>) -> Instruction { - let data = ThemisInstruction::StorePolicies { scalars }; - let accounts = vec![AccountMeta::new(*policies_pubkey, true)]; - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} - -/// Return a `SubmitInteractions` instruction. -pub fn submit_interactions( - user_pubkey: &Pubkey, - policies_pubkey: &Pubkey, - encrypted_interactions: Vec<(u8, (G1, G1))>, -) -> Instruction { - let data = ThemisInstruction::SubmitInteractions { - encrypted_interactions, - }; - let accounts = vec![ - AccountMeta::new(*user_pubkey, true), - AccountMeta::new_readonly(*policies_pubkey, false), - ]; - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} - -/// Return a `SubmitProofDecryption` instruction. -pub fn submit_proof_decryption( - user_pubkey: &Pubkey, - plaintext: G1, - announcement_g: G1, - announcement_ctx: G1, - response: Fr, -) -> Instruction { - let data = ThemisInstruction::SubmitProofDecryption { - plaintext, - announcement: Box::new((announcement_g, announcement_ctx)), - response, - }; - let accounts = vec![AccountMeta::new(*user_pubkey, true)]; - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} - -/// Return a `RequestPayment` instruction. -pub fn request_payment( - user_pubkey: &Pubkey, - encrypted_aggregate: (G1, G1), - decrypted_aggregate: G1, - proof_correct_decryption: G1, -) -> Instruction { - let data = ThemisInstruction::RequestPayment { - encrypted_aggregate: Box::new(encrypted_aggregate), - decrypted_aggregate, - proof_correct_decryption, - }; - let accounts = vec![AccountMeta::new(*user_pubkey, true)]; - Instruction { - program_id: crate::id(), - accounts, - data: data.serialize().unwrap(), - } -} diff --git a/themis/program_bn/src/lib.rs b/themis/program_bn/src/lib.rs deleted file mode 100644 index bb631894..00000000 --- a/themis/program_bn/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! An implementation of Brave's THEMIS for the Solana blockchain -#![forbid(unsafe_code)] - -pub mod error; -pub mod instruction; -pub mod processor; -pub mod state; - -#[cfg(not(feature = "exclude_entrypoint"))] -pub mod entrypoint; - -// Export current sdk types for downstream users building with a different sdk version -pub use solana_program; - -solana_program::declare_id!("F3FWeYPjD1jeR6UykMj1GRbCcmoxtJnDiPuFdTLRGvb6"); diff --git a/themis/program_bn/src/processor.rs b/themis/program_bn/src/processor.rs deleted file mode 100644 index 6f2e2358..00000000 --- a/themis/program_bn/src/processor.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Themis program - -use crate::{ - error::ThemisError, - instruction::ThemisInstruction, - state::{Policies, User}, -}; -use bn::{Fr, G1}; -use elgamal_bn::public::PublicKey; -use solana_program::{ - account_info::{next_account_info, AccountInfo}, - program_error::ProgramError, - pubkey::Pubkey, -}; - -fn process_initialize_user_account( - user_info: &AccountInfo, - public_key: PublicKey, -) -> Result<(), ProgramError> { - // TODO: verify the program ID - if let Ok(user) = User::deserialize(&user_info.data.borrow()) { - if user.is_initialized { - return Err(ThemisError::AccountInUse.into()); - } - } - let user = User::new(public_key); - user.serialize(&mut user_info.data.borrow_mut()) -} - -fn process_initialize_policies_account( - num_scalars: u8, - policies_info: &AccountInfo, -) -> Result<(), ProgramError> { - if let Ok(policies) = Policies::deserialize(&policies_info.data.borrow()) { - if policies.is_initialized { - return Err(ThemisError::AccountInUse.into()); - } - } - let policies = Policies::new(num_scalars); - policies.serialize(&mut policies_info.data.borrow_mut()) -} - -fn process_store_policies( - scalars: Vec<(u8, Fr)>, - policies_info: &AccountInfo, -) -> Result<(), ProgramError> { - let mut policies = Policies::deserialize(&policies_info.data.borrow())?; - for (i, scalar) in scalars { - policies.scalars[i as usize] = scalar; - } - policies.serialize(&mut policies_info.data.borrow_mut()) -} - -fn process_submit_interactions( - encrypted_interactions: &[(u8, (G1, G1))], - user_info: &AccountInfo, - policies_info: &AccountInfo, -) -> Result<(), ProgramError> { - let mut user = User::deserialize(&user_info.data.borrow())?; - let policies = Policies::deserialize(&policies_info.data.borrow())?; - user.submit_interactions(encrypted_interactions, &policies.scalars); - user.serialize(&mut user_info.data.borrow_mut()) -} - -fn process_submit_proof_decryption( - plaintext: G1, - announcement: (G1, G1), - response: Fr, - user_info: &AccountInfo, -) -> Result<(), ProgramError> { - let mut user = User::deserialize(&user_info.data.borrow())?; - user.submit_proof_decryption(plaintext, announcement.0, announcement.1, response); - user.serialize(&mut user_info.data.borrow_mut()) -} - -fn process_request_payment( - encrypted_aggregate: (G1, G1), - decrypted_aggregate: G1, - proof_correct_decryption: G1, - user_info: &AccountInfo, -) -> Result<(), ProgramError> { - let mut user = User::deserialize(&user_info.data.borrow())?; - user.request_payment( - encrypted_aggregate, - decrypted_aggregate, - proof_correct_decryption, - ); - user.serialize(&mut user_info.data.borrow_mut()) -} - -/// Process the given transaction instruction -pub fn process_instruction<'a>( - _program_id: &Pubkey, - account_infos: &'a [AccountInfo<'a>], - input: &[u8], -) -> Result<(), ProgramError> { - let account_infos_iter = &mut account_infos.iter(); - let instruction = ThemisInstruction::deserialize(input)?; - - match instruction { - ThemisInstruction::InitializeUserAccount { public_key } => { - let user_info = next_account_info(account_infos_iter)?; - process_initialize_user_account(&user_info, public_key) - } - ThemisInstruction::InitializePoliciesAccount { num_scalars } => { - let policies_info = next_account_info(account_infos_iter)?; - process_initialize_policies_account(num_scalars, &policies_info) - } - ThemisInstruction::StorePolicies { scalars } => { - let policies_info = next_account_info(account_infos_iter)?; - process_store_policies(scalars, &policies_info) - } - ThemisInstruction::SubmitInteractions { - encrypted_interactions, - } => { - let user_info = next_account_info(account_infos_iter)?; - let policies_info = next_account_info(account_infos_iter)?; - process_submit_interactions(&encrypted_interactions, &user_info, &policies_info) - } - ThemisInstruction::SubmitProofDecryption { - plaintext, - announcement, - response, - } => { - let user_info = next_account_info(account_infos_iter)?; - process_submit_proof_decryption(plaintext, *announcement, response, &user_info) - } - ThemisInstruction::RequestPayment { - encrypted_aggregate, - decrypted_aggregate, - proof_correct_decryption, - } => { - let user_info = next_account_info(account_infos_iter)?; - process_request_payment( - *encrypted_aggregate, - decrypted_aggregate, - proof_correct_decryption, - &user_info, - ) - } - } -} diff --git a/themis/program_bn/src/state.rs b/themis/program_bn/src/state.rs deleted file mode 100644 index 593b7c43..00000000 --- a/themis/program_bn/src/state.rs +++ /dev/null @@ -1,344 +0,0 @@ -#![allow(missing_docs)] - -use bn::{Fr, Group, G1}; -use borsh::{BorshDeserialize, BorshSerialize}; -use elgamal_bn::{ciphertext::Ciphertext, private::SecretKey, public::PublicKey}; -use rand::thread_rng; -use solana_program::program_error::ProgramError; - -type Points = (G1, G1); - -#[derive(Default, BorshSerialize, BorshDeserialize)] -pub struct Policies { - pub is_initialized: bool, - pub num_scalars: u8, - pub scalars: Vec, -} - -impl Policies { - pub fn serialize(&self, mut data: &mut [u8]) -> Result<(), ProgramError> { - BorshSerialize::serialize(self, &mut data).map_err(|_| ProgramError::AccountDataTooSmall) - } - - pub fn deserialize(data: &[u8]) -> Result { - Self::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData) - } - - pub fn new(num_scalars: u8) -> Self { - Self { - is_initialized: true, - num_scalars, - scalars: vec![Fr::zero(); num_scalars as usize], - } - } - - /// Useful for testing - pub fn new_with_scalars(scalars: Vec) -> Self { - let mut policies = Self::new(scalars.len() as u8); - policies.scalars = scalars; - policies - } -} - -#[derive(BorshSerialize, BorshDeserialize)] -pub struct PaymentRequest { - pub encrypted_aggregate: Points, - pub decrypted_aggregate: G1, - pub proof_correct_decryption: G1, - pub valid: bool, -} - -impl PaymentRequest { - fn new( - encrypted_aggregate: Points, - decrypted_aggregate: G1, - proof_correct_decryption: G1, - valid: bool, - ) -> Self { - Self { - encrypted_aggregate, - decrypted_aggregate, - proof_correct_decryption, - valid, - } - } -} - -fn inner_product( - (mut aggregate_x, mut aggregate_y): Points, - ciphertexts: &[(u8, Points)], - scalars: &[Fr], -) -> Points { - for &(i, (x, y)) in ciphertexts { - aggregate_x = x * scalars[i as usize] + aggregate_x; - aggregate_y = y * scalars[i as usize] + aggregate_y; - } - - (aggregate_x, aggregate_y) -} - -#[derive(BorshSerialize, BorshDeserialize)] -pub struct User { - encrypted_aggregate: Points, - public_key: PublicKey, - pub is_initialized: bool, - proof_verification: bool, - payment_requests: Vec, -} - -impl Default for User { - fn default() -> Self { - Self { - encrypted_aggregate: (G1::zero(), G1::zero()), - public_key: PublicKey::from(G1::zero()), - is_initialized: false, - proof_verification: false, - payment_requests: vec![], - } - } -} - -impl User { - pub fn serialize(&self, mut data: &mut [u8]) -> Result<(), ProgramError> { - BorshSerialize::serialize(self, &mut data).map_err(|_| ProgramError::AccountDataTooSmall) - } - - pub fn deserialize(data: &[u8]) -> Result { - Self::try_from_slice(&data).map_err(|_| ProgramError::InvalidAccountData) - } - - pub fn new(public_key: PublicKey) -> Self { - Self { - public_key, - ..Self::default() - } - } - - pub fn fetch_encrypted_aggregate(&self) -> Points { - self.encrypted_aggregate - } - - pub fn fetch_public_key(&self) -> PublicKey { - self.public_key - } - - pub fn fetch_proof_verification(&self) -> bool { - self.proof_verification - } - - pub fn submit_interactions(&mut self, interactions: &[(u8, Points)], policies: &[Fr]) -> bool { - self.encrypted_aggregate = inner_product(self.encrypted_aggregate, interactions, &policies); - true - } - - pub fn submit_proof_decryption( - &mut self, - plaintext: G1, - announcement_g: G1, - announcement_ctx: G1, - response: Fr, - ) -> bool { - let client_pk = self.fetch_public_key(); - let ciphertext = Ciphertext { - points: self.fetch_encrypted_aggregate(), - pk: client_pk, - }; - self.proof_verification = client_pk - .verify_correct_decryption_no_Merlin( - ((announcement_g, announcement_ctx), response), - ciphertext, - plaintext, - ) - .is_ok(); - true - } - - pub fn request_payment( - &mut self, - encrypted_aggregate: Points, - decrypted_aggregate: G1, - proof_correct_decryption: G1, - ) -> bool { - // TODO: implement proof verification - let proof_is_valid = true; - let payment_request = PaymentRequest::new( - encrypted_aggregate, - decrypted_aggregate, - proof_correct_decryption, - proof_is_valid, - ); - self.payment_requests.push(payment_request); - proof_is_valid - } -} - -pub fn generate_keys() -> (SecretKey, PublicKey) { - let mut csprng = thread_rng(); - let sk = SecretKey::new(&mut csprng); - let pk = PublicKey::from(&sk); - (sk, pk) -} - -pub fn recover_scalar(point: G1, k: u32) -> Fr { - for i in 0..2u64.pow(k) { - let scalar = Fr::new(i.into()).unwrap(); - if G1::one() * scalar == point { - return scalar; - } - } - panic!("Encrypted scalar too long"); -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - - fn test_policy_contract(policies: &[Fr], expected_scalar_aggregate: Fr) { - let (sk, pk) = generate_keys(); - let interactions: Vec<_> = (0..policies.len()) - .map(|i| (i as u8, pk.encrypt(&G1::one()).points)) - .collect(); - let mut user = User::new(pk); - - let tx_receipt = user.submit_interactions(&interactions, policies); - assert!(tx_receipt); - - let encrypted_point = user.fetch_encrypted_aggregate(); - let ciphertext = Ciphertext { - points: encrypted_point, - pk, - }; - - let decrypted_aggregate = sk.decrypt(&ciphertext); - let scalar_aggregate = recover_scalar(decrypted_aggregate, 16); - assert_eq!(scalar_aggregate, expected_scalar_aggregate); - - let ((announcement_g, announcement_ctx), response) = sk - .prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate) - .unwrap(); - - let tx_receipt_proof = user.submit_proof_decryption( - decrypted_aggregate, - announcement_g, - announcement_ctx, - response, - ); - assert!(tx_receipt_proof); - - let proof_result = user.fetch_proof_verification(); - assert!(proof_result); - } - - #[test] - fn test_policy_contract_2ads() { - let policies = vec![Fr::new(1u64.into()).unwrap(), Fr::new(2u64.into()).unwrap()]; - test_policy_contract(&policies, Fr::new(3u64.into()).unwrap()); - } - - #[test] - fn test_policy_contract_128ads() { - let policies = vec![ - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), //10 - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), // 2 * 10 - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), - Fr::new(1u64.into()).unwrap(), //10 - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), - Fr::new(2u64.into()).unwrap(), // 2 * 10 - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - Fr::new(0u64.into()).unwrap(), - ]; - test_policy_contract(&policies, Fr::new(60u64.into()).unwrap()); - } -}