diff --git a/Cargo.lock b/Cargo.lock index ce16b7a..42d86f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,6 +145,7 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ + "actix-macros", "futures-core", "tokio", ] @@ -2670,23 +2671,28 @@ name = "node_core" version = "0.1.0" dependencies = [ "accounts", + "actix-rt", "anyhow", "bincode", "elliptic-curve", "env_logger", + "hex", "k256", "log", "monotree", "rand 0.8.5", "reqwest 0.11.27", + "risc0-zkvm", "secp256k1-zkp", "serde", "serde_json", "sha2 0.10.8", "storage", + "tempfile", "thiserror", "tokio", "utxo", + "zkvm", ] [[package]] @@ -2701,6 +2707,7 @@ dependencies = [ "consensus", "env_logger", "futures", + "hex", "log", "networking", "node_core", @@ -2708,6 +2715,7 @@ dependencies = [ "serde", "serde_json", "storage", + "tokio", "utxo", "vm", "zkvm", @@ -3540,21 +3548,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "risc0-binfmt" -version = "1.1.3" -source = "git+https://github.com/risc0/risc0.git?branch=release-1.1#20df1afbcdd2ca442ece8c3fecd25a20cc0aafb5" -dependencies = [ - "anyhow", - "borsh", - "elf", - "risc0-zkp 1.1.3", - "risc0-zkvm-platform 1.1.3", - "serde", - "syn 2.0.87", - "tracing", -] - [[package]] name = "risc0-binfmt" version = "1.2.0" @@ -3563,30 +3556,12 @@ dependencies = [ "anyhow", "borsh", "elf", - "risc0-zkp 1.2.0", - "risc0-zkvm-platform 1.2.0", + "risc0-zkp", + "risc0-zkvm-platform", "serde", "tracing", ] -[[package]] -name = "risc0-build" -version = "1.1.3" -source = "git+https://github.com/risc0/risc0.git?branch=release-1.1#20df1afbcdd2ca442ece8c3fecd25a20cc0aafb5" -dependencies = [ - "anyhow", - "cargo_metadata", - "dirs", - "docker-generate", - "hex", - "risc0-binfmt 1.1.3", - "risc0-zkp 1.1.3", - "risc0-zkvm-platform 1.1.3", - "serde", - "serde_json", - "tempfile", -] - [[package]] name = "risc0-build" version = "1.2.0" @@ -3597,9 +3572,9 @@ dependencies = [ "dirs", "docker-generate", "hex", - "risc0-binfmt 1.2.0", - "risc0-zkp 1.2.0", - "risc0-zkvm-platform 1.2.0", + "risc0-binfmt", + "risc0-zkp", + "risc0-zkvm-platform", "serde", "serde_json", "tempfile", @@ -3636,9 +3611,9 @@ dependencies = [ "rand 0.8.5", "rayon", "risc0-circuit-recursion-sys", - "risc0-core 1.2.0", + "risc0-core", "risc0-sys", - "risc0-zkp 1.2.0", + "risc0-zkp", "serde", "sha2 0.10.8", "tracing", @@ -3652,7 +3627,7 @@ source = "git+https://github.com/risc0/risc0.git?branch=release-1.2#baf81cdbab10 dependencies = [ "glob", "risc0-build-kernel", - "risc0-core 1.2.0", + "risc0-core", "risc0-sys", "sppark", ] @@ -3678,12 +3653,12 @@ dependencies = [ "num-traits", "rand 0.8.5", "rayon", - "risc0-binfmt 1.2.0", + "risc0-binfmt", "risc0-circuit-rv32im-sys", - "risc0-core 1.2.0", + "risc0-core", "risc0-sys", - "risc0-zkp 1.2.0", - "risc0-zkvm-platform 1.2.0", + "risc0-zkp", + "risc0-zkvm-platform", "serde", "sha2 0.10.8", "tracing", @@ -3696,20 +3671,11 @@ source = "git+https://github.com/risc0/risc0.git?branch=release-1.2#baf81cdbab10 dependencies = [ "glob", "risc0-build-kernel", - "risc0-core 1.2.0", + "risc0-core", "risc0-sys", "sppark", ] -[[package]] -name = "risc0-core" -version = "1.1.3" -source = "git+https://github.com/risc0/risc0.git?branch=release-1.1#20df1afbcdd2ca442ece8c3fecd25a20cc0aafb5" -dependencies = [ - "bytemuck", - "rand_core 0.6.4", -] - [[package]] name = "risc0-core" version = "1.2.0" @@ -3735,9 +3701,9 @@ dependencies = [ "hex", "num-bigint 0.4.6", "num-traits", - "risc0-binfmt 1.2.0", - "risc0-core 1.2.0", - "risc0-zkp 1.2.0", + "risc0-binfmt", + "risc0-core", + "risc0-zkp", "serde", "serde_json", "stability", @@ -3757,29 +3723,6 @@ dependencies = [ "sppark", ] -[[package]] -name = "risc0-zkp" -version = "1.1.3" -source = "git+https://github.com/risc0/risc0.git?branch=release-1.1#20df1afbcdd2ca442ece8c3fecd25a20cc0aafb5" -dependencies = [ - "anyhow", - "blake2", - "borsh", - "bytemuck", - "cfg-if 1.0.0", - "digest 0.10.7", - "hex", - "hex-literal", - "metal", - "paste 1.0.15", - "rand_core 0.6.4", - "risc0-core 1.1.3", - "risc0-zkvm-platform 1.1.3", - "serde", - "sha2 0.10.8", - "tracing", -] - [[package]] name = "risc0-zkp" version = "1.2.0" @@ -3802,9 +3745,9 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rayon", - "risc0-core 1.2.0", + "risc0-core", "risc0-sys", - "risc0-zkvm-platform 1.2.0", + "risc0-zkvm-platform", "serde", "sha2 0.10.8", "tracing", @@ -3831,14 +3774,14 @@ dependencies = [ "prost", "rand 0.8.5", "rayon", - "risc0-binfmt 1.2.0", - "risc0-build 1.2.0", + "risc0-binfmt", + "risc0-build", "risc0-circuit-recursion", "risc0-circuit-rv32im", - "risc0-core 1.2.0", + "risc0-core", "risc0-groth16", - "risc0-zkp 1.2.0", - "risc0-zkvm-platform 1.2.0", + "risc0-zkp", + "risc0-zkvm-platform", "rrs-lib", "rustc-demangle", "semver", @@ -3851,14 +3794,6 @@ dependencies = [ "typetag", ] -[[package]] -name = "risc0-zkvm-platform" -version = "1.1.3" -source = "git+https://github.com/risc0/risc0.git?branch=release-1.1#20df1afbcdd2ca442ece8c3fecd25a20cc0aafb5" -dependencies = [ - "stability", -] - [[package]] name = "risc0-zkvm-platform" version = "1.2.0" @@ -4410,6 +4345,7 @@ name = "storage" version = "0.1.0" dependencies = [ "anyhow", + "elliptic-curve", "env_logger", "log", "lru", @@ -4541,7 +4477,7 @@ dependencies = [ name = "test-methods" version = "0.1.0" dependencies = [ - "risc0-build 1.1.3", + "risc0-build", ] [[package]] @@ -5465,13 +5401,16 @@ dependencies = [ name = "zkvm" version = "0.1.0" dependencies = [ + "accounts", "anyhow", "env_logger", "log", "risc0-zkvm", "serde", "serde_json", + "storage", "test-methods", + "utxo", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6b93096..e04fca5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ serde_json = "1.0.81" actix = "0.13.0" actix-cors = "0.6.1" futures = "0.3" +actix-rt = "*" env_logger = "0.10" log = "0.4" @@ -40,6 +41,7 @@ aes-gcm = "0.10.3" toml = "0.7.4" secp256k1-zkp = "0.11.0" bincode = "1.3.3" +tempfile = "3.14.0" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index 5ce19a7..5105d4c 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -40,6 +40,19 @@ impl Account { } } + pub fn new_with_balance(balance: u64) -> Self { + let key_holder = AddressKeyHolder::new_os_random(); + let address = key_holder.address; + let utxo_tree = UTXOSparseMerkleTree::new(); + + Self { + key_holder, + address, + balance, + utxo_tree, + } + } + pub fn produce_ephemeral_key_holder(&self) -> EphemeralKeyHolder { self.key_holder.produce_ephemeral_key_holder() } @@ -109,3 +122,77 @@ impl Default for Account { Self::new() } } + +#[cfg(test)] +mod tests { + use super::*; + + fn generate_dummy_utxo_nullifier() -> UTXONullifier { + UTXONullifier::default() + } + + fn generate_dummy_utxo(address: TreeHashType, amount: u128) -> UTXO { + let payload = UTXOPayload { + owner: address, + asset: vec![], + amount, + privacy_flag: false, + }; + UTXO::create_utxo_from_payload(payload) + } + + #[test] + fn test_new_account() { + let account = Account::new(); + + assert_eq!(account.balance, 0); + assert!(account.key_holder.address != [0u8; 32]); // Check if the address is not empty + } + + #[test] + fn test_mark_spent_utxo() { + let mut account = Account::new(); + let utxo = generate_dummy_utxo(account.address, 100); + account.add_new_utxo_outputs(vec![utxo]).unwrap(); + + let mut utxo_nullifier_map = HashMap::new(); + utxo_nullifier_map.insert(account.address, generate_dummy_utxo_nullifier()); + + let result = account.mark_spent_utxo(utxo_nullifier_map); + + assert!(result.is_ok()); + assert!(account.utxo_tree.store.get(&account.address).is_none()); + } + + #[test] + fn test_add_new_utxo_outputs() { + let mut account = Account::new(); + let utxo1 = generate_dummy_utxo(account.address, 100); + let utxo2 = generate_dummy_utxo(account.address, 200); + + let result = account.add_new_utxo_outputs(vec![utxo1.clone(), utxo2.clone()]); + + assert!(result.is_ok()); + assert_eq!(account.utxo_tree.store.len(), 2); + } + + #[test] + fn test_update_public_balance() { + let mut account = Account::new(); + account.update_public_balance(500); + + assert_eq!(account.balance, 500); + } + + #[test] + fn test_add_asset() { + let mut account = Account::new(); + let asset = "dummy_asset"; + let amount = 1000u128; + + let result = account.add_asset(asset, amount, false); + + assert!(result.is_ok()); + assert_eq!(account.utxo_tree.store.len(), 1); + } +} diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 817906f..df8e6b3 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -18,7 +18,7 @@ pub struct AddressKeyHolder { //Will be useful in future #[allow(dead_code)] top_secret_key_holder: TopSecretKeyHolder, - utxo_secret_key_holder: UTXOSecretKeyHolder, + pub utxo_secret_key_holder: UTXOSecretKeyHolder, pub address: TreeHashType, pub nullifer_public_key: PublicKey, pub viewing_public_key: PublicKey, diff --git a/node_core/Cargo.toml b/node_core/Cargo.toml index a93bd40..61fcc28 100644 --- a/node_core/Cargo.toml +++ b/node_core/Cargo.toml @@ -18,6 +18,10 @@ elliptic-curve.workspace = true reqwest.workspace = true thiserror.workspace = true tokio.workspace = true +tempfile.workspace = true +risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-1.2" } +hex.workspace = true +actix-rt.workspace = true [dependencies.accounts] path = "../accounts" @@ -28,6 +32,9 @@ path = "../storage" [dependencies.utxo] path = "../utxo" +[dependencies.zkvm] +path = "../zkvm" + [dependencies.secp256k1-zkp] workspace = true features = ["std", "rand-std", "rand", "serde", "global-context"] diff --git a/node_core/src/executions/mod.rs b/node_core/src/executions/mod.rs index 712e3e2..7c9d33e 100644 --- a/node_core/src/executions/mod.rs +++ b/node_core/src/executions/mod.rs @@ -1,2 +1,3 @@ -mod de; -mod se; +pub mod de; +pub mod private_exec; +pub mod se; diff --git a/node_core/src/executions/private_exec.rs b/node_core/src/executions/private_exec.rs new file mode 100644 index 0000000..2661458 --- /dev/null +++ b/node_core/src/executions/private_exec.rs @@ -0,0 +1,141 @@ +use bincode; +use k256::Scalar; +use monotree::hasher::Blake3; +use monotree::{Hasher, Monotree, Proof}; +use rand::thread_rng; +use secp256k1_zkp::{ + compute_adaptive_blinding_factor, verify_commitments_sum_to_equal, CommitmentSecrets, + Generator, PedersenCommitment, Tag, Tweak, SECP256K1, +}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use storage::{ + commitment::Commitment, commitments_sparse_merkle_tree::CommitmentsSparseMerkleTree, + nullifier::UTXONullifier, nullifier_sparse_merkle_tree::NullifierSparseMerkleTree, +}; +use utxo::{ + utxo_core::{UTXOPayload, UTXO}, + utxo_tree::UTXOSparseMerkleTree, +}; + +fn hash(input: &[u8]) -> Vec { + Sha256::digest(input).to_vec() +} + +// Generate nullifiers + +// takes the input_utxo and nsk +// returns the nullifiers[i], where the nullifier[i] = hash(in_commitments[i] || nsk) where the hash function +pub fn generate_nullifiers(input_utxo: &UTXO, nsk: &[u8]) -> Vec { + let mut input = bincode::serialize(input_utxo).unwrap().to_vec(); + input.extend_from_slice(nsk); + hash(&input) +} + +// Generate commitments for output UTXOs + +// uses the list of input_utxos[] +// returns in_commitments[] where each in_commitments[i] = Commitment(in_utxos[i]) where the commitment +pub fn generate_commitments(input_utxos: &[UTXO]) -> Vec> { + input_utxos + .iter() + .map(|utxo| { + let serialized = bincode::serialize(utxo).unwrap(); // Serialize UTXO. + hash(&serialized) + }) + .collect() +} + +// Validate inclusion proof for in_commitments + +// takes the in_commitments[i] as a leaf, the root hash root_commitment and the path in_commitments_proofs[i][], +// returns True if the in_commitments[i] is in the tree with root hash root_commitment otherwise returns False, as membership proof. +pub fn validate_in_commitments_proof( + in_commitment: &Vec, + root_commitment: Vec, + in_commitments_proof: &[Vec], +) -> bool { + // Placeholder implementation. + // Replace with Merkle proof verification logic. + // hash(&[pedersen_commitment.serialize().to_vec(), in_commitments_proof.concat()].concat()) == root_commitment + + let mut nsmt = CommitmentsSparseMerkleTree { + curr_root: Option::Some(root_commitment), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let commitments: Vec<_> = in_commitments_proof + .into_iter() + .map(|n_p| Commitment { + commitment_hash: n_p.clone(), + }) + .collect(); + nsmt.insert_items(commitments).unwrap(); + + nsmt.get_non_membership_proof(in_commitment.clone()) + .unwrap() + .1 + .is_some() +} + +// Validate non-membership proof for nullifiers + +// takes the nullifiers[i], path nullifiers_proof[i][] and the root hash root_nullifier, +// returns True if the nullifiers[i] is not in the tree with root hash root_nullifier otherwise returns False, as non-membership proof. +pub fn validate_nullifiers_proof( + nullifier: [u8; 32], + root_nullifier: [u8; 32], + nullifiers_proof: &[[u8; 32]], +) -> bool { + let mut nsmt = NullifierSparseMerkleTree { + curr_root: Option::Some(root_nullifier), + tree: Monotree::default(), + hasher: Blake3::new(), + }; + + let nullifiers: Vec<_> = nullifiers_proof + .into_iter() + .map(|n_p| UTXONullifier { utxo_hash: *n_p }) + .collect(); + nsmt.insert_items(nullifiers).unwrap(); + + nsmt.get_non_membership_proof(nullifier) + .unwrap() + .1 + .is_none() +} + +fn private_kernel( + root_commitment: &[u8], + root_nullifier: [u8; 32], + input_utxos: &[UTXO], + in_commitments_proof: &[Vec], + nullifiers_proof: &[[u8; 32]], + nullifier_secret_key: Scalar, +) -> (Vec, Vec>) { + let nullifiers: Vec<_> = input_utxos + .into_iter() + .map(|utxo| generate_nullifiers(&utxo, &nullifier_secret_key.to_bytes())) + .collect(); + + let in_commitments = generate_commitments(&input_utxos); + + for in_commitment in in_commitments { + validate_in_commitments_proof( + &in_commitment, + root_commitment.to_vec(), + in_commitments_proof, + ); + } + + for nullifier in nullifiers.iter() { + validate_nullifiers_proof( + nullifier[0..32].try_into().unwrap(), + root_nullifier, + nullifiers_proof, + ); + } + + (vec![], nullifiers) +} diff --git a/node_core/src/lib.rs b/node_core/src/lib.rs index 23437ab..1d5455b 100644 --- a/node_core/src/lib.rs +++ b/node_core/src/lib.rs @@ -1,39 +1,81 @@ -use std::sync::{ - atomic::{AtomicU64, Ordering}, - Arc, +use std::{ + collections::HashMap, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, }; -use accounts::account_core::Account; +use k256::elliptic_curve::group::GroupEncoding; + +use ::storage::transaction::{Transaction, TransactionPayload, TxKind}; +use accounts::account_core::{Account, AccountAddress}; use anyhow::Result; use config::NodeConfig; -use sequencer_client::SequencerClient; +use executions::{ + private_exec::{generate_commitments, generate_nullifiers}, + se::{commit, tag_random}, +}; +use log::info; +use rand::thread_rng; +use secp256k1_zkp::{CommitmentSecrets, Tweak}; +use sequencer_client::{json::SendTxResponse, SequencerClient}; +use serde::{Deserialize, Serialize}; use storage::NodeChainStore; -use tokio::{sync::Mutex, task::JoinHandle}; +use tokio::{sync::RwLock, task::JoinHandle}; +use utxo::utxo_core::UTXO; +use zkvm::{ + prove_mint_utxo, prove_send_utxo, prove_send_utxo_deshielded, prove_send_utxo_shielded, +}; + +pub const BLOCK_GEN_DELAY_SECS: u64 = 20; pub mod config; pub mod executions; pub mod sequencer_client; pub mod storage; +#[derive(Debug, Serialize, Deserialize)] +pub struct MintMoneyPublicTx { + pub acc: AccountAddress, + pub amount: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SendMoneyShieldedTx { + pub acc_sender: AccountAddress, + pub amount: u128, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct SendMoneyDeshieldedTx { + pub receiver_data: Vec<(u128, AccountAddress)>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum ActionData { + MintMoneyPublicTx(MintMoneyPublicTx), + SendMoneyShieldedTx(SendMoneyShieldedTx), + SendMoneyDeshieldedTx(SendMoneyDeshieldedTx), +} + pub struct NodeCore { - pub storage: Arc>, + pub storage: Arc>, pub curr_height: Arc, - pub main_acc: Account, pub node_config: NodeConfig, pub db_updater_handle: JoinHandle>, + pub sequencer_client: Arc, } impl NodeCore { pub async fn start_from_config_update_chain(config: NodeConfig) -> Result { - let client = SequencerClient::new(config.clone())?; + let client = Arc::new(SequencerClient::new(config.clone())?); let genesis_id = client.get_genesis_id().await?; let genesis_block = client.get_block(genesis_id.genesis_id).await?.block; let mut storage = NodeChainStore::new_with_genesis(&config.home, genesis_block); - let account = Account::new(); - let mut chain_height = genesis_id.genesis_id; //Chain update loop @@ -49,7 +91,7 @@ impl NodeCore { chain_height += 1; } - let wrapped_storage = Arc::new(Mutex::new(storage)); + let wrapped_storage = Arc::new(RwLock::new(storage)); let chain_height_wrapped = Arc::new(AtomicU64::new(chain_height)); let wrapped_storage_thread = wrapped_storage.clone(); @@ -62,7 +104,7 @@ impl NodeCore { if let Ok(block) = client_thread.get_block(next_block).await { { - let mut storage_guard = wrapped_storage_thread.lock().await; + let mut storage_guard = wrapped_storage_thread.write().await; storage_guard.dissect_insert_block(block.block)?; } @@ -80,9 +122,580 @@ impl NodeCore { Ok(Self { storage: wrapped_storage, curr_height: chain_height_wrapped, - main_acc: account, node_config: config.clone(), db_updater_handle: updater_handle, + sequencer_client: client.clone(), }) } + + pub async fn create_new_account(&mut self) -> AccountAddress { + let account = Account::new(); + + let addr = account.address; + + { + let mut write_guard = self.storage.write().await; + + write_guard.acc_map.insert(account.address, account); + } + + addr + } + + pub async fn mint_utxo_private( + &self, + acc: AccountAddress, + amount: u128, + ) -> (Transaction, [u8; 32]) { + let (utxo, receipt) = prove_mint_utxo(amount, acc); + let result_hash = utxo.hash; + + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let encoded_data = Account::encrypt_data( + &ephm_key_holder, + accout.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo).unwrap(), + ); + + let comm = generate_commitments(&vec![utxo]); + + ( + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: comm + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![], + execution_proof_private: serde_json::to_string(&receipt).unwrap(), + encoded_data: vec![(encoded_data.0, encoded_data.1.to_vec())], + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + result_hash, + ) + } + + pub fn deposit_money_public(&self, acc: AccountAddress, amount: u128) -> Transaction { + TransactionPayload { + tx_kind: TxKind::Public, + execution_input: serde_json::to_vec(&ActionData::MintMoneyPublicTx( + MintMoneyPublicTx { acc, amount }, + )) + .unwrap(), + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: vec![], + nullifier_created_hashes: vec![], + execution_proof_private: "".to_string(), + encoded_data: vec![], + ephemeral_pub_key: vec![], + } + .into() + } + + pub async fn transfer_utxo_private( + &self, + utxo: UTXO, + receivers: Vec<(u128, AccountAddress)>, + ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); + + let commitment_in = { + let guard = self.storage.write().await; + + guard.utxo_commitments_store.get_tx(utxo.hash).unwrap().hash + }; + + let nullifier = generate_nullifiers( + &utxo, + &accout + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key + .to_bytes() + .to_vec(), + ); + + let (resulting_utxos, receipt) = prove_send_utxo(utxo, receivers); + let utxo_hashes = resulting_utxos + .iter() + .map(|(utxo, addr)| (addr.clone(), utxo.hash)) + .collect(); + + let utxos: Vec = resulting_utxos + .iter() + .map(|(utxo, _)| utxo.clone()) + .collect(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let encoded_data: Vec<(Vec, Vec)> = utxos + .iter() + .map(|utxo_enc| { + let accout_enc = acc_map_read_guard.acc_map.get(&utxo_enc.owner).unwrap(); + + let (ciphertext, nonce) = Account::encrypt_data( + &ephm_key_holder, + accout_enc.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo_enc).unwrap(), + ); + + (ciphertext, nonce.to_vec()) + }) + .collect(); + + let commitments = generate_commitments(&utxos); + + ( + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: vec![], + utxo_commitments_spent_hashes: vec![commitment_in], + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: serde_json::to_string(&receipt).unwrap(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + utxo_hashes, + ) + } + + pub async fn transfer_balance_shielded( + &self, + acc: AccountAddress, + balance: u64, + receivers: Vec<(u128, AccountAddress)>, + ) -> (Transaction, Vec<(AccountAddress, [u8; 32])>) { + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&acc).unwrap(); + + let commitment_secrets = CommitmentSecrets { + value: balance, + value_blinding_factor: Tweak::from_slice( + &accout + .key_holder + .utxo_secret_key_holder + .viewing_secret_key + .to_bytes() + .to_vec(), + ) + .unwrap(), + generator_blinding_factor: Tweak::new(&mut thread_rng()), + }; + + let tag = tag_random(); + let commitment = commit(&commitment_secrets, tag); + + let nullifier = executions::se::generate_nullifiers( + &commitment, + &accout + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key + .to_bytes() + .to_vec(), + ); + + let (resulting_utxos, receipt) = prove_send_utxo_shielded(acc, balance as u128, receivers); + let utxo_hashes = resulting_utxos + .iter() + .map(|(utxo, addr)| (addr.clone(), utxo.hash)) + .collect(); + + let utxos: Vec = resulting_utxos + .iter() + .map(|(utxo, _)| utxo.clone()) + .collect(); + + let ephm_key_holder = &accout.produce_ephemeral_key_holder(); + + let eph_pub_key = ephm_key_holder.generate_ephemeral_public_key().to_bytes(); + + let encoded_data: Vec<(Vec, Vec)> = utxos + .iter() + .map(|utxo_enc| { + let accout_enc = acc_map_read_guard.acc_map.get(&utxo_enc.owner).unwrap(); + + let (ciphertext, nonce) = Account::encrypt_data( + &ephm_key_holder, + accout_enc.key_holder.viewing_public_key, + &serde_json::to_vec(&utxo_enc).unwrap(), + ); + + (ciphertext, nonce.to_vec()) + }) + .collect(); + + let commitments = generate_commitments(&utxos); + + ( + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: serde_json::to_vec(&ActionData::SendMoneyShieldedTx( + SendMoneyShieldedTx { + acc_sender: acc, + amount: balance as u128, + }, + )) + .unwrap(), + execution_output: vec![], + utxo_commitments_spent_hashes: vec![], + utxo_commitments_created_hashes: commitments + .into_iter() + .map(|hash_data| hash_data.try_into().unwrap()) + .collect(), + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: serde_json::to_string(&receipt).unwrap(), + encoded_data, + ephemeral_pub_key: eph_pub_key.to_vec(), + } + .into(), + utxo_hashes, + ) + } + + pub async fn transfer_utxo_deshielded( + &self, + utxo: UTXO, + receivers: Vec<(u128, AccountAddress)>, + ) -> Transaction { + let acc_map_read_guard = self.storage.read().await; + + let accout = acc_map_read_guard.acc_map.get(&utxo.owner).unwrap(); + + let commitment_in = { + let guard = self.storage.write().await; + + guard.utxo_commitments_store.get_tx(utxo.hash).unwrap().hash + }; + + let nullifier = generate_nullifiers( + &utxo, + &accout + .key_holder + .utxo_secret_key_holder + .nullifier_secret_key + .to_bytes() + .to_vec(), + ); + + let (resulting_balances, receipt) = prove_send_utxo_deshielded(utxo, receivers); + + TransactionPayload { + tx_kind: TxKind::Private, + execution_input: vec![], + execution_output: serde_json::to_vec(&ActionData::SendMoneyDeshieldedTx( + SendMoneyDeshieldedTx { + receiver_data: resulting_balances, + }, + )) + .unwrap(), + utxo_commitments_spent_hashes: vec![commitment_in], + utxo_commitments_created_hashes: vec![], + nullifier_created_hashes: vec![nullifier.try_into().unwrap()], + execution_proof_private: serde_json::to_string(&receipt).unwrap(), + encoded_data: vec![], + ephemeral_pub_key: vec![], + } + .into() + } + + pub async fn send_private_mint_tx( + &self, + acc: AccountAddress, + amount: u128, + ) -> Result<(SendTxResponse, [u8; 32])> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hash) = self.mint_utxo_private(acc, amount).await; + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Mint utxo proof spent {timedelta:?} milliseconds"); + + Ok((self.sequencer_client.send_tx(tx).await?, utxo_hash)) + } + + pub async fn send_public_deposit( + &self, + acc: AccountAddress, + amount: u128, + ) -> Result { + Ok(self + .sequencer_client + .send_tx(self.deposit_money_public(acc, amount)) + .await?) + } + + pub async fn send_private_send_tx( + &self, + utxo: UTXO, + receivers: Vec<(u128, AccountAddress)>, + ) -> Result<(SendTxResponse, Vec<([u8; 32], [u8; 32])>)> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hashes) = self.transfer_utxo_private(utxo, receivers).await; + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Send private utxo proof spent {timedelta:?} milliseconds"); + + Ok((self.sequencer_client.send_tx(tx).await?, utxo_hashes)) + } + + pub async fn send_shielded_send_tx( + &self, + acc: AccountAddress, + amount: u64, + receivers: Vec<(u128, AccountAddress)>, + ) -> Result<(SendTxResponse, Vec<([u8; 32], [u8; 32])>)> { + let point_before_prove = std::time::Instant::now(); + let (tx, utxo_hashes) = self.transfer_balance_shielded(acc, amount, receivers).await; + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Send balance shielded proof spent {timedelta:?} milliseconds"); + + Ok((self.sequencer_client.send_tx(tx).await?, utxo_hashes)) + } + + pub async fn send_deshielded_send_tx( + &self, + utxo: UTXO, + receivers: Vec<(u128, AccountAddress)>, + ) -> Result { + let point_before_prove = std::time::Instant::now(); + let tx = self.transfer_utxo_deshielded(utxo, receivers).await; + let point_after_prove = std::time::Instant::now(); + + let timedelta = (point_after_prove - point_before_prove).as_millis(); + info!("Send deshielded utxo proof spent {timedelta:?} milliseconds"); + + Ok(self.sequencer_client.send_tx(tx).await?) + } + + ///Mint utxo, make it public + pub async fn subscenario_1(&mut self) { + let acc_addr = self.create_new_account().await; + + let (resp, new_utxo_hash) = self.send_private_mint_tx(acc_addr, 100).await.unwrap(); + info!("Response for mint private is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + + let acc_map_read_guard = self.storage.read().await; + let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + let resp = self + .send_deshielded_send_tx(new_utxo, vec![(100, acc_addr)]) + .await + .unwrap(); + info!("Response for send deshielded is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + info!("New account public balance is {:?}", acc.balance); + } + + ///Deposit balance, make it private + pub async fn subscenario_2(&mut self) { + let acc_addr = self.create_new_account().await; + + let resp = self.send_public_deposit(acc_addr, 100).await.unwrap(); + info!("Response for public deposit is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let acc_map_read_guard = self.storage.read().await; + let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + + info!("New acconut public balance is {:?}", acc.balance); + + let (resp, new_utxo_hashes) = self + .send_shielded_send_tx(acc_addr, 100, vec![(100, acc_addr)]) + .await + .unwrap(); + info!("Response for send shielded is {resp:?}"); + + let new_utxo_hash = new_utxo_hashes[0].1; + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + info!("User received new utxo {new_utxo:?}"); + } + + ///Mint utxo, privately send it to another user + pub async fn subscenario_3(&mut self) { + let acc_addr = self.create_new_account().await; + let acc_addr_rec = self.create_new_account().await; + + let (resp, new_utxo_hash) = self.send_private_mint_tx(acc_addr, 100).await.unwrap(); + info!("Response for mint private is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + + let (resp, new_utxo_hashes) = self + .send_private_send_tx(new_utxo, vec![(100, acc_addr_rec)]) + .await + .unwrap(); + info!("Response for send deshielded is {resp:?}"); + + let new_utxo_hash = new_utxo_hashes[0].1; + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + + info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); + } + + ///Deposit balance, shielded send it to another user + pub async fn subscenario_4(&mut self) { + let acc_addr = self.create_new_account().await; + let acc_addr_rec = self.create_new_account().await; + + let resp = self.send_public_deposit(acc_addr, 100).await.unwrap(); + info!("Response for public deposit is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let acc_map_read_guard = self.storage.read().await; + let acc = acc_map_read_guard.acc_map.get(&acc_addr).unwrap(); + + info!("New acconut public balance is {:?}", acc.balance); + + let (resp, new_utxo_hashes) = self + .send_shielded_send_tx(acc_addr, 100, vec![(100, acc_addr_rec)]) + .await + .unwrap(); + info!("Response for send shielded is {resp:?}"); + + let new_utxo_hash = new_utxo_hashes[0].1; + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr_rec).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + + info!("User {acc_addr_rec:?} received new utxo {new_utxo:?}"); + } + + ///Mint utxo, deshielded send it to another user + pub async fn subscenario_5(&mut self) { + let acc_addr = self.create_new_account().await; + let acc_addr_rec = self.create_new_account().await; + + let (resp, new_utxo_hash) = self.send_private_mint_tx(acc_addr, 100).await.unwrap(); + info!("Response for mint private is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let new_utxo = { + let mut write_guard = self.storage.write().await; + + let acc = write_guard.acc_map.get_mut(&acc_addr).unwrap(); + + acc.utxo_tree + .get_item(new_utxo_hash) + .unwrap() + .unwrap() + .clone() + }; + + let resp = self + .send_deshielded_send_tx(new_utxo, vec![(100, acc_addr_rec)]) + .await + .unwrap(); + info!("Response for send deshielded is {resp:?}"); + + info!("Awaiting new blocks"); + tokio::time::sleep(std::time::Duration::from_secs(BLOCK_GEN_DELAY_SECS)).await; + + let read_guard = self.storage.read().await; + let acc_rec = read_guard.acc_map.get(&acc_addr_rec).unwrap(); + + info!("New account public balance is {:?}", acc_rec.balance); + } } diff --git a/node_core/src/sequencer_client/json.rs b/node_core/src/sequencer_client/json.rs index d36b695..4865f81 100644 --- a/node_core/src/sequencer_client/json.rs +++ b/node_core/src/sequencer_client/json.rs @@ -33,6 +33,7 @@ pub struct RegisterAccountResponse { #[derive(Serialize, Deserialize, Debug)] pub struct SendTxResponse { pub status: String, + pub additional_data: Option, } #[derive(Serialize, Deserialize, Debug)] diff --git a/node_core/src/storage/accounts_store.rs b/node_core/src/storage/accounts_store.rs index bcf1bc4..6cdb100 100644 --- a/node_core/src/storage/accounts_store.rs +++ b/node_core/src/storage/accounts_store.rs @@ -26,3 +26,86 @@ impl Default for NodeAccountsStore { Self::new() } } + +#[cfg(test)] +mod tests { + use super::*; + use accounts::account_core::Account; + /// Helper function to create a sample account + fn create_sample_account(balance: u64) -> Account { + Account::new_with_balance(balance) + } + + fn pad_to_32(slice: &[u8]) -> [u8; 32] { + let mut padded = [0u8; 32]; + let len = slice.len().min(32); + padded[..len].copy_from_slice(&slice[..len]); + padded + } + + #[test] + fn test_create_empty_store() { + let store = NodeAccountsStore::new(); + assert!(store.accounts.is_empty()); + } + + #[test] + fn test_register_account() { + let mut store = NodeAccountsStore::new(); + + let account = create_sample_account(100); + let account_addr = account.address.clone(); + + store.register_account(account); + + assert_eq!(store.accounts.len(), 1); + let stored_account = store.accounts.get(&account_addr).unwrap(); + assert_eq!(stored_account.balance, 100); + } + + #[test] + fn test_unregister_account() { + let mut store = NodeAccountsStore::new(); + + let account = create_sample_account(100); + let account_addr = account.address.clone(); + store.register_account(account); + + assert_eq!(store.accounts.len(), 1); + + store.unregister_account(account_addr); + assert!(store.accounts.is_empty()); + } + + #[test] + fn test_unregister_nonexistent_account() { + let mut store = NodeAccountsStore::new(); + + let account_addr: [u8; 32] = pad_to_32("nonexistent".to_string().as_bytes()); + store.unregister_account(account_addr); + + assert!(store.accounts.is_empty()); + } + + #[test] + fn test_register_multiple_accounts() { + let mut store = NodeAccountsStore::new(); + + let account1 = create_sample_account(100); + let account2 = create_sample_account(200); + + let address_1 = account1.address.clone(); + let address_2 = account2.address.clone(); + + store.register_account(account1); + store.register_account(account2); + + assert_eq!(store.accounts.len(), 2); + + let stored_account1 = store.accounts.get(&address_1).unwrap(); + let stored_account2 = store.accounts.get(&address_2).unwrap(); + + assert_eq!(stored_account1.balance, 100); + assert_eq!(stored_account2.balance, 200); + } +} diff --git a/node_core/src/storage/block_store.rs b/node_core/src/storage/block_store.rs index 2766f45..6991ee5 100644 --- a/node_core/src/storage/block_store.rs +++ b/node_core/src/storage/block_store.rs @@ -1,6 +1,6 @@ use std::path::Path; -use anyhow::Result; +use anyhow::{anyhow, Result}; use storage::{block::Block, RocksDBIO}; pub struct NodeBlockStore { @@ -20,14 +20,128 @@ impl NodeBlockStore { ///Reopening existing database pub fn open_db_restart(location: &Path) -> Result { + NodeBlockStore::db_destroy(location)?; NodeBlockStore::open_db_with_genesis(location, None) } + ///Reloading existing database + pub fn open_db_reload(location: &Path) -> Result { + NodeBlockStore::open_db_with_genesis(location, None) + } + + ///Destroying existing database + fn db_destroy(location: &Path) -> Result<()> { + RocksDBIO::destroy(location).map_err(|err| anyhow!("RocksDBIO error: {}", err)) + } + pub fn get_block_at_id(&self, id: u64) -> Result { Ok(self.dbio.get_block(id)?) } pub fn put_block_at_id(&self, block: Block) -> Result<()> { - Ok(self.dbio.put_block(block)?) + Ok(self.dbio.put_block(block, false)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use storage::block::{Block, Data}; + use tempfile::tempdir; + + fn create_genesis_block() -> Block { + Block { + block_id: 0, + prev_block_id: 0, + prev_block_hash: [0; 32], + hash: [1; 32], + transactions: vec![], + data: Data::default(), + } + } + + fn create_sample_block(block_id: u64, prev_block_id: u64) -> Block { + Block { + block_id: block_id, + prev_block_id: prev_block_id, + prev_block_hash: [0; 32], + hash: [1; 32], + transactions: vec![], + data: Data::default(), + } + } + + #[test] + fn test_open_db_with_genesis() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let genesis_block = create_genesis_block(); + let node_store = + NodeBlockStore::open_db_with_genesis(path, Some(genesis_block.clone())).unwrap(); + + // Verify the genesis block is stored + let stored_block = node_store.get_block_at_id(0).unwrap(); + assert_eq!(stored_block.block_id, genesis_block.block_id); + assert_eq!(stored_block.hash, genesis_block.hash); + } + + #[test] + fn test_open_db_restart() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let genesis_block = create_genesis_block(); + let _ = NodeBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); + + // Restart the database + let node_store = NodeBlockStore::open_db_restart(path).unwrap(); + + // The block should no longer be available since no genesis block is set on restart + let result = node_store.get_block_at_id(0); + assert!(result.is_err()); + } + + #[test] + fn test_open_db_reload() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let genesis_block = create_genesis_block(); + let _ = NodeBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); + + // Reload the database + let node_store = NodeBlockStore::open_db_reload(path).unwrap(); + + // The genesis block should be available on reload + let result = node_store.get_block_at_id(0); + assert!(!result.is_err()); + } + + #[test] + fn test_put_and_get_block() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let genesis_block = create_genesis_block(); + let node_store = NodeBlockStore::open_db_with_genesis(path, Some(genesis_block)).unwrap(); + + let block = create_sample_block(1, 0); + node_store.put_block_at_id(block.clone()).unwrap(); + + let retrieved_block = node_store.get_block_at_id(1).unwrap(); + assert_eq!(retrieved_block.block_id, block.block_id); + assert_eq!(retrieved_block.hash, block.hash); + } + + #[test] + fn test_get_block_not_found() { + let temp_dir = tempdir().unwrap(); + let path = temp_dir.path(); + + let node_store = NodeBlockStore::open_db_with_genesis(path, None).unwrap(); + + let result = node_store.get_block_at_id(42); + assert!(result.is_err()); } } diff --git a/node_core/src/storage/mod.rs b/node_core/src/storage/mod.rs index 3e16864..93abee2 100644 --- a/node_core/src/storage/mod.rs +++ b/node_core/src/storage/mod.rs @@ -1,34 +1,38 @@ -use std::path::Path; +use std::{collections::HashMap, path::Path}; use accounts::account_core::{Account, AccountAddress}; use accounts_store::NodeAccountsStore; use anyhow::Result; use block_store::NodeBlockStore; +use elliptic_curve::group::GroupEncoding; +use k256::AffinePoint; use storage::{ block::Block, - merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, + merkle_tree_public::{ + merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, + TreeHashType, + }, nullifier::UTXONullifier, nullifier_sparse_merkle_tree::NullifierSparseMerkleTree, + transaction::Transaction, utxo_commitment::UTXOCommitment, }; +use utxo::utxo_core::UTXO; pub mod accounts_store; pub mod block_store; pub struct NodeChainStore { - pub acc_store: NodeAccountsStore, + pub acc_map: HashMap, pub block_store: NodeBlockStore, pub nullifier_store: NullifierSparseMerkleTree, pub utxo_commitments_store: UTXOCommitmentsMerkleTree, pub pub_tx_store: PublicTransactionMerkleTree, - ///For simplicity, we will allow only one account per node. - /// ToDo: Change it in future - node_main_account_info: Account, } impl NodeChainStore { pub fn new_with_genesis(home_dir: &Path, genesis_block: Block) -> Self { - let acc_store = NodeAccountsStore::default(); + let acc_map = HashMap::new(); let nullifier_store = NullifierSparseMerkleTree::default(); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); @@ -40,19 +44,14 @@ impl NodeChainStore { .unwrap(); Self { - acc_store, + acc_map, block_store, nullifier_store, utxo_commitments_store, pub_tx_store, - node_main_account_info: Account::new(), } } - pub fn get_main_account_addr(&self) -> AccountAddress { - self.node_main_account_info.address - } - pub fn dissect_insert_block(&mut self, block: Block) -> Result<()> { for tx in &block.transactions { self.utxo_commitments_store.add_tx_multiple( @@ -71,6 +70,43 @@ impl NodeChainStore { .collect(), )?; + let slice_try: Result<[u8; 33], _> = tx.ephemeral_pub_key.clone().try_into(); + let eph_key_compressed = + slice_try.and_then(|inner| Ok(::Repr::from(inner))); + + if let Ok(eph_key_compressed) = eph_key_compressed { + let ephemeral_public_key_sender = AffinePoint::from_bytes(&eph_key_compressed); + + if ephemeral_public_key_sender.is_some().into() { + let ephemeral_public_key_sender = ephemeral_public_key_sender.unwrap(); + + for (ciphertext, nonce) in tx.encoded_data.clone() { + let slice = nonce.as_slice(); + let nonce = + accounts::key_management::constants_types::Nonce::clone_from_slice( + slice, + ); + + for (acc_id, acc) in self.acc_map.iter_mut() { + let decoded_data_curr_acc = acc.decrypt_data( + ephemeral_public_key_sender, + ciphertext.clone(), + nonce, + ); + + let decoded_utxo_try = + serde_json::from_slice::(&decoded_data_curr_acc); + + if let Ok(utxo) = decoded_utxo_try { + if &utxo.owner == acc_id { + acc.utxo_tree.insert_item(utxo)?; + } + } + } + } + } + } + self.pub_tx_store.add_tx(tx.clone()); } diff --git a/node_rpc/Cargo.toml b/node_rpc/Cargo.toml index 86588ed..6188e5a 100644 --- a/node_rpc/Cargo.toml +++ b/node_rpc/Cargo.toml @@ -12,6 +12,8 @@ serde.workspace = true actix.workspace = true actix-cors.workspace = true futures.workspace = true +tokio.workspace = true +hex.workspace = true actix-web.workspace = true diff --git a/node_rpc/src/lib.rs b/node_rpc/src/lib.rs index 71a3b8f..5262f9d 100644 --- a/node_rpc/src/lib.rs +++ b/node_rpc/src/lib.rs @@ -2,6 +2,9 @@ pub mod net_utils; pub mod process; pub mod types; +use std::sync::Arc; + +use node_core::{config::NodeConfig, NodeCore}; use rpc_primitives::{ errors::{RpcError, RpcErrorKind}, RpcPollingConfig, @@ -10,12 +13,15 @@ use serde::Serialize; use serde_json::Value; pub use net_utils::*; +use tokio::sync::Mutex; use self::types::err_rpc::RpcErr; //ToDo: Add necessary fields pub struct JsonHandler { pub polling_config: RpcPollingConfig, + pub node_core_config: NodeConfig, + pub node_chain_store: Arc>, } fn respond(val: T) -> Result { diff --git a/node_rpc/src/net_utils.rs b/node_rpc/src/net_utils.rs index 35c46ed..f4182c4 100644 --- a/node_rpc/src/net_utils.rs +++ b/node_rpc/src/net_utils.rs @@ -1,4 +1,5 @@ use std::io; +use std::sync::Arc; use actix_cors::Cors; use actix_web::{http, middleware, web, App, Error as HttpError, HttpResponse, HttpServer}; @@ -6,8 +7,11 @@ use futures::Future; use futures::FutureExt; use log::info; +use node_core::config::NodeConfig; +use node_core::NodeCore; use rpc_primitives::message::Message; use rpc_primitives::RpcConfig; +use tokio::sync::Mutex; use super::JsonHandler; @@ -38,7 +42,11 @@ fn get_cors(cors_allowed_origins: &[String]) -> Cors { } #[allow(clippy::too_many_arguments)] -pub fn new_http_server(config: RpcConfig) -> io::Result { +pub fn new_http_server( + config: RpcConfig, + node_config: NodeConfig, + node_chain_store: Arc>, +) -> io::Result { let RpcConfig { addr, cors_allowed_origins, @@ -46,7 +54,11 @@ pub fn new_http_server(config: RpcConfig) -> io::Result limits_config, } = config; info!(target:"network", "Starting http server at {}", addr); - let handler = web::Data::new(JsonHandler { polling_config }); + let handler = web::Data::new(JsonHandler { + polling_config, + node_core_config: node_config, + node_chain_store, + }); // HTTP server Ok(HttpServer::new(move || { diff --git a/node_rpc/src/process.rs b/node_rpc/src/process.rs index aad6124..8622f60 100644 --- a/node_rpc/src/process.rs +++ b/node_rpc/src/process.rs @@ -9,7 +9,13 @@ use rpc_primitives::{ use crate::{ rpc_error_responce_inverter, - types::rpc_structs::{HelloRequest, HelloResponse}, + types::{ + err_rpc::cast_seq_client_error_into_rpc_error, + rpc_structs::{ + ExecuteSubscenarioRequest, ExecuteSubscenarioResponse, RegisterAccountRequest, + RegisterAccountResponse, SendTxRequest, + }, + }, }; use super::{respond, types::err_rpc::RpcErr, JsonHandler}; @@ -31,13 +37,60 @@ impl JsonHandler { } } - #[allow(clippy::unused_async)] - ///Example of request processing - async fn process_temp_hello(&self, request: Request) -> Result { - let _hello_request = HelloRequest::parse(Some(request.params))?; + async fn process_request_execute_subscenario(&self, request: Request) -> Result { + let req = ExecuteSubscenarioRequest::parse(Some(request.params))?; - let helperstruct = HelloResponse { - greeting: "HELLO_FROM_NODE".to_string(), + { + let mut store = self.node_chain_store.lock().await; + + match req.scenario_id { + 1 => store.subscenario_1().await, + 2 => store.subscenario_2().await, + 3 => store.subscenario_3().await, + 4 => store.subscenario_4().await, + 5 => store.subscenario_5().await, + _ => return Err(RpcErr(RpcError::invalid_params("Scenario id not found"))), + } + } + + let helperstruct = ExecuteSubscenarioResponse { + scenario_result: "success".to_string(), + }; + + respond(helperstruct) + } + + async fn process_register_account(&self, request: Request) -> Result { + let _req = RegisterAccountRequest::parse(Some(request.params))?; + + let acc_addr = { + let mut guard = self.node_chain_store.lock().await; + + guard.create_new_account().await + }; + + let helperstruct = RegisterAccountResponse { + status: hex::encode(acc_addr), + }; + + respond(helperstruct) + } + + async fn process_send_tx(&self, request: Request) -> Result { + let req = SendTxRequest::parse(Some(request.params))?; + + { + let guard = self.node_chain_store.lock().await; + + guard + .sequencer_client + .send_tx(req.transaction) + .await + .map_err(cast_seq_client_error_into_rpc_error)?; + } + + let helperstruct = RegisterAccountResponse { + status: "success".to_string(), }; respond(helperstruct) @@ -46,7 +99,9 @@ impl JsonHandler { pub async fn process_request_internal(&self, request: Request) -> Result { match request.method.as_ref() { //Todo : Add handling of more JSON RPC methods - "hello" => self.process_temp_hello(request).await, + "register_account" => self.process_register_account(request).await, + "execute_subscenario" => self.process_request_execute_subscenario(request).await, + "send_tx" => self.process_send_tx(request).await, _ => Err(RpcErr(RpcError::method_not_found(request.method))), } } diff --git a/node_rpc/src/types/err_rpc.rs b/node_rpc/src/types/err_rpc.rs index f5998d2..5f84e2b 100644 --- a/node_rpc/src/types/err_rpc.rs +++ b/node_rpc/src/types/err_rpc.rs @@ -1,5 +1,6 @@ use log::debug; +use node_core::sequencer_client::SequencerClientError; use rpc_primitives::errors::{RpcError, RpcParseError}; pub struct RpcErr(pub RpcError); @@ -45,3 +46,12 @@ pub fn from_rpc_err_into_anyhow_err(rpc_err: RpcError) -> anyhow::Error { debug!("Rpc error cast to anyhow error : err {rpc_err:?}"); anyhow::anyhow!(format!("{rpc_err:#?}")) } + +pub fn cast_seq_client_error_into_rpc_error(seq_cli_err: SequencerClientError) -> RpcError { + let error_string = seq_cli_err.to_string(); + + match seq_cli_err { + SequencerClientError::SerdeError(_) => RpcError::serialization_error(&error_string), + SequencerClientError::HTTPError(_) => RpcError::new_internal_error(None, &error_string), + } +} diff --git a/node_rpc/src/types/rpc_structs.rs b/node_rpc/src/types/rpc_structs.rs index 4d7a2c4..ce1a43e 100644 --- a/node_rpc/src/types/rpc_structs.rs +++ b/node_rpc/src/types/rpc_structs.rs @@ -4,13 +4,62 @@ use rpc_primitives::parser::parse_params; use rpc_primitives::parser::RpcRequest; use serde::{Deserialize, Serialize}; use serde_json::Value; +use storage::block::Block; +use storage::transaction::Transaction; #[derive(Serialize, Deserialize, Debug)] -pub struct HelloRequest {} +pub struct RegisterAccountRequest {} -parse_request!(HelloRequest); +#[derive(Serialize, Deserialize, Debug)] +pub struct SendTxRequest { + pub transaction: Transaction, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetBlockDataRequest { + pub block_id: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteSubscenarioRequest { + pub scenario_id: u64, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetGenesisIdRequest {} + +parse_request!(RegisterAccountRequest); +parse_request!(SendTxRequest); +parse_request!(GetBlockDataRequest); +parse_request!(GetGenesisIdRequest); +parse_request!(ExecuteSubscenarioRequest); #[derive(Serialize, Deserialize, Debug)] pub struct HelloResponse { pub greeting: String, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct RegisterAccountResponse { + pub status: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SendTxResponse { + pub status: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetBlockDataResponse { + pub block: Block, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ExecuteSubscenarioResponse { + pub scenario_result: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetGenesisIdResponse { + pub genesis_id: u64, +} diff --git a/node_runner/src/lib.rs b/node_runner/src/lib.rs index bd6d129..8641da5 100644 --- a/node_runner/src/lib.rs +++ b/node_runner/src/lib.rs @@ -1,9 +1,10 @@ -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; use anyhow::Result; use consensus::ConsensusManager; use log::info; use networking::peer_manager::PeerManager; +use node_core::{config::NodeConfig, NodeCore}; use node_rpc::new_http_server; use rpc_primitives::RpcConfig; use tokio::sync::Mutex; @@ -11,7 +12,22 @@ use tokio::sync::Mutex; pub async fn main_runner() -> Result<()> { env_logger::init(); - let http_server = new_http_server(RpcConfig::default())?; + //ToDo: Change it + let node_config = NodeConfig { + home: PathBuf::new(), + override_rust_log: None, + sequencer_addr: "addr".to_string(), + seq_poll_timeout_secs: 1, + }; + + let node_core = NodeCore::start_from_config_update_chain(node_config.clone()).await?; + let wrapped_node_core = Arc::new(Mutex::new(node_core)); + + let http_server = new_http_server( + RpcConfig::default(), + node_config.clone(), + wrapped_node_core.clone(), + )?; info!("HTTP server started"); let _http_server_handle = http_server.handle(); tokio::spawn(http_server); diff --git a/sequencer_core/src/sequecer_store/block_store.rs b/sequencer_core/src/sequecer_store/block_store.rs index 5f668d4..e4e3493 100644 --- a/sequencer_core/src/sequecer_store/block_store.rs +++ b/sequencer_core/src/sequecer_store/block_store.rs @@ -31,6 +31,6 @@ impl SequecerBlockStore { } pub fn put_block_at_id(&self, block: Block) -> Result<()> { - Ok(self.dbio.put_block(block)?) + Ok(self.dbio.put_block(block, false)?) } } diff --git a/storage/Cargo.toml b/storage/Cargo.toml index 17e4271..cce6d2c 100644 --- a/storage/Cargo.toml +++ b/storage/Cargo.toml @@ -11,6 +11,7 @@ log.workspace = true serde.workspace = true lru.workspace = true thiserror.workspace = true +elliptic-curve.workspace = true rocksdb.workspace = true rs_merkle.workspace = true diff --git a/storage/src/block.rs b/storage/src/block.rs index 8de616a..7757667 100644 --- a/storage/src/block.rs +++ b/storage/src/block.rs @@ -7,7 +7,7 @@ pub type BlockHash = [u8; 32]; pub type Data = Vec; pub type BlockId = u64; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Block { pub block_id: BlockId, pub prev_block_id: BlockId, diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 2e87492..47b4397 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -78,9 +78,12 @@ impl RocksDBIO { if is_start_set { Ok(dbio) } else if let Some(block) = start_block { + let block_id = block.block_id; dbio.put_meta_first_block_in_db(block)?; dbio.put_meta_is_first_block_set()?; + dbio.put_meta_last_block_in_db(block_id)?; + Ok(dbio) } else { warn!("Starting db in unset mode, will have to set starting block manually"); @@ -89,6 +92,20 @@ impl RocksDBIO { } } + pub fn destroy(path: &Path) -> DbResult<()> { + let mut cf_opts = Options::default(); + cf_opts.set_max_write_buffer_number(16); + //ToDo: Add more column families for different data + let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone()); + let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone()); + + let mut db_opts = Options::default(); + db_opts.create_missing_column_families(true); + db_opts.create_if_missing(true); + DBWithThreadMode::::destroy(&db_opts, path) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None)) + } + pub fn meta_column(&self) -> Arc { self.db.cf_handle(CF_META_NAME).unwrap() } @@ -149,7 +166,7 @@ impl RocksDBIO { ) .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; - self.put_block(block)?; + self.put_block(block, true)?; Ok(()) } @@ -173,13 +190,15 @@ impl RocksDBIO { Ok(()) } - pub fn put_block(&self, block: Block) -> DbResult<()> { + pub fn put_block(&self, block: Block, first: bool) -> DbResult<()> { let cf_block = self.block_column(); - let last_curr_block = self.get_meta_last_block_in_db()?; + if !first { + let last_curr_block = self.get_meta_last_block_in_db()?; - if block.block_id > last_curr_block { - self.put_meta_last_block_in_db(block.block_id)?; + if block.block_id > last_curr_block { + self.put_meta_last_block_in_db(block.block_id)?; + } } self.db diff --git a/storage/src/transaction.rs b/storage/src/transaction.rs index 3c1cf37..35dd9f5 100644 --- a/storage/src/transaction.rs +++ b/storage/src/transaction.rs @@ -1,7 +1,17 @@ use serde::{Deserialize, Serialize}; +use sha2::{digest::FixedOutput, Digest}; use crate::merkle_tree_public::TreeHashType; +use elliptic_curve::{ + consts::{B0, B1}, + generic_array::GenericArray, +}; +use sha2::digest::typenum::{UInt, UTerm}; + +pub type CipherText = Vec; +pub type Nonce = GenericArray, B1>, B0>, B0>>; + #[derive(Debug, Serialize, Deserialize, Clone, Copy)] pub enum TxKind { Public, @@ -19,10 +29,63 @@ pub struct Transaction { pub execution_input: Vec, ///Tx output data (public_part) pub execution_output: Vec, + ///Tx input utxo commitments + pub utxo_commitments_spent_hashes: Vec, ///Tx output utxo commitments pub utxo_commitments_created_hashes: Vec, ///Tx output nullifiers pub nullifier_created_hashes: Vec, ///Execution proof (private part) pub execution_proof_private: String, + ///Encoded blobs of data + pub encoded_data: Vec<(CipherText, Vec)>, + ///Transaction senders ephemeral pub key + pub ephemeral_pub_key: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +///General transaction object +pub struct TransactionPayload { + pub tx_kind: TxKind, + ///Tx input data (public part) + pub execution_input: Vec, + ///Tx output data (public_part) + pub execution_output: Vec, + ///Tx input utxo commitments + pub utxo_commitments_spent_hashes: Vec, + ///Tx output utxo commitments + pub utxo_commitments_created_hashes: Vec, + ///Tx output nullifiers + pub nullifier_created_hashes: Vec, + ///Execution proof (private part) + pub execution_proof_private: String, + ///Encoded blobs of data + pub encoded_data: Vec<(CipherText, Vec)>, + ///Transaction senders ephemeral pub key + pub ephemeral_pub_key: Vec, +} + +impl From for Transaction { + fn from(value: TransactionPayload) -> Self { + let raw_data = serde_json::to_vec(&value).unwrap(); + + let mut hasher = sha2::Sha256::new(); + + hasher.update(&raw_data); + + let hash = ::from(hasher.finalize_fixed()); + + Self { + hash, + tx_kind: value.tx_kind, + execution_input: value.execution_input, + execution_output: value.execution_output, + utxo_commitments_spent_hashes: value.utxo_commitments_spent_hashes, + utxo_commitments_created_hashes: value.utxo_commitments_created_hashes, + nullifier_created_hashes: value.nullifier_created_hashes, + execution_proof_private: value.execution_proof_private, + encoded_data: value.encoded_data, + ephemeral_pub_key: value.ephemeral_pub_key, + } + } } diff --git a/utxo/src/utxo_core.rs b/utxo/src/utxo_core.rs index 44a3a5d..1d79398 100644 --- a/utxo/src/utxo_core.rs +++ b/utxo/src/utxo_core.rs @@ -18,7 +18,7 @@ pub struct UTXO { pub privacy_flag: bool, } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct UTXOPayload { pub owner: AccountId, pub asset: Asset, diff --git a/zkvm/Cargo.toml b/zkvm/Cargo.toml index 69444e7..5a2139b 100644 --- a/zkvm/Cargo.toml +++ b/zkvm/Cargo.toml @@ -13,6 +13,15 @@ serde.workspace = true risc0-zkvm = { git = "https://github.com/risc0/risc0.git", branch = "release-1.2" } test-methods = { path = "test_methods" } +[dependencies.accounts] +path = "../accounts" + +[dependencies.storage] +path = "../storage" + +[dependencies.utxo] +path = "../utxo" + [features] cuda = ["risc0-zkvm/cuda"] default = [] diff --git a/zkvm/src/lib.rs b/zkvm/src/lib.rs index 14841d3..72dfa0b 100644 --- a/zkvm/src/lib.rs +++ b/zkvm/src/lib.rs @@ -1,4 +1,166 @@ +use accounts::account_core::AccountAddress; use risc0_zkvm::{default_executor, default_prover, sha::Digest, ExecutorEnv, Receipt}; +use utxo::utxo_core::{UTXOPayload, UTXO}; + +pub fn prove_mint_utxo(amount_to_mint: u128, owner: AccountAddress) -> (UTXO, Receipt) { + let mut builder = ExecutorEnv::builder(); + + builder.write(&amount_to_mint).unwrap(); + builder.write(&owner).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::MINT_UTXO_ELF) + .unwrap() + .receipt; + + let digest: UTXOPayload = receipt.journal.decode().unwrap(); + + (UTXO::create_utxo_from_payload(digest), receipt) +} + +pub fn prove_send_utxo( + spent_utxo: UTXO, + owners_parts: Vec<(u128, AccountAddress)>, +) -> (Vec<(UTXO, AccountAddress)>, Receipt) { + let mut builder = ExecutorEnv::builder(); + + builder.write(&spent_utxo).unwrap(); + builder.write(&owners_parts).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::SEND_UTXO_ELF) + .unwrap() + .receipt; + + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + + ( + digest + .into_iter() + .map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr)) + .collect(), + receipt, + ) +} + +pub fn prove_send_utxo_shielded( + owner: AccountAddress, + amount: u128, + owners_parts: Vec<(u128, AccountAddress)>, +) -> (Vec<(UTXO, AccountAddress)>, Receipt) { + let temp_utxo_to_spend = UTXO::create_utxo_from_payload(UTXOPayload { + owner, + asset: vec![], + amount, + privacy_flag: true, + }); + + let mut builder = ExecutorEnv::builder(); + + builder.write(&temp_utxo_to_spend).unwrap(); + builder.write(&owners_parts).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::SEND_UTXO_ELF) + .unwrap() + .receipt; + + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + + ( + digest + .into_iter() + .map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr)) + .collect(), + receipt, + ) +} + +pub fn prove_send_utxo_deshielded( + spent_utxo: UTXO, + owners_parts: Vec<(u128, AccountAddress)>, +) -> (Vec<(u128, AccountAddress)>, Receipt) { + let mut builder = ExecutorEnv::builder(); + + builder.write(&spent_utxo).unwrap(); + builder.write(&owners_parts).unwrap(); + + let env = builder.build().unwrap(); + + let prover = default_prover(); + + let receipt = prover + .prove(env, test_methods::SEND_UTXO_ELF) + .unwrap() + .receipt; + + let digest: Vec<(UTXOPayload, AccountAddress)> = receipt.journal.decode().unwrap(); + + ( + digest + .into_iter() + .map(|(payload, addr)| (payload.amount, addr)) + .collect(), + receipt, + ) +} + +pub fn execute_mint_utxo(amount_to_mint: u128, owner: AccountAddress) -> UTXO { + let mut builder = ExecutorEnv::builder(); + + builder.write(&amount_to_mint).unwrap(); + builder.write(&owner).unwrap(); + + let env = builder.build().unwrap(); + + let executor = default_executor(); + + let receipt = executor.execute(env, test_methods::MINT_UTXO_ELF).unwrap(); + + let digest: UTXOPayload = receipt.journal.decode().unwrap(); + + UTXO::create_utxo_from_payload(digest) +} + +pub fn execute_send_utxo( + spent_utxo: UTXO, + owners_parts: Vec<(u128, AccountAddress)>, +) -> (UTXO, Vec<(UTXO, AccountAddress)>) { + let mut builder = ExecutorEnv::builder(); + + builder.write(&spent_utxo).unwrap(); + builder.write(&owners_parts).unwrap(); + + let env = builder.build().unwrap(); + + let executor = default_executor(); + + let receipt = executor.execute(env, test_methods::SEND_UTXO_ELF).unwrap(); + + let digest: (UTXOPayload, Vec<(UTXOPayload, AccountAddress)>) = + receipt.journal.decode().unwrap(); + + ( + UTXO::create_utxo_from_payload(digest.0), + digest + .1 + .into_iter() + .map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr)) + .collect(), + ) +} pub fn prove(input_vec: Vec, elf: &[u8]) -> (u64, Receipt) { let mut builder = ExecutorEnv::builder(); @@ -48,7 +210,7 @@ pub fn verify(receipt: Receipt, image_id: impl Into) { #[cfg(test)] mod tests { use super::*; - use test_methods::{BIG_CALCULATION_ELF, BIG_CALCULATION_ID}; + use test_methods::BIG_CALCULATION_ELF; use test_methods::{MULTIPLICATION_ELF, MULTIPLICATION_ID}; use test_methods::{SUMMATION_ELF, SUMMATION_ID}; diff --git a/zkvm/test_methods/Cargo.toml b/zkvm/test_methods/Cargo.toml index 5ac19b1..36ab3f9 100644 --- a/zkvm/test_methods/Cargo.toml +++ b/zkvm/test_methods/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [build-dependencies] -risc0-build = { git = "https://github.com/risc0/risc0.git", branch = "release-1.1" } +risc0-build = { git = "https://github.com/risc0/risc0.git", branch = "release-1.2" } [package.metadata.risc0] methods = ["guest"] diff --git a/zkvm/test_methods/guest/Cargo.lock b/zkvm/test_methods/guest/Cargo.lock index 0fbb3a9..20a7a7a 100644 --- a/zkvm/test_methods/guest/Cargo.lock +++ b/zkvm/test_methods/guest/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ahash" @@ -490,6 +490,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + [[package]] name = "libc" version = "0.2.162" @@ -805,6 +811,12 @@ dependencies = [ "semver", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "semver" version = "1.0.23" @@ -831,6 +843,18 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.8" @@ -885,6 +909,8 @@ name = "test" version = "0.1.0" dependencies = [ "risc0-zkvm", + "serde", + "serde_json", ] [[package]] diff --git a/zkvm/test_methods/guest/Cargo.toml b/zkvm/test_methods/guest/Cargo.toml index efc0456..ea2c331 100644 --- a/zkvm/test_methods/guest/Cargo.toml +++ b/zkvm/test_methods/guest/Cargo.toml @@ -6,6 +6,11 @@ edition = "2021" [workspace] [dependencies] +serde_json = "1.0.81" risc0-zkvm = { git = "https://github.com/risc0/risc0.git", default-features = false, features = [ "std", ] } + +[dependencies.serde] +features = ["derive"] +version = "1.0.60" diff --git a/zkvm/test_methods/guest/src/bin/mint_utxo.rs b/zkvm/test_methods/guest/src/bin/mint_utxo.rs new file mode 100644 index 0000000..ad51311 --- /dev/null +++ b/zkvm/test_methods/guest/src/bin/mint_utxo.rs @@ -0,0 +1,29 @@ +use risc0_zkvm::{ + guest::env, +}; +use serde::{Deserialize, Serialize}; + +type AccountAddr = [u8; 32]; + +#[derive(Serialize, Deserialize)] +pub struct UTXOPayload { + pub owner: AccountAddr, + pub asset: Vec, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, +} + +fn main() { + let amount_to_mint: u128 = env::read(); + let owner: AccountAddr = env::read(); + + let payload = UTXOPayload { + owner, + asset: vec![], + amount: amount_to_mint, + privacy_flag: true, + }; + + env::commit(&(payload)); +} diff --git a/zkvm/test_methods/guest/src/bin/send_utxo.rs b/zkvm/test_methods/guest/src/bin/send_utxo.rs new file mode 100644 index 0000000..d05995c --- /dev/null +++ b/zkvm/test_methods/guest/src/bin/send_utxo.rs @@ -0,0 +1,32 @@ +use risc0_zkvm::{ + guest::env, +}; +use serde::{Deserialize, Serialize}; + +type AccountAddr = [u8; 32]; + +#[derive(Serialize, Deserialize)] +pub struct UTXOPayload { + pub owner: AccountAddr, + pub asset: Vec, + // TODO: change to u256 + pub amount: u128, + pub privacy_flag: bool, +} + +fn main() { + let utxo_spent: UTXOPayload = env::read(); + let owners_parts: Vec<(u128, AccountAddr)> = env::read(); + + let res: Vec<(UTXOPayload, AccountAddr)> = owners_parts.into_iter().map(|(amount, addr)| ( + UTXOPayload { + owner: addr.clone(), + asset: vec![], + amount, + privacy_flag: true, + }, + addr + )).collect(); + + env::commit(&(res)); +}