Cleanup themis (#742)
* Cleanup themis * Speed up seeding fee-payers * Add utility functions * Remove BN variant of themis
This commit is contained in:
parent
cb9a94142a
commit
6906200174
|
@ -196,19 +196,6 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
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]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -477,12 +464,6 @@ version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpuid-bool"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
|
@ -623,19 +604,6 @@ dependencies = [
|
||||||
"subtle 2.2.3",
|
"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]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -650,6 +618,19 @@ dependencies = [
|
||||||
"zeroize",
|
"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]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.1.1"
|
version = "2.1.1"
|
||||||
|
@ -746,7 +727,7 @@ dependencies = [
|
||||||
"ed25519",
|
"ed25519",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -756,20 +737,6 @@ version = "1.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
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]]
|
[[package]]
|
||||||
name = "elgamal_ristretto"
|
name = "elgamal_ristretto"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
@ -781,7 +748,7 @@ dependencies = [
|
||||||
"curve25519-dalek 2.1.0 (git+https://github.com/garious/curve25519-dalek?rev=60efef3553d6bf3d7f3b09b5f97acd54d72529ff)",
|
"curve25519-dalek 2.1.0 (git+https://github.com/garious/curve25519-dalek?rev=60efef3553d6bf3d7f3b09b5f97acd54d72529ff)",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"zkp",
|
"zkp",
|
||||||
]
|
]
|
||||||
|
@ -1393,7 +1360,7 @@ dependencies = [
|
||||||
"digest 0.8.1",
|
"digest 0.8.1",
|
||||||
"hmac-drbg",
|
"hmac-drbg",
|
||||||
"rand",
|
"rand",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"subtle 2.2.3",
|
"subtle 2.2.3",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
@ -2105,12 +2072,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-hex"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -2329,19 +2290,6 @@ dependencies = [
|
||||||
"opaque-debug 0.2.3",
|
"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]]
|
[[package]]
|
||||||
name = "sha3"
|
name = "sha3"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
@ -2552,7 +2500,7 @@ dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"solana-frozen-abi-macro",
|
"solana-frozen-abi-macro",
|
||||||
"solana-logger",
|
"solana-logger",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -2654,7 +2602,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"solana-frozen-abi",
|
"solana-frozen-abi",
|
||||||
"solana-frozen-abi-macro",
|
"solana-frozen-abi-macro",
|
||||||
"solana-logger",
|
"solana-logger",
|
||||||
|
@ -2773,7 +2721,7 @@ dependencies = [
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"sha3",
|
"sha3",
|
||||||
"solana-crate-features",
|
"solana-crate-features",
|
||||||
"solana-frozen-abi",
|
"solana-frozen-abi",
|
||||||
|
@ -2941,22 +2889,6 @@ dependencies = [
|
||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "spl-themis-ristretto"
|
name = "spl-themis-ristretto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -3238,7 +3170,7 @@ dependencies = [
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"rand",
|
"rand",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"sha2 0.8.2",
|
"sha2",
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,10 @@ members = [
|
||||||
"token/cli",
|
"token/cli",
|
||||||
"token/program",
|
"token/program",
|
||||||
"token/program-v3",
|
"token/program-v3",
|
||||||
"themis/program_bn",
|
|
||||||
"themis/program_ristretto",
|
"themis/program_ristretto",
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"shared-memory/client",
|
"shared-memory/client",
|
||||||
"token/perf-monitor",
|
"token/perf-monitor",
|
||||||
"themis/client_bn",
|
|
||||||
"themis/client_ristretto",
|
"themis/client_ristretto",
|
||||||
]
|
]
|
||||||
|
|
|
@ -56,7 +56,6 @@ done
|
||||||
# Run client tests
|
# Run client tests
|
||||||
_ cargo test --manifest-path=shared-memory/client/Cargo.toml -- --nocapture
|
_ cargo test --manifest-path=shared-memory/client/Cargo.toml -- --nocapture
|
||||||
_ cargo test --manifest-path=token/perf-monitor/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
|
_ cargo test --manifest-path=themis/client_ristretto/Cargo.toml -- --nocapture
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ fi
|
||||||
workspace_crates=(
|
workspace_crates=(
|
||||||
Cargo.toml
|
Cargo.toml
|
||||||
shared-memory/client/Cargo.toml
|
shared-memory/client/Cargo.toml
|
||||||
themis/client_bn/Cargo.toml
|
|
||||||
themis/client_ristretto/Cargo.toml
|
themis/client_ristretto/Cargo.toml
|
||||||
token/perf-monitor/Cargo.toml
|
token/perf-monitor/Cargo.toml
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "spl-themis-bn-client"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "SPL THEMIS client"
|
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|
||||||
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"]
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -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<u64> {
|
|
||||||
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<Fr>,
|
|
||||||
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::<u64>();
|
|
||||||
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<Pubkey, Account> = 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<AccountInfo> = 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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<u8> {
|
|
||||||
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<u64, InstructionError> {
|
|
||||||
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::<solana_bpf_loader_program::BPFError>::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<RefCell<SolanaAccount>>],
|
|
||||||
) -> Result<(), InstructionError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn get_caller(&self) -> Result<&Pubkey, InstructionError> {
|
|
||||||
Ok(&self.key)
|
|
||||||
}
|
|
||||||
fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)] {
|
|
||||||
&[]
|
|
||||||
}
|
|
||||||
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
|
|
||||||
Rc::new(RefCell::new(self.logger.clone()))
|
|
||||||
}
|
|
||||||
fn get_compute_budget(&self) -> &ComputeBudget {
|
|
||||||
&self.compute_budget
|
|
||||||
}
|
|
||||||
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
|
|
||||||
Rc::new(RefCell::new(self.compute_meter.clone()))
|
|
||||||
}
|
|
||||||
fn add_executor(&mut self, _pubkey: &Pubkey, _executor: Arc<dyn Executor>) {}
|
|
||||||
fn get_executor(&mut self, _pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,6 +13,7 @@ use solana_sdk::{
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
system_instruction,
|
system_instruction,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
|
transport,
|
||||||
};
|
};
|
||||||
use spl_themis_ristretto::{
|
use spl_themis_ristretto::{
|
||||||
instruction,
|
instruction,
|
||||||
|
@ -21,6 +22,32 @@ use spl_themis_ristretto::{
|
||||||
use std::{io, time::Instant};
|
use std::{io, time::Instant};
|
||||||
//use tarpc::context;
|
//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<Transaction>,
|
||||||
|
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<Result<_, _>> to Result<Vec<_>>
|
||||||
|
}
|
||||||
|
|
||||||
/// For a single user, create interactions, calculate the aggregate, submit a proof, and verify it.
|
/// For a single user, create interactions, calculate the aggregate, submit a proof, and verify it.
|
||||||
async fn run_user_workflow(
|
async fn run_user_workflow(
|
||||||
mut client: BanksClient,
|
mut client: BanksClient,
|
||||||
|
@ -44,15 +71,10 @@ async fn run_user_workflow(
|
||||||
sol_to_lamports(0.001),
|
sol_to_lamports(0.001),
|
||||||
pk,
|
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 recent_blockhash = client.get_recent_blockhash().await?;
|
||||||
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
||||||
let tx_size = bincode::serialize(&tx).unwrap().len();
|
assert_transaction_size(&tx);
|
||||||
assert!(
|
|
||||||
tx_size <= 1200,
|
|
||||||
"transaction over 1200 bytes: {} bytes",
|
|
||||||
tx_size
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
||||||
.await
|
.await
|
||||||
|
@ -68,15 +90,10 @@ async fn run_user_workflow(
|
||||||
&policies_pubkey,
|
&policies_pubkey,
|
||||||
interactions,
|
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 recent_blockhash = client.get_recent_blockhash().await?;
|
||||||
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
||||||
let tx_size = bincode::serialize(&tx).unwrap().len();
|
assert_transaction_size(&tx);
|
||||||
assert!(
|
|
||||||
tx_size <= 1200,
|
|
||||||
"transaction over 1200 bytes: {} bytes",
|
|
||||||
tx_size
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
||||||
.await
|
.await
|
||||||
|
@ -110,7 +127,6 @@ async fn run_user_workflow(
|
||||||
(RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_POINT),
|
(RISTRETTO_BASEPOINT_POINT, RISTRETTO_BASEPOINT_POINT),
|
||||||
0u64.into(),
|
0u64.into(),
|
||||||
);
|
);
|
||||||
//sk.prove_correct_decryption_no_Merlin(&ciphertext, &decrypted_aggregate).unwrap();
|
|
||||||
|
|
||||||
let ix = instruction::submit_proof_decryption(
|
let ix = instruction::submit_proof_decryption(
|
||||||
program_id,
|
program_id,
|
||||||
|
@ -120,15 +136,10 @@ async fn run_user_workflow(
|
||||||
announcement_ctx,
|
announcement_ctx,
|
||||||
response,
|
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 recent_blockhash = client.get_recent_blockhash().await?;
|
||||||
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
let tx = Transaction::new(&[&sender_keypair, &user_keypair], msg, recent_blockhash);
|
||||||
let tx_size = bincode::serialize(&tx).unwrap().len();
|
assert_transaction_size(&tx);
|
||||||
assert!(
|
|
||||||
tx_size <= 1200,
|
|
||||||
"transaction over 1200 bytes: {} bytes",
|
|
||||||
tx_size
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
||||||
.await
|
.await
|
||||||
|
@ -174,43 +185,33 @@ pub async fn test_e2e(
|
||||||
policies_slice,
|
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 recent_blockhash = client.get_recent_blockhash().await?;
|
||||||
let tx = Transaction::new(&[&sender_keypair, &policies_keypair], msg, recent_blockhash);
|
let tx = Transaction::new(&[&sender_keypair, &policies_keypair], msg, recent_blockhash);
|
||||||
let tx_size = bincode::serialize(&tx).unwrap().len();
|
assert_transaction_size(&tx);
|
||||||
assert!(
|
|
||||||
tx_size <= 1200,
|
|
||||||
"transaction over 1200 bytes: {} bytes",
|
|
||||||
tx_size
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Send feepayer_keypairs some SOL
|
// Send feepayer_keypairs some SOL
|
||||||
|
println!("Seeding feepayer accounts...");
|
||||||
let feepayers: Vec<_> = (0..num_users).map(|_| Keypair::new()).collect();
|
let feepayers: Vec<_> = (0..num_users).map(|_| Keypair::new()).collect();
|
||||||
for feepayers in feepayers.chunks(20) {
|
let recent_blockhash = client.get_recent_blockhash().await.unwrap();
|
||||||
println!("Seeding feepayer accounts...");
|
let txs: Vec<_> = feepayers.chunks(20).map(|feepayers| {
|
||||||
let payments: Vec<_> = feepayers
|
let payments: Vec<_> = feepayers
|
||||||
.iter()
|
.iter()
|
||||||
.map(|keypair| (keypair.pubkey(), sol_to_lamports(0.0011)))
|
.map(|keypair| (keypair.pubkey(), sol_to_lamports(0.0011)))
|
||||||
.collect();
|
.collect();
|
||||||
let ixs = system_instruction::transfer_many(&sender_pubkey, &payments);
|
let ixs = system_instruction::transfer_many(&sender_pubkey, &payments);
|
||||||
let msg = Message::new(&ixs, Some(&sender_keypair.pubkey()));
|
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 = Transaction::new(&[&sender_keypair], msg, recent_blockhash);
|
||||||
let tx_size = bincode::serialize(&tx).unwrap().len();
|
assert_transaction_size(&tx);
|
||||||
assert!(
|
tx
|
||||||
tx_size <= 1200,
|
}).collect();
|
||||||
"transaction over 1200 bytes: {} bytes",
|
process_transactions_with_commitment(client, txs, CommitmentLevel::Recent)
|
||||||
tx_size
|
.await
|
||||||
);
|
.unwrap();
|
||||||
client
|
|
||||||
.process_transaction_with_commitment(tx, CommitmentLevel::Recent)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("Starting benchmark...");
|
println!("Starting benchmark...");
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
|
@ -12,7 +12,7 @@ use solana_sdk::{
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
transport,
|
transport,
|
||||||
};
|
};
|
||||||
use spl_themis_ristretto_client::test_e2e;
|
use spl_themis_ristretto_client::{test_e2e, process_transactions_with_commitment};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{remove_dir_all, File},
|
fs::{remove_dir_all, File},
|
||||||
io::Read,
|
io::Read,
|
||||||
|
@ -34,23 +34,6 @@ fn load_program(name: &str) -> Vec<u8> {
|
||||||
program
|
program
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add this to BanksClient
|
|
||||||
async fn process_transactions_with_commitment(
|
|
||||||
client: &mut BanksClient,
|
|
||||||
transactions: Vec<Transaction>,
|
|
||||||
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<Result<_, _>> to Result<Vec<_>>
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create_program_account_with_commitment(
|
async fn create_program_account_with_commitment(
|
||||||
client: &mut BanksClient,
|
client: &mut BanksClient,
|
||||||
loader_id: &Pubkey,
|
loader_id: &Pubkey,
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "spl-themis-bn"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Solana Program Library THEMIS"
|
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
|
||||||
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"]
|
|
|
@ -1,2 +0,0 @@
|
||||||
[target.bpfel-unknown-unknown.dependencies.std]
|
|
||||||
features = []
|
|
|
@ -1 +0,0 @@
|
||||||
F3FWeYPjD1jeR6UykMj1GRbCcmoxtJnDiPuFdTLRGvb6
|
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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<ThemisError> for ProgramError {
|
|
||||||
fn from(e: ThemisError) -> Self {
|
|
||||||
ProgramError::Custom(e as u32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DecodeError<T> for ThemisError {
|
|
||||||
fn type_of() -> &'static str {
|
|
||||||
"ThemisError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrintProgramError for ThemisError {
|
|
||||||
fn print<E>(&self)
|
|
||||||
where
|
|
||||||
E: 'static + std::error::Error + DecodeError<E> + PrintProgramError + FromPrimitive,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
ThemisError::InvalidInstruction => println!("Error: Invalid instruction"),
|
|
||||||
ThemisError::AccountInUse => println!("Error: Account in use"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Vec<u8>, ProgramError> {
|
|
||||||
self.try_to_vec()
|
|
||||||
.map_err(|_| ProgramError::AccountDataTooSmall)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn deserialize(data: &[u8]) -> Result<Self, ProgramError> {
|
|
||||||
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<Instruction> {
|
|
||||||
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<Instruction> {
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
|
|
@ -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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<Fr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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, ProgramError> {
|
|
||||||
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<Fr>) -> 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<PaymentRequest>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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, ProgramError> {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue