Merge branch 'main' into Pravdyvy/integration-tests

This commit is contained in:
Oleksandr Pravdyvyi 2025-07-30 10:10:36 +03:00
commit 28317b6227
No known key found for this signature in database
GPG Key ID: 9F8955C63C443871
23 changed files with 1090 additions and 152 deletions

18
Cargo.lock generated
View File

@ -19,6 +19,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"tiny-keccak",
"utxo", "utxo",
] ]
@ -1141,6 +1142,12 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]] [[package]]
name = "crypto-bigint" name = "crypto-bigint"
version = "0.5.5" version = "0.5.5"
@ -4396,12 +4403,14 @@ dependencies = [
"serde_json", "serde_json",
"storage", "storage",
"tempfile", "tempfile",
"tiny-keccak",
] ]
[[package]] [[package]]
name = "sequencer_rpc" name = "sequencer_rpc"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"accounts",
"actix", "actix",
"actix-cors", "actix-cors",
"actix-web", "actix-web",
@ -4883,6 +4892,15 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.1" version = "0.8.1"

View File

@ -46,6 +46,7 @@ tempfile = "3.14.0"
light-poseidon = "0.3.0" light-poseidon = "0.3.0"
ark-bn254 = "0.5.0" ark-bn254 = "0.5.0"
ark-ff = "0.5.0" ark-ff = "0.5.0"
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
rocksdb = { version = "0.21.0", default-features = false, features = [ rocksdb = { version = "0.21.0", default-features = false, features = [
"snappy", "snappy",

View File

@ -16,6 +16,7 @@ elliptic-curve.workspace = true
hex.workspace = true hex.workspace = true
aes-gcm.workspace = true aes-gcm.workspace = true
lazy_static.workspace = true lazy_static.workspace = true
tiny-keccak.workspace = true
[dependencies.utxo] [dependencies.utxo]
path = "../utxo" path = "../utxo"

View File

@ -16,7 +16,7 @@ use crate::key_management::{
pub type PublicKey = AffinePoint; pub type PublicKey = AffinePoint;
pub type AccountAddress = TreeHashType; pub type AccountAddress = TreeHashType;
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Account { pub struct Account {
pub key_holder: AddressKeyHolder, pub key_holder: AddressKeyHolder,
pub address: AccountAddress, pub address: AccountAddress,
@ -24,7 +24,7 @@ pub struct Account {
pub utxos: HashMap<TreeHashType, UTXO>, pub utxos: HashMap<TreeHashType, UTXO>,
} }
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AccountForSerialization { pub struct AccountForSerialization {
pub key_holder: AddressKeyHolder, pub key_holder: AddressKeyHolder,
pub address: AccountAddress, pub address: AccountAddress,

View File

@ -7,6 +7,7 @@ use log::info;
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder}; use secret_holders::{SeedHolder, TopSecretKeyHolder, UTXOSecretKeyHolder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Keccak};
use crate::account_core::PublicKey; use crate::account_core::PublicKey;
pub type PublicAccountSigningKey = [u8; 32]; pub type PublicAccountSigningKey = [u8; 32];
@ -15,7 +16,7 @@ pub mod constants_types;
pub mod ephemeral_key_holder; pub mod ephemeral_key_holder;
pub mod secret_holders; pub mod secret_holders;
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone, Debug)]
///Entrypoint to key management ///Entrypoint to key management
pub struct AddressKeyHolder { pub struct AddressKeyHolder {
//Will be useful in future //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 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 nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key();
let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key(); let viewing_public_key = utxo_secret_key_holder.generate_viewing_public_key();
@ -47,6 +47,17 @@ impl AddressKeyHolder {
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);
Self { Self {
top_secret_key_holder, top_secret_key_holder,
utxo_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] #[test]
fn key_generation_test() { fn key_generation_test() {
let seed_holder = SeedHolder::new_os_random(); 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 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 nullifer_public_key = utxo_secret_key_holder.generate_nullifier_public_key();
let viewing_public_key = utxo_secret_key_holder.generate_viewing_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!("======Prerequisites======");
println!(); println!();

View File

@ -98,16 +98,4 @@ impl UTXOSecretKeyHolder {
pub fn generate_viewing_public_key(&self) -> AffinePoint { pub fn generate_viewing_public_key(&self) -> AffinePoint {
(AffinePoint::GENERATOR * self.viewing_secret_key).into() (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());
<TreeHashType>::from(hasher.finalize_fixed())
}
} }

View File

@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};
use crate::merkle_tree_public::TreeHashType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicNativeTokenSend {
pub from: TreeHashType,
pub to: TreeHashType,
pub balance_to_move: u64,
}

View File

@ -3,6 +3,7 @@ use serde::Deserialize;
pub mod block; pub mod block;
pub mod commitment; pub mod commitment;
pub mod execution_input;
pub mod merkle_tree_public; pub mod merkle_tree_public;
pub mod nullifier; pub mod nullifier;
pub mod rpc_primitives; pub mod rpc_primitives;

View File

@ -34,6 +34,9 @@ pub struct GetGenesisIdRequest {}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetLastBlockRequest {} pub struct GetLastBlockRequest {}
#[derive(Serialize, Deserialize, Debug)]
pub struct GetInitialTestnetAccountsRequest {}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetAccountBalanceRequest { pub struct GetAccountBalanceRequest {
pub address: String, pub address: String,
@ -50,6 +53,7 @@ parse_request!(SendTxRequest);
parse_request!(GetBlockDataRequest); parse_request!(GetBlockDataRequest);
parse_request!(GetGenesisIdRequest); parse_request!(GetGenesisIdRequest);
parse_request!(GetLastBlockRequest); parse_request!(GetLastBlockRequest);
parse_request!(GetInitialTestnetAccountsRequest);
parse_request!(GetAccountBalanceRequest); parse_request!(GetAccountBalanceRequest);
parse_request!(GetTransactionByHashRequest); parse_request!(GetTransactionByHashRequest);

View File

@ -235,8 +235,8 @@ pub type SignaturePrivateKey = SigningKey;
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Transaction { pub struct Transaction {
body: TransactionBody, body: TransactionBody,
signature: TransactionSignature, pub signature: TransactionSignature,
public_key: VerifyingKey, pub public_key: VerifyingKey,
} }
impl Transaction { impl Transaction {

View File

@ -5,6 +5,7 @@ use anyhow::Result;
use block_store::NodeBlockStore; use block_store::NodeBlockStore;
use common::{ use common::{
block::Block, block::Block,
execution_input::PublicNativeTokenSend,
merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree}, merkle_tree_public::merkle_tree::{PublicTransactionMerkleTree, UTXOCommitmentsMerkleTree},
nullifier::UTXONullifier, nullifier::UTXONullifier,
utxo_commitment::UTXOCommitment, utxo_commitment::UTXOCommitment,
@ -158,6 +159,20 @@ impl NodeChainStore {
} }
_ => {} _ => {}
} }
} else {
let native_transfer =
serde_json::from_slice::<PublicNativeTokenSend>(&tx.body().execution_input);
if let Ok(transfer) = native_transfer {
if let Some(acc_sender) = self.acc_map.get_mut(&transfer.from) {
//Can panic, we depend on sequencer maintaining chain consistency here
acc_sender.balance -= transfer.balance_to_move;
if let Some(acc_rec) = self.acc_map.get_mut(&transfer.to) {
acc_rec.balance += transfer.balance_to_move;
}
}
}
} }
} }
@ -289,6 +304,248 @@ mod tests {
use std::path::PathBuf; use std::path::PathBuf;
use tempfile::tempdir; use tempfile::tempdir;
fn create_initial_accounts() -> Vec<Account> {
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 { fn create_genesis_block() -> Block {
Block { Block {
block_id: 0, block_id: 0,
@ -346,6 +603,7 @@ mod tests {
port: 8000, port: 8000,
gas_config: create_sample_gas_config(), gas_config: create_sample_gas_config(),
shapshot_frequency_in_blocks: 1, shapshot_frequency_in_blocks: 1,
initial_accounts: create_initial_accounts(),
} }
} }

View File

@ -1,5 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use accounts::account_core::Account;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use zkvm::gas_calculator::GasCalculator; use zkvm::gas_calculator::GasCalculator;
@ -51,4 +52,6 @@ pub struct NodeConfig {
pub gas_config: GasConfig, pub gas_config: GasConfig,
///Frequency of snapshots ///Frequency of snapshots
pub shapshot_frequency_in_blocks: u64, pub shapshot_frequency_in_blocks: u64,
///Initial accounts for wallet
pub initial_accounts: Vec<Account>,
} }

View File

@ -3,7 +3,9 @@ use std::sync::{
Arc, Arc,
}; };
use common::{transaction::Transaction, ExecutionFailureKind}; use common::{
execution_input::PublicNativeTokenSend, transaction::Transaction, ExecutionFailureKind,
};
use accounts::{ use accounts::{
account_core::{Account, AccountAddress}, account_core::{Account, AccountAddress},
@ -101,7 +103,14 @@ impl NodeCore {
let genesis_block = client.get_block(genesis_id.genesis_id).await?.block; let genesis_block = client.get_block(genesis_id.genesis_id).await?.block;
let initial_accounts_ser = client.get_initial_testnet_accounts().await?;
let initial_accounts: Vec<Account> =
initial_accounts_ser.into_iter().map(Into::into).collect();
let (mut storage, mut chain_height) = NodeChainStore::new(config.clone(), genesis_block)?; let (mut storage, mut chain_height) = NodeChainStore::new(config.clone(), genesis_block)?;
for acc in initial_accounts {
storage.acc_map.insert(acc.address, acc);
}
pre_start::setup_empty_sc_states(&storage).await?; pre_start::setup_empty_sc_states(&storage).await?;
@ -942,6 +951,70 @@ impl NodeCore {
// Ok(self.sequencer_client.send_tx(tx, tx_roots).await?) // Ok(self.sequencer_client.send_tx(tx, tx_roots).await?)
// } // }
// ToDo: Currently untested due to need for end-to-end integration tests.
// Add integration tests to cover this functionality
pub async fn send_public_native_token_transfer(
&self,
from: AccountAddress,
to: AccountAddress,
balance_to_move: u64,
) -> Result<SendTxResponse, ExecutionFailureKind> {
let tx_roots = self.get_roots().await;
let public_context = {
let read_guard = self.storage.read().await;
read_guard.produce_context(from)
};
let (tweak, secret_r, commitment) = pedersen_commitment_vec(
//Will not panic, as public context is serializable
public_context.produce_u64_list_from_context().unwrap(),
);
let sc_addr = hex::encode([0; 32]);
//Native contract does not change its state
let state_changes: Vec<DataBlobChangeVariant> = vec![];
let new_len = 0;
let state_changes = (serde_json::to_value(state_changes).unwrap(), new_len);
let tx: TransactionBody =
sc_core::transaction_payloads_tools::create_public_transaction_payload(
serde_json::to_vec(&PublicNativeTokenSend {
from,
to,
balance_to_move,
})
.unwrap(),
commitment,
tweak,
secret_r,
sc_addr,
state_changes,
);
tx.log();
{
let read_guard = self.storage.read().await;
let account = read_guard.acc_map.get(&from);
if let Some(account) = account {
let key_to_sign_transaction = account.key_holder.get_pub_account_signing_key();
let signed_transaction = Transaction::new(tx, key_to_sign_transaction);
Ok(self
.sequencer_client
.send_tx(signed_transaction, tx_roots)
.await?)
} else {
Err(ExecutionFailureKind::AmountMismatchError)
}
}
}
pub async fn send_private_send_tx( pub async fn send_private_send_tx(
&self, &self,
utxo: UTXO, utxo: UTXO,

View File

@ -1,8 +1,8 @@
use accounts::account_core::Account; use accounts::account_core::{Account, AccountForSerialization};
use anyhow::Result; use anyhow::Result;
use common::rpc_primitives::requests::{ use common::rpc_primitives::requests::{
GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse, GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse,
RegisterAccountRequest, RegisterAccountResponse, GetInitialTestnetAccountsRequest, RegisterAccountRequest, RegisterAccountResponse,
}; };
use common::transaction::Transaction; use common::transaction::Transaction;
use common::{SequencerClientError, SequencerRpcError}; use common::{SequencerClientError, SequencerRpcError};
@ -121,4 +121,21 @@ impl SequencerClient {
Ok(resp_deser) Ok(resp_deser)
} }
pub async fn get_initial_testnet_accounts(
&self,
) -> Result<Vec<AccountForSerialization>, SequencerClientError> {
let acc_req = GetInitialTestnetAccountsRequest {};
let req = serde_json::to_value(acc_req).unwrap();
let resp = self
.call_method_with_payload("get_initial_testnet_accounts", req)
.await
.unwrap();
let resp_deser = serde_json::from_value(resp).unwrap();
Ok(resp_deser)
}
} }

View File

@ -13,5 +13,241 @@
"gas_limit_deploy": 30000000, "gas_limit_deploy": 30000000,
"gas_limit_runtime": 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": {}
}
]
} }

View File

@ -13,6 +13,7 @@ serde.workspace = true
rand.workspace = true rand.workspace = true
elliptic-curve.workspace = true elliptic-curve.workspace = true
k256.workspace = true k256.workspace = true
tiny-keccak.workspace = true
tempfile.workspace = true tempfile.workspace = true
[dependencies.storage] [dependencies.storage]

View File

@ -1,8 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
//
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
///Helperstruct for account serialization ///Helperstruct for account serialization
pub struct AccountInitialData { pub struct AccountInitialData {
@ -11,7 +9,7 @@ pub struct AccountInitialData {
pub balance: u64, pub balance: u64,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
pub struct SequencerConfig { pub struct SequencerConfig {
///Home dir of sequencer storage ///Home dir of sequencer storage
pub home: PathBuf, pub home: PathBuf,
@ -27,6 +25,6 @@ pub struct SequencerConfig {
pub block_create_timeout_millis: u64, pub block_create_timeout_millis: u64,
///Port to listen ///Port to listen
pub port: u16, pub port: u16,
///List of pairs (account_address, initial_balance) ///List of initial accounts data
pub initial_accounts: Vec<AccountInitialData>, pub initial_accounts: Vec<AccountInitialData>,
} }

View File

@ -4,6 +4,7 @@ use accounts::account_core::AccountAddress;
use anyhow::Result; use anyhow::Result;
use common::{ use common::{
block::{Block, HashableBlockData}, block::{Block, HashableBlockData},
execution_input::PublicNativeTokenSend,
merkle_tree_public::TreeHashType, merkle_tree_public::TreeHashType,
nullifier::UTXONullifier, nullifier::UTXONullifier,
transaction::{AuthenticatedTransaction, Transaction, TransactionBody, TxKind}, transaction::{AuthenticatedTransaction, Transaction, TransactionBody, TxKind},
@ -14,6 +15,7 @@ use mempool::MemPool;
use mempool_transaction::MempoolTransaction; use mempool_transaction::MempoolTransaction;
use sequencer_store::SequecerChainStore; use sequencer_store::SequecerChainStore;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Keccak};
pub mod config; pub mod config;
pub mod mempool_transaction; pub mod mempool_transaction;
@ -26,7 +28,7 @@ pub struct SequencerCore {
pub chain_height: u64, pub chain_height: u64,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum TransactionMalformationErrorKind { pub enum TransactionMalformationErrorKind {
PublicTransactionChangedPrivateData { tx: TreeHashType }, PublicTransactionChangedPrivateData { tx: TreeHashType },
PrivateTransactionChangedPublicData { tx: TreeHashType }, PrivateTransactionChangedPublicData { tx: TreeHashType },
@ -37,6 +39,9 @@ pub enum TransactionMalformationErrorKind {
ChainStateFurtherThanTransactionState { tx: TreeHashType }, ChainStateFurtherThanTransactionState { tx: TreeHashType },
FailedToInsert { tx: TreeHashType, details: String }, FailedToInsert { tx: TreeHashType, details: String },
InvalidSignature, InvalidSignature,
IncorrectSender,
BalanceMismatch { tx: TreeHashType },
FailedToDecode { tx: TreeHashType },
} }
impl Display for TransactionMalformationErrorKind { impl Display for TransactionMalformationErrorKind {
@ -137,6 +142,21 @@ impl SequencerCore {
_ => {} _ => {}
}; };
//Native transfers checks
if let Ok(native_transfer_action) =
serde_json::from_slice::<PublicNativeTokenSend>(execution_input)
{
let mut output = [0; 32];
let mut keccak_hasher = Keccak::v256();
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);
}
}
//Tree checks //Tree checks
let tx_tree_check = self.store.pub_tx_store.get_tx(tx_hash).is_some(); let tx_tree_check = self.store.pub_tx_store.get_tx(tx_hash).is_some();
let nullifier_tree_check = nullifier_created_hashes.iter().any(|nullifier_hash| { let nullifier_tree_check = nullifier_created_hashes.iter().any(|nullifier_hash| {
@ -203,9 +223,40 @@ impl SequencerCore {
let TransactionBody { let TransactionBody {
ref utxo_commitments_created_hashes, ref utxo_commitments_created_hashes,
ref nullifier_created_hashes, ref nullifier_created_hashes,
execution_input,
.. ..
} = mempool_tx.auth_tx.transaction().body(); } = mempool_tx.auth_tx.transaction().body();
let tx_hash = *mempool_tx.auth_tx.hash();
//Balance move
if let Ok(native_transfer_action) =
serde_json::from_slice::<PublicNativeTokenSend>(execution_input)
{
let from_balance = self
.store
.acc_store
.get_account_balance(&native_transfer_action.from);
let to_balance = self
.store
.acc_store
.get_account_balance(&native_transfer_action.to);
//Balance check
if from_balance < native_transfer_action.balance_to_move {
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 { for utxo_comm in utxo_commitments_created_hashes {
self.store self.store
.utxo_commitments_store .utxo_commitments_store
@ -276,6 +327,7 @@ mod tests {
use std::path::PathBuf; use std::path::PathBuf;
use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind}; use common::transaction::{SignaturePrivateKey, Transaction, TransactionBody, TxKind};
use k256::{ecdsa::SigningKey, FieldBytes};
use mempool_transaction::MempoolTransaction; use mempool_transaction::MempoolTransaction;
use rand::Rng; use rand::Rng;
use secp256k1_zkp::Tweak; use secp256k1_zkp::Tweak;
@ -301,19 +353,28 @@ mod tests {
} }
fn setup_sequencer_config() -> SequencerConfig { fn setup_sequencer_config() -> SequencerConfig {
let initial_accounts = vec![ let acc1_addr = vec![
AccountInitialData { 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181,
addr: "bfd91e6703273a115ad7f099ef32f621243be69369d00ddef5d3a25117d09a8c" 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215,
.to_string(),
balance: 10,
},
AccountInitialData {
addr: "20573479053979b98d2ad09ef31a0750f22c77709bed51c4e64946bd1e376f31"
.to_string(),
balance: 100,
},
]; ];
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];
setup_sequencer_config_variable_initial_accounts(initial_accounts) setup_sequencer_config_variable_initial_accounts(initial_accounts)
} }
@ -343,6 +404,59 @@ mod tests {
Transaction::new(body, SignaturePrivateKey::random(&mut rng)) 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) { fn common_setup(sequencer: &mut SequencerCore) {
let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]); let tx = create_dummy_transaction(vec![[9; 32]], vec![[7; 32]], vec![[8; 32]]);
let mempool_tx = MempoolTransaction { let mempool_tx = MempoolTransaction {
@ -364,68 +478,65 @@ mod tests {
assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10); assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10);
assert_eq!(sequencer.sequencer_config.port, 8080); assert_eq!(sequencer.sequencer_config.port, 8080);
let acc1_addr: [u8; 32] = let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
hex::decode("bfd91e6703273a115ad7f099ef32f621243be69369d00ddef5d3a25117d09a8c") .unwrap()
.unwrap() .try_into()
.try_into() .unwrap();
.unwrap(); let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
let acc2_addr: [u8; 32] = .unwrap()
hex::decode("20573479053979b98d2ad09ef31a0750f22c77709bed51c4e64946bd1e376f31") .try_into()
.unwrap() .unwrap();
.try_into()
.unwrap();
assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); assert!(sequencer.store.acc_store.contains_account(&acc1_addr));
assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); assert!(sequencer.store.acc_store.contains_account(&acc2_addr));
assert_eq!( assert_eq!(
10, 10000,
sequencer sequencer.store.acc_store.get_account_balance(&acc1_addr)
.store
.acc_store
.get_account_balance(&acc1_addr)
.unwrap()
); );
assert_eq!( assert_eq!(
100, 20000,
sequencer sequencer.store.acc_store.get_account_balance(&acc2_addr)
.store
.acc_store
.get_account_balance(&acc2_addr)
.unwrap()
); );
} }
#[test] #[test]
fn test_start_different_intial_accounts() { fn test_start_different_intial_accounts_balances() {
let initial_accounts = vec![ let acc1_addr = vec![
AccountInitialData { 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181,
addr: "bfd91e6703273a115ad7f099ef32f621243be69369d00ddef5d3a25117ffffff" 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 42, 42, 42,
.to_string(),
balance: 1000,
},
AccountInitialData {
addr: "20573479053979b98d2ad09ef31a0750f22c77709bed51c4e64946bd1effffff"
.to_string(),
balance: 1000,
},
]; ];
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];
let intial_accounts_len = initial_accounts.len(); let intial_accounts_len = initial_accounts.len();
let config = setup_sequencer_config_variable_initial_accounts(initial_accounts); let config = setup_sequencer_config_variable_initial_accounts(initial_accounts);
let sequencer = SequencerCore::start_from_config(config.clone()); let sequencer = SequencerCore::start_from_config(config.clone());
let acc1_addr: [u8; 32] = let acc1_addr = hex::decode(config.initial_accounts[0].addr.clone())
hex::decode("bfd91e6703273a115ad7f099ef32f621243be69369d00ddef5d3a25117ffffff") .unwrap()
.unwrap() .try_into()
.try_into() .unwrap();
.unwrap(); let acc2_addr = hex::decode(config.initial_accounts[1].addr.clone())
let acc2_addr: [u8; 32] = .unwrap()
hex::decode("20573479053979b98d2ad09ef31a0750f22c77709bed51c4e64946bd1effffff") .try_into()
.unwrap() .unwrap();
.try_into()
.unwrap();
assert!(sequencer.store.acc_store.contains_account(&acc1_addr)); assert!(sequencer.store.acc_store.contains_account(&acc1_addr));
assert!(sequencer.store.acc_store.contains_account(&acc2_addr)); assert!(sequencer.store.acc_store.contains_account(&acc2_addr));
@ -433,20 +544,12 @@ mod tests {
assert_eq!(sequencer.store.acc_store.len(), intial_accounts_len); assert_eq!(sequencer.store.acc_store.len(), intial_accounts_len);
assert_eq!( assert_eq!(
1000, 10000,
sequencer sequencer.store.acc_store.get_account_balance(&acc1_addr)
.store
.acc_store
.get_account_balance(&acc1_addr)
.unwrap()
); );
assert_eq!( assert_eq!(
1000, 20000,
sequencer sequencer.store.acc_store.get_account_balance(&acc2_addr)
.store
.acc_store
.get_account_balance(&acc2_addr)
.unwrap()
); );
} }
@ -475,6 +578,124 @@ mod tests {
assert!(result.is_ok()); 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);
//Passed pre-check
assert!(result.is_ok());
let result = sequencer.execute_check_transaction_on_state(&result.unwrap().into());
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] #[test]
fn test_push_tx_into_mempool_fails_mempool_full() { fn test_push_tx_into_mempool_fails_mempool_full() {
let config = SequencerConfig { let config = SequencerConfig {

View File

@ -56,9 +56,33 @@ impl SequencerAccountsStore {
///Check `account_addr` balance, ///Check `account_addr` balance,
/// ///
///returns `None`, if account address not found ///returns 0, if account address not found
pub fn get_account_balance(&self, account_addr: &AccountAddress) -> Option<u64> { pub fn get_account_balance(&self, account_addr: &AccountAddress) -> u64 {
self.accounts.get(account_addr).map(|acc| acc.balance) self.accounts
.get(account_addr)
.map(|acc| acc.balance)
.unwrap_or(0)
}
///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);
if let Some(acc_data) = acc_data {
let old_balance = acc_data.balance;
acc_data.balance = new_balance;
old_balance
} else {
self.register_account(*account_addr);
0
}
} }
///Remove account from storage ///Remove account from storage
@ -70,14 +94,10 @@ impl SequencerAccountsStore {
&mut self, &mut self,
account_addr: AccountAddress, account_addr: AccountAddress,
) -> Result<Option<AccountAddress>> { ) -> Result<Option<AccountAddress>> {
if let Some(account_balance) = self.get_account_balance(&account_addr) { if self.get_account_balance(&account_addr) == 0 {
if account_balance == 0 { Ok(self.accounts.remove(&account_addr).map(|data| data.address))
Ok(self.accounts.remove(&account_addr).map(|data| data.address))
} else {
anyhow::bail!("Chain consistency violation: It is forbidden to remove account with nonzero balance");
}
} else { } else {
Ok(None) anyhow::bail!("Chain consistency violation: It is forbidden to remove account with nonzero balance");
} }
} }
@ -133,7 +153,7 @@ mod tests {
assert!(seq_acc_store.contains_account(&[1; 32])); assert!(seq_acc_store.contains_account(&[1; 32]));
let acc_balance = seq_acc_store.get_account_balance(&[1; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[1; 32]);
assert_eq!(acc_balance, 0); assert_eq!(acc_balance, 0);
} }
@ -178,11 +198,11 @@ mod tests {
assert!(seq_acc_store.contains_account(&[1; 32])); assert!(seq_acc_store.contains_account(&[1; 32]));
assert!(seq_acc_store.contains_account(&[2; 32])); assert!(seq_acc_store.contains_account(&[2; 32]));
let acc_balance = seq_acc_store.get_account_balance(&[1; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[1; 32]);
assert_eq!(acc_balance, 12); assert_eq!(acc_balance, 12);
let acc_balance = seq_acc_store.get_account_balance(&[2; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[2; 32]);
assert_eq!(acc_balance, 100); assert_eq!(acc_balance, 100);
} }
@ -196,15 +216,15 @@ mod tests {
assert!(seq_acc_store.contains_account(&[7; 32])); assert!(seq_acc_store.contains_account(&[7; 32]));
assert!(seq_acc_store.contains_account(&[8; 32])); assert!(seq_acc_store.contains_account(&[8; 32]));
let acc_balance = seq_acc_store.get_account_balance(&[6; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[6; 32]);
assert_eq!(acc_balance, 120); assert_eq!(acc_balance, 120);
let acc_balance = seq_acc_store.get_account_balance(&[7; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[7; 32]);
assert_eq!(acc_balance, 15); assert_eq!(acc_balance, 15);
let acc_balance = seq_acc_store.get_account_balance(&[8; 32]).unwrap(); let acc_balance = seq_acc_store.get_account_balance(&[8; 32]);
assert_eq!(acc_balance, 10); assert_eq!(acc_balance, 10);
} }
@ -216,7 +236,7 @@ mod tests {
let acc_balance = seq_acc_store.get_account_balance(&[9; 32]); let acc_balance = seq_acc_store.get_account_balance(&[9; 32]);
assert!(acc_balance.is_none()); assert_eq!(acc_balance, 0);
} }
#[test] #[test]
@ -225,4 +245,15 @@ mod tests {
assert!(seq_acc_store.is_empty()); 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);
}
} }

View File

@ -29,12 +29,10 @@ impl SequecerChainStore {
is_genesis_random: bool, is_genesis_random: bool,
initial_accounts: &[AccountInitialData], initial_accounts: &[AccountInitialData],
) -> Self { ) -> Self {
let acc_data_decoded: Vec<([u8; 32], u64)> = initial_accounts let init_accs: Vec<_> = initial_accounts
.iter() .iter()
.map(|acc_data| { .map(|acc_data| {
( (
//ToDo: Handle this error for direct error message
//Failure to produce account address is critical, so error handling is needed only for clarity
hex::decode(acc_data.addr.clone()) hex::decode(acc_data.addr.clone())
.unwrap() .unwrap()
.try_into() .try_into()
@ -44,7 +42,7 @@ impl SequecerChainStore {
}) })
.collect(); .collect();
let acc_store = SequencerAccountsStore::new(&acc_data_decoded); let acc_store = SequencerAccountsStore::new(&init_accs);
let nullifier_store = HashSet::new(); let nullifier_store = HashSet::new();
let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]); let utxo_commitments_store = UTXOCommitmentsMerkleTree::new(vec![]);
let pub_tx_store = PublicTransactionMerkleTree::new(vec![]); let pub_tx_store = PublicTransactionMerkleTree::new(vec![]);

View File

@ -21,6 +21,9 @@ tokio.workspace = true
[dependencies.mempool] [dependencies.mempool]
path = "../mempool" path = "../mempool"
[dependencies.accounts]
path = "../accounts"
[dependencies.consensus] [dependencies.consensus]
path = "../consensus" path = "../consensus"

View File

@ -1,4 +1,5 @@
use actix_web::Error as HttpError; use actix_web::Error as HttpError;
use sequencer_core::config::AccountInitialData;
use serde_json::Value; use serde_json::Value;
use common::{ use common::{
@ -8,8 +9,8 @@ use common::{
message::{Message, Request}, message::{Message, Request},
parser::RpcRequest, parser::RpcRequest,
requests::{ requests::{
GetAccountBalanceRequest, GetAccountBalanceResponse, GetTransactionByHashRequest, GetAccountBalanceRequest, GetAccountBalanceResponse, GetInitialTestnetAccountsRequest,
GetTransactionByHashResponse, GetTransactionByHashRequest, GetTransactionByHashResponse,
}, },
}, },
}; };
@ -35,6 +36,8 @@ pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER";
pub const SUCCESS: &str = "Success"; pub const SUCCESS: &str = "Success";
pub const GET_INITIAL_TESTNET_ACCOUNTS: &str = "get_initial_testnet_accounts";
impl JsonHandler { impl JsonHandler {
pub async fn process(&self, message: Message) -> Result<Message, HttpError> { pub async fn process(&self, message: Message) -> Result<Message, HttpError> {
let id = message.id(); let id = message.id();
@ -140,6 +143,21 @@ impl JsonHandler {
respond(helperstruct) respond(helperstruct)
} }
/// Returns the initial accounts for testnet
/// ToDo: Useful only for testnet and needs to be removed later
async fn get_initial_testnet_accounts(&self, request: Request) -> Result<Value, RpcErr> {
let _get_initial_testnet_accounts_request =
GetInitialTestnetAccountsRequest::parse(Some(request.params))?;
let initial_accounts: Vec<AccountInitialData> = {
let state = self.sequencer_state.lock().await;
state.sequencer_config.initial_accounts.clone()
};
respond(initial_accounts)
}
/// Returns the balance of the account at the given address. /// Returns the balance of the account at the given address.
/// The address must be a valid hex string of the correct length. /// The address must be a valid hex string of the correct length.
async fn process_get_account_balance(&self, request: Request) -> Result<Value, RpcErr> { async fn process_get_account_balance(&self, request: Request) -> Result<Value, RpcErr> {
@ -153,8 +171,7 @@ impl JsonHandler {
let balance = { let balance = {
let state = self.sequencer_state.lock().await; let state = self.sequencer_state.lock().await;
state.store.acc_store.get_account_balance(&address) state.store.acc_store.get_account_balance(&address)
} };
.unwrap_or(0);
let helperstruct = GetAccountBalanceResponse { balance }; let helperstruct = GetAccountBalanceResponse { balance };
@ -187,6 +204,7 @@ impl JsonHandler {
GET_BLOCK => self.process_get_block_data(request).await, GET_BLOCK => self.process_get_block_data(request).await,
GET_GENESIS => self.process_get_genesis(request).await, GET_GENESIS => self.process_get_genesis(request).await,
GET_LAST_BLOCK => self.process_get_last_block(request).await, GET_LAST_BLOCK => self.process_get_last_block(request).await,
GET_INITIAL_TESTNET_ACCOUNTS => self.get_initial_testnet_accounts(request).await,
GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await, GET_ACCOUNT_BALANCE => self.process_get_account_balance(request).await,
GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await, GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await,
_ => Err(RpcErr(RpcError::method_not_found(request.method))), _ => Err(RpcErr(RpcError::method_not_found(request.method))),
@ -214,17 +232,28 @@ mod tests {
fn sequencer_config_for_tests() -> SequencerConfig { fn sequencer_config_for_tests() -> SequencerConfig {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let home = tempdir.path().to_path_buf(); let home = tempdir.path().to_path_buf();
let initial_accounts = vec![ let acc1_addr = vec![
AccountInitialData { 13, 150, 223, 204, 65, 64, 25, 56, 12, 157, 222, 12, 211, 220, 229, 170, 201, 15, 181,
addr: "cafe".repeat(16).to_string(), 68, 59, 248, 113, 16, 135, 65, 174, 175, 222, 85, 42, 215,
balance: 100,
},
AccountInitialData {
addr: "feca".repeat(16).to_string(),
balance: 200,
},
]; ];
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];
SequencerConfig { SequencerConfig {
home, home,
override_rust_log: Some("info".to_string()), override_rust_log: Some("info".to_string()),
@ -237,9 +266,13 @@ mod tests {
} }
} }
fn json_handler_for_tests() -> JsonHandler { fn json_handler_for_tests() -> (JsonHandler, Vec<AccountInitialData>) {
let config = sequencer_config_for_tests(); let config = sequencer_config_for_tests();
let mut sequencer_core = SequencerCore::start_from_config(config); let mut sequencer_core = SequencerCore::start_from_config(config);
let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone();
let tx_body = TransactionBody { let tx_body = TransactionBody {
tx_kind: common::transaction::TxKind::Public, tx_kind: common::transaction::TxKind::Public,
execution_input: Default::default(), execution_input: Default::default(),
@ -265,10 +298,15 @@ mod tests {
.produce_new_block_with_mempool_transactions() .produce_new_block_with_mempool_transactions()
.unwrap(); .unwrap();
JsonHandler { let sequencer_core = Arc::new(Mutex::new(sequencer_core));
polling_config: RpcPollingConfig::default(),
sequencer_state: Arc::new(Mutex::new(sequencer_core)), (
} JsonHandler {
polling_config: RpcPollingConfig::default(),
sequencer_state: sequencer_core,
},
initial_accounts,
)
} }
async fn call_rpc_handler_with_json(handler: JsonHandler, request_json: Value) -> Value { async fn call_rpc_handler_with_json(handler: JsonHandler, request_json: Value) -> Value {
@ -294,7 +332,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_account_balance_for_non_existent_account() { async fn test_get_account_balance_for_non_existent_account() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_account_balance", "method": "get_account_balance",
@ -316,7 +354,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_account_balance_for_invalid_hex() { async fn test_get_account_balance_for_invalid_hex() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_account_balance", "method": "get_account_balance",
@ -339,7 +377,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_account_balance_for_invalid_length() { async fn test_get_account_balance_for_invalid_length() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_account_balance", "method": "get_account_balance",
@ -362,18 +400,21 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_account_balance_for_existing_account() { async fn test_get_account_balance_for_existing_account() {
let json_handler = json_handler_for_tests(); let (json_handler, initial_accounts) = json_handler_for_tests();
let acc1_addr = initial_accounts[0].addr.clone();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_account_balance", "method": "get_account_balance",
"params": { "address": "cafe".repeat(16) }, "params": { "address": acc1_addr },
"id": 1 "id": 1
}); });
let expected_response = serde_json::json!({ let expected_response = serde_json::json!({
"id": 1, "id": 1,
"jsonrpc": "2.0", "jsonrpc": "2.0",
"result": { "result": {
"balance": 100 "balance": 10000
} }
}); });
@ -384,7 +425,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_transaction_by_hash_for_non_existent_hash() { async fn test_get_transaction_by_hash_for_non_existent_hash() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_transaction_by_hash", "method": "get_transaction_by_hash",
@ -406,7 +447,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_transaction_by_hash_for_invalid_hex() { async fn test_get_transaction_by_hash_for_invalid_hex() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_transaction_by_hash", "method": "get_transaction_by_hash",
@ -430,7 +471,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_transaction_by_hash_for_invalid_length() { async fn test_get_transaction_by_hash_for_invalid_length() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_transaction_by_hash", "method": "get_transaction_by_hash",
@ -454,7 +495,7 @@ mod tests {
#[actix_web::test] #[actix_web::test]
async fn test_get_transaction_by_hash_for_existing_transaction() { async fn test_get_transaction_by_hash_for_existing_transaction() {
let json_handler = json_handler_for_tests(); let (json_handler, _) = json_handler_for_tests();
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "get_transaction_by_hash", "method": "get_transaction_by_hash",

View File

@ -7,13 +7,6 @@
"block_create_timeout_millis": 10000, "block_create_timeout_millis": 10000,
"port": 3040, "port": 3040,
"initial_accounts": [ "initial_accounts": [
{
"addr": "bfd91e6703273a115ad7f099ef32f621243be69369d00ddef5d3a25117d09a8c",
"balance": 10
},
{
"addr": "20573479053979b98d2ad09ef31a0750f22c77709bed51c4e64946bd1e376f31",
"balance": 100
}
] ]
} }