diff --git a/Cargo.lock b/Cargo.lock index b1d2f8a..ea0a22e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ "serde", "serde_json", "sha2", + "tiny-keccak", "utxo", ] diff --git a/accounts/Cargo.toml b/accounts/Cargo.toml index 682d8ac..ffd500a 100644 --- a/accounts/Cargo.toml +++ b/accounts/Cargo.toml @@ -16,6 +16,7 @@ elliptic-curve.workspace = true hex.workspace = true aes-gcm.workspace = true lazy_static.workspace = true +tiny-keccak.workspace = true [dependencies.utxo] path = "../utxo" diff --git a/accounts/src/account_core/mod.rs b/accounts/src/account_core/mod.rs index b1b4a91..e698b49 100644 --- a/accounts/src/account_core/mod.rs +++ b/accounts/src/account_core/mod.rs @@ -16,7 +16,7 @@ use crate::key_management::{ pub type PublicKey = AffinePoint; pub type AccountAddress = TreeHashType; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Account { pub key_holder: AddressKeyHolder, pub address: AccountAddress, @@ -24,7 +24,7 @@ pub struct Account { pub utxos: HashMap, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct AccountForSerialization { pub key_holder: AddressKeyHolder, pub address: AccountAddress, diff --git a/accounts/src/key_management/mod.rs b/accounts/src/key_management/mod.rs index 6fa1462..a5a3e4b 100644 --- a/accounts/src/key_management/mod.rs +++ b/accounts/src/key_management/mod.rs @@ -7,6 +7,7 @@ use log::info; use rand::{rngs::OsRng, RngCore}; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use serde::{Deserialize, Serialize}; +use tiny_keccak::{Hasher, Keccak}; use crate::account_core::PublicKey; pub type PublicAccountSigningKey = [u8; 32]; @@ -15,7 +16,7 @@ pub mod constants_types; pub mod ephemeral_key_holder; pub mod secret_holders; -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] ///Entrypoint to key management pub struct AddressKeyHolder { //Will be useful in future @@ -37,7 +38,6 @@ impl AddressKeyHolder { let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); - let address = utxo_secret_key_holder.generate_address(); let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); @@ -47,6 +47,17 @@ impl AddressKeyHolder { bytes }; + //Address is a Keccak(verification_key) + let field_bytes = FieldBytes::from_slice(&pub_account_signing_key); + let signing_key = SigningKey::from_bytes(field_bytes).unwrap(); + + let verifying_key = signing_key.verifying_key(); + + let mut address = [0; 32]; + let mut keccak_hasher = Keccak::v256(); + keccak_hasher.update(&verifying_key.to_sec1_bytes()); + keccak_hasher.finalize(&mut address); + Self { top_secret_key_holder, utxo_secret_key_holder, @@ -332,6 +343,21 @@ mod tests { ); } + #[test] + fn test_address_key_equal_keccak_pub_sign_key() { + let address_key_holder = AddressKeyHolder::new_os_random(); + let signing_key = address_key_holder.get_pub_account_signing_key(); + + let verifying_key = signing_key.verifying_key(); + + let mut address = [0; 32]; + let mut keccak_hasher = Keccak::v256(); + keccak_hasher.update(&verifying_key.to_sec1_bytes()); + keccak_hasher.finalize(&mut address); + + assert_eq!(address, address_key_holder.address); + } + #[test] fn key_generation_test() { let seed_holder = SeedHolder::new_os_random(); @@ -339,10 +365,26 @@ mod tests { let utxo_secret_key_holder = top_secret_key_holder.produce_utxo_secret_holder(); - let address = utxo_secret_key_holder.generate_address(); let nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); + let pub_account_signing_key = { + let mut bytes = [0; 32]; + OsRng.fill_bytes(&mut bytes); + bytes + }; + + //Address is a Keccak(verification_key) + let field_bytes = FieldBytes::from_slice(&pub_account_signing_key); + let signing_key = SigningKey::from_bytes(field_bytes).unwrap(); + + let verifying_key = signing_key.verifying_key(); + + let mut address = [0; 32]; + let mut keccak_hasher = Keccak::v256(); + keccak_hasher.update(&verifying_key.to_sec1_bytes()); + keccak_hasher.finalize(&mut address); + println!("======Prerequisites======"); println!(); diff --git a/accounts/src/key_management/secret_holders.rs b/accounts/src/key_management/secret_holders.rs index 45fd792..47342ec 100644 --- a/accounts/src/key_management/secret_holders.rs +++ b/accounts/src/key_management/secret_holders.rs @@ -98,16 +98,4 @@ impl UTXOSecretKeyHolder { pub fn generate_viewing_public_key(&self) -> AffinePoint { (AffinePoint::GENERATOR * self.viewing_secret_key).into() } - - pub fn generate_address(&self) -> TreeHashType { - let npk = self.generate_nullifier_public_key(); - let vpk = self.generate_viewing_public_key(); - - let mut hasher = sha2::Sha256::new(); - - hasher.update(serde_json::to_vec(&npk).unwrap()); - hasher.update(serde_json::to_vec(&vpk).unwrap()); - - ::from(hasher.finalize_fixed()) - } } diff --git a/node_core/src/chain_storage/mod.rs b/node_core/src/chain_storage/mod.rs index 126574f..985014b 100644 --- a/node_core/src/chain_storage/mod.rs +++ b/node_core/src/chain_storage/mod.rs @@ -304,6 +304,248 @@ mod tests { use std::path::PathBuf; use tempfile::tempdir; + fn create_initial_accounts() -> Vec { + let initial_acc1 = serde_json::from_str(r#"{ + "address": [ + 244, + 55, + 238, + 205, + 74, + 115, + 179, + 192, + 65, + 186, + 166, + 169, + 221, + 45, + 6, + 57, + 200, + 65, + 195, + 70, + 118, + 252, + 206, + 100, + 215, + 250, + 72, + 230, + 19, + 71, + 217, + 249 + ], + "balance": 100, + "key_holder": { + "address": [ + 244, + 55, + 238, + 205, + 74, + 115, + 179, + 192, + 65, + 186, + 166, + 169, + 221, + 45, + 6, + 57, + 200, + 65, + 195, + 70, + 118, + 252, + 206, + 100, + 215, + 250, + 72, + 230, + 19, + 71, + 217, + 249 + ], + "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", + "pub_account_signing_key": [ + 244, + 88, + 134, + 61, + 35, + 209, + 229, + 101, + 85, + 35, + 140, + 140, + 192, + 226, + 83, + 83, + 190, + 189, + 110, + 8, + 89, + 127, + 147, + 142, + 157, + 204, + 51, + 109, + 189, + 92, + 144, + 68 + ], + "top_secret_key_holder": { + "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" + }, + "utxo_secret_key_holder": { + "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", + "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" + }, + "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" + }, + "utxos": {} + }"#).unwrap(); + + let initial_acc2 = serde_json::from_str(r#"{ + "address": [ + 72, + 169, + 70, + 237, + 1, + 96, + 35, + 157, + 25, + 15, + 83, + 18, + 52, + 206, + 202, + 63, + 48, + 59, + 173, + 76, + 78, + 7, + 254, + 229, + 28, + 45, + 194, + 79, + 6, + 89, + 58, + 85 + ], + "balance": 200, + "key_holder": { + "address": [ + 72, + 169, + 70, + 237, + 1, + 96, + 35, + 157, + 25, + 15, + 83, + 18, + 52, + 206, + 202, + 63, + 48, + 59, + 173, + 76, + 78, + 7, + 254, + 229, + 28, + 45, + 194, + 79, + 6, + 89, + 58, + 85 + ], + "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", + "pub_account_signing_key": [ + 136, + 105, + 9, + 53, + 180, + 145, + 64, + 5, + 235, + 174, + 62, + 211, + 206, + 116, + 185, + 24, + 214, + 62, + 244, + 64, + 224, + 59, + 120, + 150, + 30, + 249, + 160, + 46, + 189, + 254, + 47, + 244 + ], + "top_secret_key_holder": { + "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" + }, + "utxo_secret_key_holder": { + "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", + "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" + }, + "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" + }, + "utxos": {} + }"#).unwrap(); + + let initial_accounts = vec![initial_acc1, initial_acc2]; + + initial_accounts + } + fn create_genesis_block() -> Block { Block { block_id: 0, @@ -361,6 +603,7 @@ mod tests { port: 8000, gas_config: create_sample_gas_config(), shapshot_frequency_in_blocks: 1, + initial_accounts: create_initial_accounts(), } } diff --git a/node_core/src/config.rs b/node_core/src/config.rs index 935a803..e2ddf83 100644 --- a/node_core/src/config.rs +++ b/node_core/src/config.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use accounts::account_core::Account; use serde::{Deserialize, Serialize}; use zkvm::gas_calculator::GasCalculator; @@ -51,4 +52,6 @@ pub struct NodeConfig { pub gas_config: GasConfig, ///Frequency of snapshots pub shapshot_frequency_in_blocks: u64, + ///Initial accounts for wallet + pub initial_accounts: Vec, } diff --git a/node_runner/configs/debug/node_config.json b/node_runner/configs/debug/node_config.json index c948b82..6c39f15 100644 --- a/node_runner/configs/debug/node_config.json +++ b/node_runner/configs/debug/node_config.json @@ -13,5 +13,241 @@ "gas_limit_deploy": 30000000, "gas_limit_runtime": 30000000 }, - "shapshot_frequency_in_blocks": 10 + "shapshot_frequency_in_blocks": 10, + "initial_accounts": [ + { + "address": [ + 13, + 150, + 223, + 204, + 65, + 64, + 25, + 56, + 12, + 157, + 222, + 12, + 211, + 220, + 229, + 170, + 201, + 15, + 181, + 68, + 59, + 248, + 113, + 16, + 135, + 65, + 174, + 175, + 222, + 85, + 42, + 215 + ], + "balance": 10000, + "key_holder": { + "address": [ + 13, + 150, + 223, + 204, + 65, + 64, + 25, + 56, + 12, + 157, + 222, + 12, + 211, + 220, + 229, + 170, + 201, + 15, + 181, + 68, + 59, + 248, + 113, + 16, + 135, + 65, + 174, + 175, + 222, + 85, + 42, + 215 + ], + "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", + "pub_account_signing_key": [ + 133, + 143, + 177, + 187, + 252, + 66, + 237, + 236, + 234, + 252, + 244, + 138, + 5, + 151, + 3, + 99, + 217, + 231, + 112, + 217, + 77, + 211, + 58, + 218, + 176, + 68, + 99, + 53, + 152, + 228, + 198, + 190 + ], + "top_secret_key_holder": { + "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" + }, + "utxo_secret_key_holder": { + "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", + "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" + }, + "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" + }, + "utxos": {} + }, + { + "address": [ + 151, + 72, + 112, + 233, + 190, + 141, + 10, + 192, + 138, + 168, + 59, + 63, + 199, + 167, + 166, + 134, + 41, + 29, + 135, + 50, + 80, + 138, + 186, + 152, + 179, + 96, + 128, + 243, + 156, + 44, + 243, + 100 + ], + "balance": 20000, + "key_holder": { + "address": [ + 151, + 72, + 112, + 233, + 190, + 141, + 10, + 192, + 138, + 168, + 59, + 63, + 199, + 167, + 166, + 134, + 41, + 29, + 135, + 50, + 80, + 138, + 186, + 152, + 179, + 96, + 128, + 243, + 156, + 44, + 243, + 100 + ], + "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", + "pub_account_signing_key": [ + 54, + 90, + 62, + 225, + 71, + 225, + 228, + 148, + 143, + 53, + 210, + 23, + 137, + 158, + 171, + 156, + 48, + 7, + 139, + 52, + 117, + 242, + 214, + 7, + 99, + 29, + 122, + 184, + 59, + 116, + 144, + 107 + ], + "top_secret_key_holder": { + "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" + }, + "utxo_secret_key_holder": { + "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", + "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" + }, + "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" + }, + "utxos": {} + } + ] } \ No newline at end of file diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index aa40351..2dda687 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -1,7 +1,14 @@ -use accounts::account_core::AccountForSerialization; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +#[derive(Debug, Serialize, Deserialize, Clone)] +///Helperstruct for account serialization +pub struct AccountInitialData { + ///Hex encoded `AccountAddress` + pub addr: String, + pub balance: u64, +} + #[derive(Clone, Serialize, Deserialize)] pub struct SequencerConfig { ///Home dir of sequencer storage @@ -19,5 +26,5 @@ pub struct SequencerConfig { ///Port to listen pub port: u16, ///List of initial accounts data - pub initial_accounts: Vec, + pub initial_accounts: Vec, } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 87075d6..f827501 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -28,7 +28,7 @@ pub struct SequencerCore { pub chain_height: u64, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum TransactionMalformationErrorKind { PublicTransactionChangedPrivateData { tx: TreeHashType }, PrivateTransactionChangedPublicData { tx: TreeHashType }, @@ -142,7 +142,7 @@ impl SequencerCore { _ => {} }; - //Correct sender check + //Native transfers checks if let Ok(native_transfer_action) = serde_json::from_slice::(execution_input) { @@ -151,9 +151,20 @@ impl SequencerCore { keccak_hasher.update(&tx.transaction().public_key.to_sec1_bytes()); keccak_hasher.finalize(&mut output); + //Correct sender check if native_transfer_action.from != output { return Err(TransactionMalformationErrorKind::IncorrectSender); } + + let from_balance = self + .store + .acc_store + .get_account_balance(&native_transfer_action.from); + + //Balance check + if from_balance < native_transfer_action.balance_to_move { + return Err(TransactionMalformationErrorKind::BalanceMismatch { tx: tx_hash }); + } } //Tree checks @@ -226,9 +237,7 @@ impl SequencerCore { .. } = mempool_tx.auth_tx.transaction().body(); - let tx_hash = *mempool_tx.auth_tx.hash(); - - //Balance check + //Balance move if let Ok(native_transfer_action) = serde_json::from_slice::(execution_input) { @@ -241,18 +250,14 @@ impl SequencerCore { .acc_store .get_account_balance(&native_transfer_action.to); - if from_balance >= native_transfer_action.balance_to_move { - self.store.acc_store.set_account_balance( - &native_transfer_action.from, - from_balance - native_transfer_action.balance_to_move, - ); - self.store.acc_store.set_account_balance( - &native_transfer_action.to, - to_balance + native_transfer_action.balance_to_move, - ); - } else { - return Err(TransactionMalformationErrorKind::BalanceMismatch { tx: tx_hash }); - } + self.store.acc_store.set_account_balance( + &native_transfer_action.from, + from_balance - native_transfer_action.balance_to_move, + ); + self.store.acc_store.set_account_balance( + &native_transfer_action.to, + to_balance + native_transfer_action.balance_to_move, + ); } for utxo_comm in utxo_commitments_created_hashes { @@ -319,17 +324,19 @@ impl SequencerCore { #[cfg(test)] mod tests { + use crate::config::AccountInitialData; + use super::*; use std::path::PathBuf; - use accounts::account_core::AccountForSerialization; use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; + use k256::{ecdsa::SigningKey, FieldBytes}; use mempool_transaction::MempoolTransaction; use rand::Rng; use secp256k1_zkp::Tweak; fn setup_sequencer_config_variable_initial_accounts( - initial_accounts: Vec, + initial_accounts: Vec, ) -> SequencerConfig { let mut rng = rand::thread_rng(); let random_u8: u8 = rng.gen(); @@ -349,241 +356,25 @@ mod tests { } fn setup_sequencer_config() -> SequencerConfig { - let initial_acc1 = serde_json::from_str(r#"{ - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "balance": 10, - "key_holder": { - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": [ - 244, - 88, - 134, - 61, - 35, - 209, - 229, - 101, - 85, - 35, - 140, - 140, - 192, - 226, - 83, - 83, - 190, - 189, - 110, - 8, - 89, - 127, - 147, - 142, - 157, - 204, - 51, - 109, - 189, - 92, - 144, - 68 - ], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} - }"#).unwrap(); + let acc1_addr = vec![ + 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, + 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + ]; - let initial_acc2 = serde_json::from_str(r#"{ - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "balance": 100, - "key_holder": { - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": [ - 136, - 105, - 9, - 53, - 180, - 145, - 64, - 5, - 235, - 174, - 62, - 211, - 206, - 116, - 185, - 24, - 214, - 62, - 244, - 64, - 224, - 59, - 120, - 150, - 30, - 249, - 160, - 46, - 189, - 254, - 47, - 244 - ], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} - }"#).unwrap(); + let acc2_addr = vec![ + 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, + 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + ]; + + let initial_acc1 = AccountInitialData { + addr: hex::encode(acc1_addr), + balance: 10000, + }; + + let initial_acc2 = AccountInitialData { + addr: hex::encode(acc2_addr), + balance: 20000, + }; let initial_accounts = vec![initial_acc1, initial_acc2]; @@ -616,6 +407,59 @@ mod tests { Transaction::new(body, SignaturePrivateKey::random(&mut rng)) } + fn create_dummy_transaction_native_token_transfer( + from: [u8; 32], + to: [u8; 32], + balance_to_move: u64, + signing_key: SigningKey, + ) -> Transaction { + let mut rng = rand::thread_rng(); + + let native_token_transfer = PublicNativeTokenSend { + from, + to, + balance_to_move, + }; + + let body = TransactionBody { + tx_kind: TxKind::Public, + execution_input: serde_json::to_vec(&native_token_transfer).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![10, 11, 12], + commitment: vec![], + tweak: Tweak::new(&mut rng), + secret_r: [0; 32], + sc_addr: "sc_addr".to_string(), + state_changes: (serde_json::Value::Null, 0), + }; + Transaction::new(body, signing_key) + } + + fn create_signing_key_for_account1() -> SigningKey { + let pub_sign_key_acc1 = [ + 133, 143, 177, 187, 252, 66, 237, 236, 234, 252, 244, 138, 5, 151, 3, 99, 217, 231, + 112, 217, 77, 211, 58, 218, 176, 68, 99, 53, 152, 228, 198, 190, + ]; + + let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc1); + SigningKey::from_bytes(field_bytes).unwrap() + } + + fn create_signing_key_for_account2() -> SigningKey { + let pub_sign_key_acc2 = [ + 54, 90, 62, 225, 71, 225, 228, 148, 143, 53, 210, 23, 137, 158, 171, 156, 48, 7, 139, + 52, 117, 242, 214, 7, 99, 29, 122, 184, 59, 116, 144, 107, + ]; + + let field_bytes = FieldBytes::from_slice(&pub_sign_key_acc2); + SigningKey::from_bytes(field_bytes).unwrap() + } + fn common_setup(sequencer: &mut SequencerCore) { let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]); let mempool_tx = MempoolTransaction { @@ -637,259 +481,49 @@ mod tests { assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10); assert_eq!(sequencer.sequencer_config.port, 8080); - let acc1_addr = config.initial_accounts[0].address; - let acc2_addr = config.initial_accounts[1].address; + let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); assert_eq!( - 10, + 10000, sequencer.store.acc_store.get_account_balance(&acc1_addr) ); assert_eq!( - 100, + 20000, sequencer.store.acc_store.get_account_balance(&acc2_addr) ); } #[test] fn test_start_different_intial_accounts_balances() { - let initial_acc1 = serde_json::from_str(r#"{ - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "balance": 1000, - "key_holder": { - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": [ - 244, - 88, - 134, - 61, - 35, - 209, - 229, - 101, - 85, - 35, - 140, - 140, - 192, - 226, - 83, - 83, - 190, - 189, - 110, - 8, - 89, - 127, - 147, - 142, - 157, - 204, - 51, - 109, - 189, - 92, - 144, - 68 - ], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} - }"#).unwrap(); + let acc1_addr = vec![ + 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, + 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 42, 42, 42, + ]; - let initial_acc2 = serde_json::from_str(r#"{ - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "balance": 1000, - "key_holder": { - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": [ - 136, - 105, - 9, - 53, - 180, - 145, - 64, - 5, - 235, - 174, - 62, - 211, - 206, - 116, - 185, - 24, - 214, - 62, - 244, - 64, - 224, - 59, - 120, - 150, - 30, - 249, - 160, - 46, - 189, - 254, - 47, - 244 - ], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} - }"#).unwrap(); + let acc2_addr = vec![ + 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, + 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 42, 42, 42, + ]; + + let initial_acc1 = AccountInitialData { + addr: hex::encode(acc1_addr), + balance: 10000, + }; + + let initial_acc2 = AccountInitialData { + addr: hex::encode(acc2_addr), + balance: 20000, + }; let initial_accounts = vec![initial_acc1, initial_acc2]; @@ -898,8 +532,14 @@ mod tests { let config = setup_sequencer_config_variable_initial_accounts(initial_accounts); let sequencer = SequencerCore::start_from_config(config.clone()); - let acc1_addr = config.initial_accounts[0].address; - let acc2_addr = config.initial_accounts[1].address; + let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); @@ -907,11 +547,11 @@ mod tests { assert_eq!(sequencer.store.acc_store.len(), intial_accounts_len); assert_eq!( - 1000, + 10000, sequencer.store.acc_store.get_account_balance(&acc1_addr) ); assert_eq!( - 1000, + 20000, sequencer.store.acc_store.get_account_balance(&acc2_addr) ); } @@ -941,6 +581,120 @@ mod tests { assert!(result.is_ok()); } + #[test] + fn test_transaction_pre_check_native_transfer_valid() { + let config = setup_sequencer_config(); + let mut sequencer = SequencerCore::start_from_config(config); + + common_setup(&mut sequencer); + + let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + + let sign_key1 = create_signing_key_for_account1(); + + let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10, sign_key1); + let tx_roots = sequencer.get_tree_roots(); + let result = sequencer.transaction_pre_check(tx, tx_roots); + + assert!(result.is_ok()); + } + + #[test] + fn test_transaction_pre_check_native_transfer_other_signature() { + let config = setup_sequencer_config(); + let mut sequencer = SequencerCore::start_from_config(config); + + common_setup(&mut sequencer); + + let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + + let sign_key2 = create_signing_key_for_account2(); + + let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10, sign_key2); + let tx_roots = sequencer.get_tree_roots(); + let result = sequencer.transaction_pre_check(tx, tx_roots); + + assert_eq!( + result.err().unwrap(), + TransactionMalformationErrorKind::IncorrectSender + ); + } + + #[test] + fn test_transaction_pre_check_native_transfer_sent_too_much() { + let config = setup_sequencer_config(); + let mut sequencer = SequencerCore::start_from_config(config); + + common_setup(&mut sequencer); + + let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + + let sign_key1 = create_signing_key_for_account1(); + + let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 10000000, sign_key1); + let tx_roots = sequencer.get_tree_roots(); + let result = sequencer.transaction_pre_check(tx, tx_roots); + + let is_failed_at_balance_mismatch = matches!( + result.err().unwrap(), + TransactionMalformationErrorKind::BalanceMismatch { tx: _ } + ); + + assert!(is_failed_at_balance_mismatch); + } + + #[test] + fn test_transaction_execute_native_transfer() { + let config = setup_sequencer_config(); + let mut sequencer = SequencerCore::start_from_config(config); + + common_setup(&mut sequencer); + + let acc1 = hex::decode(sequencer.sequencer_config.initial_accounts[0].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + let acc2 = hex::decode(sequencer.sequencer_config.initial_accounts[1].addr.clone()) + .unwrap() + .try_into() + .unwrap(); + + let sign_key1 = create_signing_key_for_account1(); + + let tx = create_dummy_transaction_native_token_transfer(acc1, acc2, 100, sign_key1); + + sequencer + .execute_check_transaction_on_state(&tx.into_authenticated().unwrap().into()) + .unwrap(); + + let bal_from = sequencer.store.acc_store.get_account_balance(&acc1); + let bal_to = sequencer.store.acc_store.get_account_balance(&acc2); + + assert_eq!(bal_from, 9900); + assert_eq!(bal_to, 20100); + } + #[test] fn test_push_tx_into_mempool_fails_mempool_full() { let config = SequencerConfig { diff --git a/sequencer_core/src/sequencer_store/accounts_store.rs b/sequencer_core/src/sequencer_store/accounts_store.rs index 3944d03..8dd2070 100644 --- a/sequencer_core/src/sequencer_store/accounts_store.rs +++ b/sequencer_core/src/sequencer_store/accounts_store.rs @@ -67,18 +67,22 @@ impl SequencerAccountsStore { ///Update `account_addr` balance, /// /// returns 0, if account address not found, otherwise returns previous balance + /// + /// Also, if account was not previously found, sets it with zero balance pub fn set_account_balance(&mut self, account_addr: &AccountAddress, new_balance: u64) -> u64 { let acc_data = self.accounts.get_mut(account_addr); - acc_data - .map(|data| { - let old_balance = data.balance; + if let Some(acc_data) = acc_data { + let old_balance = acc_data.balance; - data.balance = new_balance; + acc_data.balance = new_balance; - old_balance - }) - .unwrap_or(0) + old_balance + } else { + self.register_account(*account_addr); + + 0 + } } ///Remove account from storage @@ -241,4 +245,15 @@ mod tests { assert!(seq_acc_store.is_empty()); } + + #[test] + fn account_sequencer_store_set_balance_to_unknown_account() { + let mut seq_acc_store = SequencerAccountsStore::default(); + + let ret = seq_acc_store.set_account_balance(&[1; 32], 100); + + assert_eq!(ret, 0); + assert!(seq_acc_store.contains_account(&[1; 32])); + assert_eq!(seq_acc_store.get_account_balance(&[1; 32]), 0); + } } diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 3a74fb3..a85fd23 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -1,6 +1,5 @@ use std::{collections::HashSet, path::Path}; -use accounts::account_core::AccountForSerialization; use accounts_store::SequencerAccountsStore; use block_store::SequecerBlockStore; use common::{ @@ -10,6 +9,8 @@ use common::{ }; use rand::{rngs::OsRng, RngCore}; +use crate::config::AccountInitialData; + pub mod accounts_store; pub mod block_store; @@ -26,14 +27,22 @@ impl SequecerChainStore { home_dir: &Path, genesis_id: u64, is_genesis_random: bool, - initial_accounts: &[AccountForSerialization], + initial_accounts: &[AccountInitialData], ) -> Self { - let accs_pregenerated: Vec<_> = initial_accounts + let init_accs: Vec<_> = initial_accounts .iter() - .map(|acc| (acc.address, acc.balance)) + .map(|acc_data| { + ( + hex::decode(acc_data.addr.clone()) + .unwrap() + .try_into() + .unwrap(), + acc_data.balance, + ) + }) .collect(); - let acc_store = SequencerAccountsStore::new(&accs_pregenerated); + let acc_store = SequencerAccountsStore::new(&init_accs); let nullifier_store = HashSet::new(); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 41d2261..d606f4c 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -1,5 +1,5 @@ -use accounts::account_core::AccountForSerialization; use actix_web::Error as HttpError; +use sequencer_core::config::AccountInitialData; use serde_json::Value; use common::{ @@ -149,13 +149,13 @@ impl JsonHandler { let _get_initial_testnet_accounts_request = GetInitialTestnetAccountsRequest::parse(Some(request.params))?; - let accounts_for_serialization: Vec = { + let initial_accounts: Vec = { let state = self.sequencer_state.lock().await; state.sequencer_config.initial_accounts.clone() }; - respond(accounts_for_serialization) + respond(initial_accounts) } /// Returns the balance of the account at the given address. @@ -217,12 +217,14 @@ mod tests { use std::sync::Arc; use crate::{rpc_handler, JsonHandler}; - use accounts::account_core::Account; use common::{ rpc_primitives::RpcPollingConfig, transaction::{SignaturePrivateKey, Transaction, TransactionBody}, }; - use sequencer_core::{config::SequencerConfig, SequencerCore}; + use sequencer_core::{ + config::{AccountInitialData, SequencerConfig}, + SequencerCore, + }; use serde_json::Value; use tempfile::tempdir; use tokio::sync::Mutex; @@ -230,241 +232,25 @@ mod tests { fn sequencer_config_for_tests() -> SequencerConfig { let tempdir = tempdir().unwrap(); let home = tempdir.path().to_path_buf(); - let initial_acc1 = serde_json::from_str(r#"{ - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "balance": 100, - "key_holder": { - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": [ - 244, - 88, - 134, - 61, - 35, - 209, - 229, - 101, - 85, - 35, - 140, - 140, - 192, - 226, - 83, - 83, - 190, - 189, - 110, - 8, - 89, - 127, - 147, - 142, - 157, - 204, - 51, - 109, - 189, - 92, - 144, - 68 - ], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} - }"#).unwrap(); + let acc1_addr = vec![ + 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181, + 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215, + ]; - let initial_acc2 = serde_json::from_str(r#"{ - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "balance": 200, - "key_holder": { - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": [ - 136, - 105, - 9, - 53, - 180, - 145, - 64, - 5, - 235, - 174, - 62, - 211, - 206, - 116, - 185, - 24, - 214, - 62, - 244, - 64, - 224, - 59, - 120, - 150, - 30, - 249, - 160, - 46, - 189, - 254, - 47, - 244 - ], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} - }"#).unwrap(); + let acc2_addr = vec![ + 151, 72, 112, 233, 190, 141, 10, 192, 138, 168, 59, 63, 199, 167, 166, 134, 41, 29, + 135, 50, 80, 138, 186, 152, 179, 96, 128, 243, 156, 44, 243, 100, + ]; + + let initial_acc1 = AccountInitialData { + addr: hex::encode(acc1_addr), + balance: 10000, + }; + + let initial_acc2 = AccountInitialData { + addr: hex::encode(acc2_addr), + balance: 20000, + }; let initial_accounts = vec![initial_acc1, initial_acc2]; @@ -480,16 +266,12 @@ mod tests { } } - fn json_handler_for_tests() -> (JsonHandler, Vec) { + fn json_handler_for_tests() -> (JsonHandler, Vec) { let config = sequencer_config_for_tests(); let mut sequencer_core = SequencerCore::start_from_config(config); - let initial_accounts = sequencer_core - .sequencer_config - .initial_accounts - .iter() - .map(|acc_ser| acc_ser.clone().into()) - .collect(); + + let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone(); let tx_body = TransactionBody { tx_kind: common::transaction::TxKind::Public, @@ -620,7 +402,7 @@ mod tests { async fn test_get_account_balance_for_existing_account() { let (json_handler, initial_accounts) = json_handler_for_tests(); - let acc1_addr = hex::encode(initial_accounts[0].address); + let acc1_addr = initial_accounts[0].addr.clone(); let request = serde_json::json!({ "jsonrpc": "2.0", @@ -632,7 +414,7 @@ mod tests { "id": 1, "jsonrpc": "2.0", "result": { - "balance": 100 + "balance": 10000 } }); diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index b707b85..010a9ba 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -7,239 +7,6 @@ "block_create_timeout_millis": 10000, "port": 3040, "initial_accounts": [ - { - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "balance": 100, - "key_holder": { - "address": [ - 244, - 55, - 238, - 205, - 74, - 115, - 179, - 192, - 65, - 186, - 166, - 169, - 221, - 45, - 6, - 57, - 200, - 65, - 195, - 70, - 118, - 252, - 206, - 100, - 215, - 250, - 72, - 230, - 19, - 71, - 217, - 249 - ], - "nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718", - "pub_account_signing_key": [ - 244, - 88, - 134, - 61, - 35, - 209, - 229, - 101, - 85, - 35, - 140, - 140, - 192, - 226, - 83, - 83, - 190, - 189, - 110, - 8, - 89, - 127, - 147, - 142, - 157, - 204, - 51, - 109, - 189, - 92, - 144, - 68 - ], - "top_secret_key_holder": { - "secret_spending_key": "7BC46784DB1BC67825D8F029436846712BFDF9B5D79EA3AB11D39A52B9B229D4" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "BB54A8D3C9C51B82C431082D1845A74677B0EF829A11B517E1D9885DE3139506", - "viewing_secret_key": "AD923E92F6A5683E30140CEAB2702AFB665330C1EE4EFA70FAF29767B6B52BAF" - }, - "viewing_public_key": "0361220C5D277E7A1709340FD31A52600C1432B9C45B9BCF88A43581D58824A8B6" - }, - "utxos": {} - }, - { - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "balance": 1000, - "key_holder": { - "address": [ - 72, - 169, - 70, - 237, - 1, - 96, - 35, - 157, - 25, - 15, - 83, - 18, - 52, - 206, - 202, - 63, - 48, - 59, - 173, - 76, - 78, - 7, - 254, - 229, - 28, - 45, - 194, - 79, - 6, - 89, - 58, - 85 - ], - "nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271", - "pub_account_signing_key": [ - 136, - 105, - 9, - 53, - 180, - 145, - 64, - 5, - 235, - 174, - 62, - 211, - 206, - 116, - 185, - 24, - 214, - 62, - 244, - 64, - 224, - 59, - 120, - 150, - 30, - 249, - 160, - 46, - 189, - 254, - 47, - 244 - ], - "top_secret_key_holder": { - "secret_spending_key": "80A186737C8D38B4288A03F0F589957D9C040D79C19F3E0CC4BA80F8494E5179" - }, - "utxo_secret_key_holder": { - "nullifier_secret_key": "746928E63F0984F6F4818933493CE9C067562D9CB932FDC06D82C86CDF6D7122", - "viewing_secret_key": "89176CF4BC9E673807643FD52110EF99D4894335AFB10D881AC0B5041FE1FCB7" - }, - "viewing_public_key": "026072A8F83FEC3472E30CDD4767683F30B91661D25B1040AD9A5FC2E01D659F99" - }, - "utxos": {} - } + ] } \ No newline at end of file