Merge pull request #102 from vacp2p/Pravdyvy/structural-improvements

Structural improvements
This commit is contained in:
Pravdyvy 2025-08-20 17:51:57 +03:00 committed by GitHub
commit 6547d0efb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 307 additions and 3265 deletions

View File

@ -8,11 +8,9 @@ members = [
"utxo",
"sequencer_rpc",
"mempool",
"zkvm",
"wallet",
"sequencer_core",
"common",
"sc_core",
"nssa",
]

View File

@ -4,18 +4,14 @@ use anyhow::Result;
use common::{merkle_tree_public::TreeHashType, transaction::Tag};
use k256::AffinePoint;
use log::info;
use nssa::Address;
use serde::{Deserialize, Serialize};
use utxo::utxo_core::UTXO;
pub mod address;
use crate::{
account_core::address::AccountAddress,
key_management::{
constants_types::{CipherText, Nonce},
ephemeral_key_holder::EphemeralKeyHolder,
AddressKeyHolder,
},
use crate::key_management::{
constants_types::{CipherText, Nonce},
ephemeral_key_holder::EphemeralKeyHolder,
AddressKeyHolder,
};
pub type PublicKey = AffinePoint;
@ -23,7 +19,7 @@ pub type PublicKey = AffinePoint;
#[derive(Clone, Debug)]
pub struct Account {
pub key_holder: AddressKeyHolder,
pub address: AccountAddress,
pub address: Address,
pub balance: u64,
pub utxos: HashMap<TreeHashType, UTXO>,
}
@ -31,7 +27,7 @@ pub struct Account {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AccountForSerialization {
pub key_holder: AddressKeyHolder,
pub address: AccountAddress,
pub address: Address,
pub balance: u64,
pub utxos: HashMap<String, UTXO>,
}
@ -95,7 +91,7 @@ impl<'de> Deserialize<'de> for Account {
pub struct AccountPublicMask {
pub nullifier_public_key: AffinePoint,
pub viewing_public_key: AffinePoint,
pub address: AccountAddress,
pub address: Address,
pub balance: u64,
}
@ -110,7 +106,7 @@ impl AccountPublicMask {
}
pub fn make_tag(&self) -> Tag {
self.address[0]
self.address.value()[0]
}
}
@ -119,13 +115,13 @@ impl Account {
let key_holder = AddressKeyHolder::new_os_random();
let public_key =
nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key());
let address = nssa::Address::from_public_key(&public_key);
let address = nssa::Address::from(&public_key);
let balance = 0;
let utxos = HashMap::new();
Self {
key_holder,
address: *address.value(),
address,
balance,
utxos,
}
@ -135,12 +131,12 @@ impl Account {
let key_holder = AddressKeyHolder::new_os_random();
let public_key =
nssa::PublicKey::new_from_private_key(key_holder.get_pub_account_signing_key());
let address = nssa::Address::from_public_key(&public_key);
let address = nssa::Address::from(&public_key);
let utxos = HashMap::new();
Self {
key_holder,
address: *address.value(),
address,
balance,
utxos,
}
@ -185,7 +181,7 @@ impl Account {
privacy_flag: bool,
) -> Result<()> {
let asset_utxo = UTXO::new(
self.address,
*self.address.value(),
serde_json::to_vec(&asset)?,
amount,
privacy_flag,
@ -203,7 +199,7 @@ impl Account {
}
pub fn make_tag(&self) -> Tag {
self.address[0]
self.address.value()[0]
}
///Produce account public mask
@ -241,8 +237,8 @@ mod tests {
#[test]
fn test_add_new_utxo_outputs() {
let mut account = Account::new();
let utxo1 = generate_dummy_utxo(account.address, 100);
let utxo2 = generate_dummy_utxo(account.address, 200);
let utxo1 = generate_dummy_utxo(*account.address.value(), 100);
let utxo2 = generate_dummy_utxo(*account.address.value(), 200);
let result = account.add_new_utxo_outputs(vec![utxo1.clone(), utxo2.clone()]);

View File

@ -343,7 +343,7 @@ mod tests {
let public_key = nssa::PublicKey::new_from_private_key(&pub_account_signing_key);
let address = nssa::Address::from_public_key(&public_key);
let address = nssa::Address::from(&public_key);
println!("======Prerequisites======");
println!();

View File

@ -5,10 +5,10 @@
"seq_poll_timeout_secs": 10,
"initial_accounts": [
{
"address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143],
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"balance": 10000,
"key_holder": {
"address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143],
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718",
"pub_account_signing_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
"top_secret_key_holder": {
@ -23,10 +23,10 @@
"utxos": {}
},
{
"address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102],
"address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"balance": 20000,
"key_holder": {
"address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102],
"address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271",
"pub_account_signing_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
"top_secret_key_holder": {

View File

@ -180,6 +180,72 @@ pub async fn test_failure() {
info!("Success!");
}
pub async fn test_success_two_transactions() {
let command = Command::SendNativeTokenTransfer {
from: ACC_SENDER.to_string(),
nonce: 0,
to: ACC_RECEIVER.to_string(),
amount: 100,
};
let wallet_config = fetch_config().unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
wallet::execute_subcommand(command).await.unwrap();
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = seq_client
.get_account_balance(ACC_SENDER.to_string())
.await
.unwrap();
let acc_2_balance = seq_client
.get_account_balance(ACC_RECEIVER.to_string())
.await
.unwrap();
info!("Balance of sender : {acc_1_balance:#?}");
info!("Balance of receiver : {acc_2_balance:#?}");
assert_eq!(acc_1_balance.balance, 9900);
assert_eq!(acc_2_balance.balance, 20100);
info!("First TX Success!");
let command = Command::SendNativeTokenTransfer {
from: ACC_SENDER.to_string(),
nonce: 1,
to: ACC_RECEIVER.to_string(),
amount: 100,
};
wallet::execute_subcommand(command).await.unwrap();
info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct balance move");
let acc_1_balance = seq_client
.get_account_balance(ACC_SENDER.to_string())
.await
.unwrap();
let acc_2_balance = seq_client
.get_account_balance(ACC_RECEIVER.to_string())
.await
.unwrap();
info!("Balance of sender : {acc_1_balance:#?}");
info!("Balance of receiver : {acc_2_balance:#?}");
assert_eq!(acc_1_balance.balance, 9800);
assert_eq!(acc_2_balance.balance, 20200);
info!("Second TX Success!");
}
macro_rules! test_cleanup_wrap {
($home_dir:ident, $test_func:ident) => {{
let res = pre_test($home_dir.clone()).await.unwrap();
@ -212,10 +278,14 @@ pub async fn main_tests_runner() -> Result<()> {
"test_failure" => {
test_cleanup_wrap!(home_dir, test_failure);
}
"test_success_two_transactions" => {
test_cleanup_wrap!(home_dir, test_success_two_transactions);
}
"all" => {
test_cleanup_wrap!(home_dir, test_success_move_to_another_account);
test_cleanup_wrap!(home_dir, test_success);
test_cleanup_wrap!(home_dir, test_failure);
test_cleanup_wrap!(home_dir, test_success_two_transactions);
}
_ => {
anyhow::bail!("Unknown test name");

View File

@ -12,7 +12,8 @@ serde = "1.0.219"
sha2 = "0.10.9"
secp256k1 = "0.31.1"
rand = "0.8"
hex = "0.4.3"
anyhow.workspace = true
[dev-dependencies]
test-program-methods = { path = "test_program_methods" }
hex = "0.4.3"

View File

@ -1,8 +1,10 @@
use std::{fmt::Display, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::signature::PublicKey;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Address {
value: [u8; 32],
}
@ -12,12 +14,102 @@ impl Address {
Self { value }
}
pub fn from_public_key(public_key: &PublicKey) -> Self {
// TODO: Check specs
Address::new(*public_key.value())
}
pub fn value(&self) -> &[u8; 32] {
&self.value
}
}
impl AsRef<[u8]> for Address {
fn as_ref(&self) -> &[u8] {
&self.value
}
}
impl From<&PublicKey> for Address {
fn from(value: &PublicKey) -> Self {
// TODO: Check specs
Self::new(*value.value())
}
}
#[derive(Debug, thiserror::Error)]
pub enum AddressError {
#[error("invalid hex")]
InvalidHex(#[from] hex::FromHexError),
#[error("invalid length: expected 32 bytes, got {0}")]
InvalidLength(usize),
}
impl FromStr for Address {
type Err = AddressError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = hex::decode(s)?;
if bytes.len() != 32 {
return Err(AddressError::InvalidLength(bytes.len()));
}
let mut value = [0u8; 32];
value.copy_from_slice(&bytes);
Ok(Address { value })
}
}
impl Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.value))
}
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let hex_string = self.to_string();
hex_string.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let hex_string = String::deserialize(deserializer)?;
Address::from_str(&hex_string).map_err(serde::de::Error::custom)
}
}
#[cfg(test)]
mod tests {
use crate::{Address, address::AddressError};
#[test]
fn parse_valid_address() {
let hex_str = "00".repeat(32); // 64 hex chars = 32 bytes
let addr: Address = hex_str.parse().unwrap();
assert_eq!(addr.value, [0u8; 32]);
}
#[test]
fn parse_invalid_hex() {
let hex_str = "zz".repeat(32); // invalid hex chars
let result = hex_str.parse::<Address>().unwrap_err();
assert!(matches!(result, AddressError::InvalidHex(_)));
}
#[test]
fn parse_wrong_length_short() {
let hex_str = "00".repeat(31); // 62 chars = 31 bytes
let result = hex_str.parse::<Address>().unwrap_err();
assert!(matches!(result, AddressError::InvalidLength(_)));
}
#[test]
fn parse_wrong_length_long() {
let hex_str = "00".repeat(33); // 66 chars = 33 bytes
let result = hex_str.parse::<Address>().unwrap_err();
assert!(matches!(result, AddressError::InvalidLength(_)));
}
}

View File

@ -1,4 +1,4 @@
mod address;
pub mod address;
pub mod error;
pub mod program;
pub mod public_transaction;

View File

@ -39,7 +39,7 @@ impl PublicTransaction {
self.witness_set
.signatures_and_public_keys()
.iter()
.map(|(_, public_key)| Address::from_public_key(public_key))
.map(|(_, public_key)| Address::from(public_key))
.collect()
}
@ -129,14 +129,14 @@ pub mod tests {
fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) {
let key1 = PrivateKey::try_new([1; 32]).unwrap();
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let addr1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1));
let addr2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2));
let addr1 = Address::from(&PublicKey::new_from_private_key(&key1));
let addr2 = Address::from(&PublicKey::new_from_private_key(&key2));
(key1, key2, addr1, addr2)
}
fn state_for_tests() -> V01State {
let (_, _, addr1, addr2) = keys_for_tests();
let initial_data = [(*addr1.value(), 10000), (*addr2.value(), 20000)];
let initial_data = [(addr1, 10000), (addr2, 20000)];
V01State::new_with_genesis_accounts(&initial_data)
}
@ -224,7 +224,7 @@ pub mod tests {
let instruction = 1337;
let message = Message::try_new(
Program::authenticated_transfer_program().id(),
vec![addr1.clone(), addr1],
vec![addr1, addr1],
nonces,
instruction,
)

View File

@ -49,8 +49,8 @@ mod tests {
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let pubkey1 = PublicKey::new_from_private_key(&key1);
let pubkey2 = PublicKey::new_from_private_key(&key2);
let addr1 = Address::from_public_key(&pubkey1);
let addr2 = Address::from_public_key(&pubkey2);
let addr1 = Address::from(&pubkey1);
let addr2 = Address::from(&pubkey2);
let nonces = vec![1, 2];
let instruction = vec![1, 2, 3, 4];
let message = Message::try_new([0; 8], vec![addr1, addr2], nonces, instruction).unwrap();

View File

@ -10,18 +10,17 @@ pub struct V01State {
}
impl V01State {
pub fn new_with_genesis_accounts(initial_data: &[([u8; 32], u128)]) -> Self {
pub fn new_with_genesis_accounts(initial_data: &[(Address, u128)]) -> Self {
let authenticated_transfer_program = Program::authenticated_transfer_program();
let public_state = initial_data
.iter()
.copied()
.map(|(address_value, balance)| {
.map(|(address, balance)| {
let account = Account {
balance,
program_owner: authenticated_transfer_program.id(),
..Account::default()
};
let address = Address::new(address_value);
(address, account)
})
.collect();
@ -106,9 +105,9 @@ mod tests {
fn test_new_with_genesis() {
let key1 = PrivateKey::try_new([1; 32]).unwrap();
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let addr1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1));
let addr2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2));
let initial_data = [(*addr1.value(), 100u128), (*addr2.value(), 151u128)];
let addr1 = Address::from(&PublicKey::new_from_private_key(&key1));
let addr2 = Address::from(&PublicKey::new_from_private_key(&key2));
let initial_data = [(addr1, 100u128), (addr2, 151u128)];
let program = Program::authenticated_transfer_program();
let expected_public_state = {
let mut this = HashMap::new();
@ -157,8 +156,8 @@ mod tests {
#[test]
fn test_get_account_by_address_non_default_account() {
let key = PrivateKey::try_new([1; 32]).unwrap();
let addr = Address::from_public_key(&PublicKey::new_from_private_key(&key));
let initial_data = [(*addr.value(), 100u128)];
let addr = Address::from(&PublicKey::new_from_private_key(&key));
let initial_data = [(addr, 100u128)];
let state = V01State::new_with_genesis_accounts(&initial_data);
let expected_account = state.public_state.get(&addr).unwrap();
@ -190,15 +189,15 @@ mod tests {
#[test]
fn transition_from_authenticated_transfer_program_invocation_default_account_destination() {
let key = PrivateKey::try_new([1; 32]).unwrap();
let address = Address::from_public_key(&PublicKey::new_from_private_key(&key));
let initial_data = [(*address.value(), 100)];
let address = Address::from(&PublicKey::new_from_private_key(&key));
let initial_data = [(address, 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = address;
let to = Address::new([2; 32]);
assert_eq!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 5;
let tx = transfer_transaction(from.clone(), key, 0, to.clone(), balance_to_move);
let tx = transfer_transaction(from, key, 0, to, balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 95);
@ -210,8 +209,8 @@ mod tests {
#[test]
fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() {
let key = PrivateKey::try_new([1; 32]).unwrap();
let address = Address::from_public_key(&PublicKey::new_from_private_key(&key));
let initial_data = [(*address.value(), 100)];
let address = Address::from(&PublicKey::new_from_private_key(&key));
let initial_data = [(address, 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = address;
let from_key = key;
@ -219,7 +218,7 @@ mod tests {
let balance_to_move = 101;
assert!(state.get_account_by_address(&from).balance < balance_to_move);
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
let result = state.transition_from_public_transaction(&tx);
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
@ -233,9 +232,9 @@ mod tests {
fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() {
let key1 = PrivateKey::try_new([1; 32]).unwrap();
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let address1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1));
let address2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2));
let initial_data = [(*address1.value(), 100), (*address2.value(), 200)];
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
let address2 = Address::from(&PublicKey::new_from_private_key(&key2));
let initial_data = [(address1, 100), (address2, 200)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let from = address2;
let from_key = key2;
@ -243,7 +242,7 @@ mod tests {
assert_ne!(state.get_account_by_address(&to), Account::default());
let balance_to_move = 8;
let tx = transfer_transaction(from.clone(), from_key, 0, to.clone(), balance_to_move);
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&from).balance, 192);
@ -255,18 +254,18 @@ mod tests {
#[test]
fn transition_from_chained_authenticated_transfer_program_invocations() {
let key1 = PrivateKey::try_new([8; 32]).unwrap();
let address1 = Address::from_public_key(&PublicKey::new_from_private_key(&key1));
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
let key2 = PrivateKey::try_new([2; 32]).unwrap();
let address2 = Address::from_public_key(&PublicKey::new_from_private_key(&key2));
let initial_data = [(*address1.value(), 100)];
let address2 = Address::from(&PublicKey::new_from_private_key(&key2));
let initial_data = [(address1, 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data);
let address3 = Address::new([3; 32]);
let balance_to_move = 5;
let tx = transfer_transaction(address1.clone(), key1, 0, address2.clone(), balance_to_move);
let tx = transfer_transaction(address1, key1, 0, address2, balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
let balance_to_move = 3;
let tx = transfer_transaction(address2.clone(), key2, 0, address3.clone(), balance_to_move);
let tx = transfer_transaction(address2, key2, 0, address3, balance_to_move);
state.transition_from_public_transaction(&tx).unwrap();
assert_eq!(state.get_account_by_address(&address1).balance, 95);
@ -336,7 +335,7 @@ mod tests {
#[test]
fn test_program_should_fail_if_modifies_nonces() {
let initial_data = [([1; 32], 100)];
let initial_data = [(Address::new([1; 32]), 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs();
let addresses = vec![Address::new([1; 32])];
let program_id = Program::nonce_changer_program().id();
@ -352,7 +351,7 @@ mod tests {
#[test]
fn test_program_should_fail_if_output_accounts_exceed_inputs() {
let initial_data = [([1; 32], 100)];
let initial_data = [(Address::new([1; 32]), 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs();
let addresses = vec![Address::new([1; 32])];
let program_id = Program::extra_output_program().id();
@ -368,7 +367,7 @@ mod tests {
#[test]
fn test_program_should_fail_with_missing_output_accounts() {
let initial_data = [([1; 32], 100)];
let initial_data = [(Address::new([1; 32]), 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs();
let addresses = vec![Address::new([1; 32]), Address::new([2; 32])];
let program_id = Program::missing_output_program().id();
@ -384,7 +383,7 @@ mod tests {
#[test]
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() {
let initial_data = [([1; 32], 0)];
let initial_data = [(Address::new([1; 32]), 0)];
let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs();
let address = Address::new([1; 32]);
let account = state.get_account_by_address(&address);
@ -478,7 +477,7 @@ mod tests {
#[test]
fn test_program_should_fail_if_transfers_balance_from_non_owned_account() {
let initial_data = [([1; 32], 100)];
let initial_data = [(Address::new([1; 32]), 100)];
let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs();
let sender_address = Address::new([1; 32]);
let receiver_address = Address::new([2; 32]);

View File

@ -1,38 +0,0 @@
[package]
name = "sc_core"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
serde_json.workspace = true
env_logger.workspace = true
log.workspace = true
serde.workspace = true
rand.workspace = true
k256.workspace = true
sha2.workspace = true
bincode.workspace = true
elliptic-curve.workspace = true
hex.workspace = true
light-poseidon.workspace = true
ark-bn254.workspace = true
ark-ff.workspace = true
risc0-zkvm = "2.3.1"
[dependencies.accounts]
path = "../accounts"
[dependencies.storage]
path = "../storage"
[dependencies.utxo]
path = "../utxo"
[dependencies.common]
path = "../common"
[dependencies.secp256k1-zkp]
workspace = true
features = ["std", "rand-std", "rand", "serde", "global-context"]

View File

@ -1,125 +0,0 @@
use serde::Serialize;
use storage::{
sc_db_utils::{produce_blob_from_fit_vec, DataBlob, DataBlobChangeVariant},
SC_DATA_BLOB_SIZE,
};
///Creates blob list from generic serializable state
///
///`ToDo`: Find a way to align data in a way, to minimize read and write operations in db
pub fn produce_blob_list_from_sc_public_state<S: Serialize>(
state: &S,
) -> Result<Vec<DataBlob>, serde_json::Error> {
let mut blob_list = vec![];
let ser_data = serde_json::to_vec(state)?;
//`ToDo` Replace with `next_chunk` usage, when feature stabilizes in Rust
for i in 0..=(ser_data.len() / SC_DATA_BLOB_SIZE) {
let next_chunk: Vec<u8> = if (i + 1) * SC_DATA_BLOB_SIZE < ser_data.len() {
ser_data[(i * SC_DATA_BLOB_SIZE)..((i + 1) * SC_DATA_BLOB_SIZE)].to_vec()
} else {
ser_data[(i * SC_DATA_BLOB_SIZE)..(ser_data.len())].to_vec()
};
blob_list.push(produce_blob_from_fit_vec(next_chunk));
}
Ok(blob_list)
}
///Compare two consecutive in time blob lists to produce list of modified ids
pub fn compare_blob_lists(
blob_list_old: &[DataBlob],
blob_list_new: &[DataBlob],
) -> Vec<DataBlobChangeVariant> {
let mut changed_ids = vec![];
let mut id_end = 0;
let old_len = blob_list_old.len();
let new_len = blob_list_new.len();
if old_len > new_len {
for id in new_len..old_len {
changed_ids.push(DataBlobChangeVariant::Deleted { id });
}
} else if new_len > old_len {
for (id, blob) in blob_list_new.iter().enumerate().take(new_len).skip(old_len) {
changed_ids.push(DataBlobChangeVariant::Created { id, blob: *blob });
}
}
loop {
let old_blob = blob_list_old.get(id_end);
let new_blob = blob_list_new.get(id_end);
match (old_blob, new_blob) {
(Some(old), Some(new)) => {
if old != new {
changed_ids.push(DataBlobChangeVariant::Modified {
id: id_end,
blob_old: *old,
blob_new: *new,
});
}
}
_ => break,
}
id_end += 1;
}
changed_ids
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Serialize;
const TEST_BLOB_SIZE: usize = 256; // Define a test blob size for simplicity
static SC_DATA_BLOB_SIZE: usize = TEST_BLOB_SIZE;
#[derive(Serialize)]
struct TestState {
a: u32,
b: u32,
}
#[test]
fn test_produce_blob_list_from_sc_public_state() {
let state = TestState { a: 42, b: 99 };
let result = produce_blob_list_from_sc_public_state(&state).unwrap();
assert!(!result.is_empty());
}
#[test]
fn test_compare_blob_lists_created() {
let old_list: Vec<DataBlob> = vec![];
let new_list: Vec<DataBlob> = vec![[1; SC_DATA_BLOB_SIZE].into()];
let changes = compare_blob_lists(&old_list, &new_list);
assert_eq!(changes.len(), 1);
assert!(matches!(changes[0], DataBlobChangeVariant::Created { .. }));
}
#[test]
fn test_compare_blob_lists_deleted() {
let old_list: Vec<DataBlob> = vec![[1; SC_DATA_BLOB_SIZE].into()];
let new_list: Vec<DataBlob> = vec![];
let changes = compare_blob_lists(&old_list, &new_list);
assert_eq!(changes.len(), 1);
assert!(matches!(changes[0], DataBlobChangeVariant::Deleted { .. }));
}
#[test]
fn test_compare_blob_lists_modified() {
let old_list: Vec<DataBlob> = vec![[1; SC_DATA_BLOB_SIZE].into()];
let new_list: Vec<DataBlob> = vec![[2; SC_DATA_BLOB_SIZE].into()];
let changes = compare_blob_lists(&old_list, &new_list);
assert_eq!(changes.len(), 1);
assert!(matches!(changes[0], DataBlobChangeVariant::Modified { .. }));
}
}

View File

@ -1,10 +0,0 @@
use ark_bn254::Fr;
use light_poseidon::{Poseidon, PoseidonBytesHasher};
pub fn poseidon_hash(inputs: &[&[u8]]) -> anyhow::Result<[u8; 32]> {
let mut poseidon = Poseidon::<Fr>::new_circom(2).unwrap();
let hash = poseidon.hash_bytes_be(inputs)?;
Ok(hash)
}

View File

@ -1,5 +0,0 @@
pub mod blob_utils;
pub mod cryptography;
pub mod proofs_circuits;
pub mod public_context;
pub mod transaction_payloads_tools;

View File

@ -1,285 +0,0 @@
use bincode;
use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree;
use rand::{thread_rng, RngCore};
use secp256k1_zkp::{CommitmentSecrets, Generator, PedersenCommitment, Tag, Tweak, SECP256K1};
use sha2::{Digest, Sha256};
use utxo::utxo_core::UTXO;
//
use crate::{cryptography::poseidon_hash, public_context::PublicSCContext};
fn hash(input: &[u8]) -> Vec<u8> {
Sha256::digest(input).to_vec()
}
/// Generate nullifiers
///
/// takes the input_utxo and npk
///
/// returns the nullifiers[i], where the nullifiers[i] = poseidon_hash(in_commitments[i] || npk)
pub fn generate_nullifiers(input_utxo: &UTXO, npk: &[u8]) -> Vec<u8> {
let commitment = generate_commitment(input_utxo);
poseidon_hash(&[commitment.as_ref(), npk]).unwrap().to_vec()
}
/// Generate commitment for UTXO
///
/// uses the input_utxo
///
/// returns commitment here commitment is a hash(bincode(input_utxo))
pub fn generate_commitment(input_utxo: &UTXO) -> Vec<u8> {
let serialized = bincode::serialize(input_utxo).unwrap(); // Serialize UTXO.
hash(&serialized)
}
/// Generate commitments for UTXO
///
/// uses the input_utxos
///
/// returns commitments
pub fn generate_commitments(input_utxos: &[UTXO]) -> Vec<Vec<u8>> {
input_utxos
.iter()
.map(|utxo| {
let serialized = bincode::serialize(utxo).unwrap(); // Serialize UTXO.
hash(&serialized)
})
.collect()
}
/// Validate inclusion proof for in_commitments
///
/// ToDo: Solve it in more scalable way
pub fn validate_in_commitments_tree(
in_commitment: &[u8],
commitment_tree: &UTXOCommitmentsMerkleTree,
) -> bool {
let alighned_hash: [u8; 32] = in_commitment.try_into().unwrap();
commitment_tree.get_proof(alighned_hash).is_some()
}
/// Check, that input utxos balances is equal to out utxo balances
pub fn check_balances_private(in_utxos: &[UTXO], out_utxos: &[UTXO]) -> bool {
let in_sum = in_utxos.iter().fold(0, |prev, utxo| prev + utxo.amount);
let out_sum = out_utxos.iter().fold(0, |prev, utxo| prev + utxo.amount);
in_sum == out_sum
}
pub fn private_circuit(
input_utxos: &[UTXO],
output_utxos: &[UTXO],
public_context: &PublicSCContext,
) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
assert!(check_balances_private(input_utxos, output_utxos));
let in_commitments = generate_commitments(input_utxos);
let mut in_nullifiers = vec![];
for in_utxo in input_utxos {
let nullifier_public_key = public_context
.account_masks
.get(&in_utxo.owner)
.unwrap()
.nullifier_public_key;
let key_ser = serde_json::to_vec(&nullifier_public_key).unwrap();
in_nullifiers.push(generate_nullifiers(in_utxo, &key_ser));
}
for in_commitment in in_commitments {
assert!(validate_in_commitments_tree(
&in_commitment,
&public_context.commitments_tree,
));
}
(in_nullifiers, generate_commitments(output_utxos))
}
/// Check balances DE
///
/// takes the input_utxos[] and output_balance,
///
/// returns the True if the token amount in output_balance matches the sum of all input_utxos[], otherwise return False.
pub fn check_balances_de(input_utxos: &[UTXO], output_balance: u128) -> bool {
let total_input: u128 = input_utxos.iter().map(|utxo| utxo.amount).sum();
total_input == output_balance
}
pub fn deshielded_circuit(
input_utxos: &[UTXO],
output_balance: u128,
public_context: &PublicSCContext,
) -> Vec<Vec<u8>> {
assert!(check_balances_de(input_utxos, output_balance));
let in_commitments = generate_commitments(input_utxos);
let mut in_nullifiers = vec![];
for in_utxo in input_utxos {
let nullifier_public_key = public_context
.account_masks
.get(&in_utxo.owner)
.unwrap()
.nullifier_public_key;
let key_ser = serde_json::to_vec(&nullifier_public_key).unwrap();
in_nullifiers.push(generate_nullifiers(in_utxo, &key_ser));
}
for in_commitment in in_commitments {
assert!(validate_in_commitments_tree(
&in_commitment,
&public_context.commitments_tree,
));
}
in_nullifiers
}
#[allow(unused)]
fn commitment_secrets_random(value: u64) -> CommitmentSecrets {
CommitmentSecrets {
value,
value_blinding_factor: Tweak::new(&mut thread_rng()),
generator_blinding_factor: Tweak::new(&mut thread_rng()),
}
}
pub fn tag_random() -> Tag {
use rand::thread_rng;
use rand::RngCore;
let mut bytes = [0u8; 32];
thread_rng().fill_bytes(&mut bytes);
Tag::from(bytes)
}
pub fn commit(comm: &CommitmentSecrets, tag: Tag) -> PedersenCommitment {
let generator = Generator::new_blinded(SECP256K1, tag, comm.generator_blinding_factor);
PedersenCommitment::new(SECP256K1, comm.value, comm.value_blinding_factor, generator)
}
/// new_commitment for a Vec of values
pub fn pedersen_commitment_vec(
public_info_vec: Vec<u64>,
) -> (Tweak, [u8; 32], Vec<PedersenCommitment>) {
let mut random_val: [u8; 32] = [0; 32];
thread_rng().fill_bytes(&mut random_val);
let generator_blinding_factor = Tweak::new(&mut thread_rng());
let tag = tag_random();
let vec_commitments = public_info_vec
.into_iter()
.map(|public_info| {
let commitment_secrets = CommitmentSecrets {
value: public_info,
value_blinding_factor: Tweak::from_slice(&random_val).unwrap(),
generator_blinding_factor,
};
commit(&commitment_secrets, tag)
})
.collect();
(generator_blinding_factor, random_val, vec_commitments)
}
/// Verify Pedersen commitment
///
/// takes the public_info, secret_r and pedersen_commitment and
///
/// checks that commitment(public_info,secret_r) is equal pedersen_commitment where the commitment is pedersen commitment.
pub fn verify_commitment(
public_info: u64,
secret_r: &[u8],
pedersen_commitment: &PedersenCommitment,
) -> bool {
let commitment_secrets = CommitmentSecrets {
value: public_info,
value_blinding_factor: Tweak::from_slice(secret_r).unwrap(),
generator_blinding_factor: Tweak::new(&mut thread_rng()),
};
let tag = tag_random();
let commitment = commit(&commitment_secrets, tag);
commitment == *pedersen_commitment
}
/// Validate inclusion proof for pedersen_commitment
///
/// ToDo: Solve it in more scalable way
pub fn validate_in_commitments_tree_se(
pedersen_commitment: &PedersenCommitment,
commitment_tree: &UTXOCommitmentsMerkleTree,
) -> bool {
let alighned_hash: [u8; 32] = pedersen_commitment.serialize()[0..32].try_into().unwrap();
commitment_tree.get_proof(alighned_hash).is_some()
}
/// Generate nullifier SE
///
/// takes the pedersen_commitment and npk then
/// returns a nullifier, where the nullifier = poseidon_hash(pedersen_commitment || npk)
pub fn generate_nullifiers_se(pedersen_commitment: &PedersenCommitment, npk: &[u8]) -> Vec<u8> {
let commitment_ser = pedersen_commitment.serialize().to_vec();
poseidon_hash(&[&commitment_ser, npk]).unwrap().to_vec()
}
/// Check balances SE
///
/// takes the input_balance and output_utxos[],
///
/// returns the True if the token amount in input_balance matches the sum of all output_utxos[], otherwise return False.
pub fn check_balances_se(input_balance: u128, output_utxos: &[UTXO]) -> bool {
let total_output: u128 = output_utxos.iter().map(|utxo| utxo.amount).sum();
total_output == input_balance
}
pub fn shielded_circuit(
public_info: u64,
output_utxos: &[UTXO],
pedersen_commitment: PedersenCommitment,
secret_r: &[u8],
public_context: &PublicSCContext,
) -> (Vec<Vec<u8>>, Vec<u8>) {
assert!(check_balances_se(public_info as u128, output_utxos));
let out_commitments = generate_commitments(output_utxos);
let nullifier_public_key = public_context
.account_masks
.get(&public_context.caller_address)
.unwrap()
.nullifier_public_key;
let key_ser = serde_json::to_vec(&nullifier_public_key).unwrap();
let nullifier = generate_nullifiers_se(&pedersen_commitment, &key_ser);
assert!(validate_in_commitments_tree_se(
&pedersen_commitment,
&public_context.commitments_tree,
));
assert!(verify_commitment(
public_info,
secret_r,
&pedersen_commitment
));
(out_commitments, nullifier)
}

View File

@ -1,180 +0,0 @@
use std::collections::BTreeMap;
use accounts::account_core::{address::AccountAddress, AccountPublicMask};
use common::merkle_tree_public::{merkle_tree::UTXOCommitmentsMerkleTree, TreeHashType};
use serde::{ser::SerializeStruct, Serialize};
pub const PUBLIC_SC_CONTEXT: &str = "PublicSCContext";
pub const CALLER_ADDRESS: &str = "caller_address";
pub const CALLER_BALANCE: &str = "caller_balance";
pub const ACCOUNT_MASKS_KEYS_SORTED: &str = "account_masks_keys_sorted";
pub const ACCOUNT_MASKS_VALUES_SORTED: &str = "account_masks_values_sorted";
pub const COMMITMENT_STORE_ROOT: &str = "commitment_store_root";
pub const PUT_TX_STORE_ROOT: &str = "put_tx_store_root";
pub const COMMITMENT_TREE: &str = "commitments_tree";
pub const NULLIFIERS_SET: &str = "nullifiers_set";
///Strucutre, representing context, given to a smart contract on a call
pub struct PublicSCContext {
pub caller_address: AccountAddress,
pub caller_balance: u64,
pub account_masks: BTreeMap<AccountAddress, AccountPublicMask>,
pub comitment_store_root: TreeHashType,
pub commitments_tree: UTXOCommitmentsMerkleTree,
}
impl Serialize for PublicSCContext {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut account_masks_keys: Vec<[u8; 32]> = self.account_masks.keys().cloned().collect();
account_masks_keys.sort();
let mut account_mask_values: Vec<AccountPublicMask> =
self.account_masks.values().cloned().collect();
account_mask_values.sort_by(|left, right| left.address.cmp(&right.address));
let mut s = serializer.serialize_struct(PUBLIC_SC_CONTEXT, 7)?;
s.serialize_field(CALLER_ADDRESS, &self.caller_address)?;
s.serialize_field(CALLER_BALANCE, &self.caller_balance)?;
s.serialize_field(ACCOUNT_MASKS_KEYS_SORTED, &account_masks_keys)?;
s.serialize_field(ACCOUNT_MASKS_VALUES_SORTED, &account_mask_values)?;
s.serialize_field(COMMITMENT_STORE_ROOT, &self.comitment_store_root)?;
s.serialize_field(COMMITMENT_TREE, &self.commitments_tree)?;
s.end()
}
}
impl PublicSCContext {
///Produces `u64` from bytes in a vector
///
/// Assumes, that vector of le_bytes
pub fn produce_u64_from_fit_vec(data: Vec<u8>) -> u64 {
let data_len = data.len();
assert!(data_len <= 8);
let mut le_bytes: [u8; 8] = [0; 8];
for (idx, item) in data.into_iter().enumerate() {
le_bytes[idx] = item
}
u64::from_le_bytes(le_bytes)
}
///Produces vector of `u64` from context
pub fn produce_u64_list_from_context(&self) -> Result<Vec<u64>, serde_json::Error> {
let mut u64_list = vec![];
let ser_data = serde_json::to_vec(self)?;
//`ToDo` Replace with `next_chunk` usage, when feature stabilizes in Rust
for i in 0..=(ser_data.len() / 8) {
let next_chunk: Vec<u8> = if (i + 1) * 8 < ser_data.len() {
ser_data[(i * 8)..((i + 1) * 8)].to_vec()
} else {
ser_data[(i * 8)..(ser_data.len())].to_vec()
};
u64_list.push(PublicSCContext::produce_u64_from_fit_vec(next_chunk));
}
Ok(u64_list)
}
}
#[cfg(test)]
mod tests {
use accounts::account_core::Account;
use common::utxo_commitment::UTXOCommitment;
use super::*;
fn create_test_context() -> PublicSCContext {
let caller_address = [1; 32];
let comitment_store_root = [3; 32];
let commitments_tree =
UTXOCommitmentsMerkleTree::new(vec![UTXOCommitment { hash: [5; 32] }]);
let mut account_masks = BTreeMap::new();
let acc_1 = Account::new();
let acc_2 = Account::new();
let acc_3 = Account::new();
account_masks.insert(acc_1.address, acc_1.make_account_public_mask());
account_masks.insert(acc_2.address, acc_2.make_account_public_mask());
account_masks.insert(acc_3.address, acc_3.make_account_public_mask());
PublicSCContext {
caller_address,
caller_balance: 100,
account_masks,
comitment_store_root,
commitments_tree,
}
}
#[test]
fn bin_ser_stability_test() {
let test_context = create_test_context();
let serialization_1 = serde_json::to_vec(&test_context).unwrap();
let serialization_2 = serde_json::to_vec(&test_context).unwrap();
assert_eq!(serialization_1, serialization_2);
}
#[test]
fn correct_u64_production_from_fit_vec() {
let le_vec = vec![1, 1, 1, 1, 2, 1, 1, 1];
let num = PublicSCContext::produce_u64_from_fit_vec(le_vec);
assert_eq!(num, 72340177133043969);
}
#[test]
fn correct_u64_production_from_small_vec() {
//7 items instead of 8
let le_vec = vec![1, 1, 1, 1, 2, 1, 1];
let num = PublicSCContext::produce_u64_from_fit_vec(le_vec);
assert_eq!(num, 282583095116033);
}
#[test]
fn correct_u64_production_from_small_vec_le_bytes() {
//7 items instead of 8
let le_vec = vec![1, 1, 1, 1, 2, 1, 1];
let le_vec_res = [1, 1, 1, 1, 2, 1, 1, 0];
let num = PublicSCContext::produce_u64_from_fit_vec(le_vec);
assert_eq!(num.to_le_bytes(), le_vec_res);
}
#[test]
#[should_panic]
fn correct_u64_production_from_unfit_vec_should_panic() {
//9 items instead of 8
let le_vec = vec![1, 1, 1, 1, 2, 1, 1, 1, 1];
PublicSCContext::produce_u64_from_fit_vec(le_vec);
}
#[test]
fn consistent_len_of_context_commitments() {
let test_context = create_test_context();
let context_num_vec1 = test_context.produce_u64_list_from_context().unwrap();
let context_num_vec2 = test_context.produce_u64_list_from_context().unwrap();
assert_eq!(context_num_vec1.len(), context_num_vec2.len());
}
}

View File

@ -1,100 +0,0 @@
use accounts::{account_core::Account, key_management::ephemeral_key_holder::EphemeralKeyHolder};
use anyhow::Result;
use common::transaction::{TransactionBody, TxKind};
use rand::thread_rng;
use risc0_zkvm::Receipt;
use secp256k1_zkp::{CommitmentSecrets, PedersenCommitment, Tweak};
use utxo::utxo_core::UTXO;
use crate::proofs_circuits::{commit, generate_nullifiers, tag_random};
pub fn create_public_transaction_payload(
execution_input: Vec<u8>,
commitment: Vec<PedersenCommitment>,
tweak: Tweak,
secret_r: [u8; 32],
sc_addr: String,
) -> TransactionBody {
TransactionBody {
tx_kind: TxKind::Public,
execution_input,
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![],
commitment,
tweak,
secret_r,
sc_addr,
}
}
pub fn encode_utxos_to_receivers(
utxos_receivers: Vec<(UTXO, &Account)>,
) -> Vec<(Vec<u8>, Vec<u8>)> {
let mut all_encoded_data = vec![];
for (utxo, receiver) in utxos_receivers {
let ephm_key_holder = EphemeralKeyHolder::new_os_random();
let encoded_data = Account::encrypt_data(
&ephm_key_holder,
receiver.key_holder.viewing_public_key,
&serde_json::to_vec(&utxo).unwrap(),
);
let encoded_data_vec = (encoded_data.0, encoded_data.1.to_vec());
all_encoded_data.push(encoded_data_vec);
}
all_encoded_data
}
pub fn generate_nullifiers_spent_utxos(utxos_spent: Vec<(UTXO, &Account)>) -> Vec<Vec<u8>> {
let mut all_nullifiers = vec![];
for (utxo, spender) in utxos_spent {
let nullifier = generate_nullifiers(
&utxo,
&spender
.key_holder
.utxo_secret_key_holder
.nullifier_secret_key
.to_bytes(),
);
all_nullifiers.push(nullifier);
}
all_nullifiers
}
pub fn encode_receipt(receipt: Receipt) -> Result<String> {
Ok(hex::encode(serde_json::to_vec(&receipt)?))
}
pub fn generate_secret_random_commitment(
value: u64,
account: &Account,
) -> Result<PedersenCommitment> {
let commitment_secrets = CommitmentSecrets {
value,
value_blinding_factor: Tweak::from_slice(
&account
.key_holder
.utxo_secret_key_holder
.viewing_secret_key
.to_bytes(),
)?,
generator_blinding_factor: Tweak::new(&mut thread_rng()),
};
let tag = tag_random();
let commitment = commit(&commitment_secrets, tag);
Ok(commitment)
}

View File

@ -1,9 +1,8 @@
use std::path::Path;
use accounts::account_core::address::AccountAddress;
use block_store::SequecerBlockStore;
use common::block::HashableBlockData;
use nssa;
use nssa::{self, Address};
use rand::{rngs::OsRng, RngCore};
use crate::config::AccountInitialData;
@ -22,17 +21,9 @@ impl SequecerChainStore {
is_genesis_random: bool,
initial_accounts: &[AccountInitialData],
) -> Self {
let init_accs: Vec<(AccountAddress, u128)> = initial_accounts
let init_accs: Vec<(Address, u128)> = initial_accounts
.iter()
.map(|acc_data| {
(
hex::decode(acc_data.addr.clone())
.unwrap()
.try_into()
.unwrap(),
acc_data.balance,
)
})
.map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance))
.collect();
let state = nssa::V01State::new_with_genesis_accounts(&init_accs);

View File

@ -23,18 +23,12 @@ hex.workspace = true
actix-rt.workspace = true
clap.workspace = true
[dependencies.sc_core]
path = "../sc_core"
[dependencies.accounts]
path = "../accounts"
[dependencies.utxo]
path = "../utxo"
[dependencies.zkvm]
path = "../zkvm"
[dependencies.nssa]
path = "../nssa"

View File

@ -1,8 +1,9 @@
use accounts::account_core::{address::AccountAddress, Account};
use accounts::account_core::Account;
use nssa::Address;
use std::collections::HashMap;
pub struct WalletAccountsStore {
pub accounts: HashMap<AccountAddress, Account>,
pub accounts: HashMap<Address, Account>,
}
impl WalletAccountsStore {
@ -16,7 +17,7 @@ impl WalletAccountsStore {
self.accounts.insert(account.address, account);
}
pub fn unregister_account(&mut self, account_addr: AccountAddress) {
pub fn unregister_account(&mut self, account_addr: Address) {
self.accounts.remove(&account_addr);
}
}
@ -82,7 +83,7 @@ mod tests {
let mut store = WalletAccountsStore::new();
let account_addr: [u8; 32] = pad_to_32("nonexistent".to_string().as_bytes());
store.unregister_account(account_addr);
store.unregister_account(Address::new(account_addr));
assert!(store.accounts.is_empty());
}

View File

@ -1,9 +1,9 @@
use std::collections::{BTreeMap, HashMap};
use std::collections::HashMap;
use accounts::account_core::{address::AccountAddress, Account};
use accounts::account_core::Account;
use anyhow::Result;
use common::merkle_tree_public::merkle_tree::UTXOCommitmentsMerkleTree;
use sc_core::public_context::PublicSCContext;
use nssa::Address;
use serde::{Deserialize, Serialize};
use crate::config::WalletConfig;
@ -37,7 +37,7 @@ impl From<AccMap> for HashMap<[u8; 32], Account> {
}
pub struct WalletChainStore {
pub acc_map: HashMap<AccountAddress, Account>,
pub acc_map: HashMap<Address, Account>,
pub utxo_commitments_store: UTXOCommitmentsMerkleTree,
pub wallet_config: WalletConfig,
}
@ -53,22 +53,6 @@ impl WalletChainStore {
wallet_config: config,
})
}
pub fn produce_context(&self, caller: AccountAddress) -> PublicSCContext {
let mut account_masks = BTreeMap::new();
for (acc_addr, acc) in &self.acc_map {
account_masks.insert(*acc_addr, acc.make_account_public_mask());
}
PublicSCContext {
caller_address: caller,
caller_balance: self.acc_map.get(&caller).unwrap().balance,
account_masks,
comitment_store_root: self.utxo_commitments_store.get_root().unwrap_or([0; 32]),
commitments_tree: self.utxo_commitments_store.clone(),
}
}
}
#[cfg(test)]
@ -80,8 +64,9 @@ mod tests {
fn create_initial_accounts() -> Vec<Account> {
let initial_acc1 = serde_json::from_str(r#"{
"address": [27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143],
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f",
"balance": 100,
"nonce": 0,
"key_holder": {
"nullifer_public_key": "03A340BECA9FAAB444CED0140681D72EA1318B5C611704FEE017DA9836B17DB718",
"pub_account_signing_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
@ -98,8 +83,9 @@ mod tests {
}"#).unwrap();
let initial_acc2 = serde_json::from_str(r#"{
"address": [77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102],
"address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766",
"balance": 200,
"nonce": 0,
"key_holder": {
"nullifer_public_key": "02172F50274DE67C4087C344F5D58E11DF761D90285B095060E0994FAA6BCDE271",
"pub_account_signing_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],

View File

@ -2,7 +2,6 @@ use std::path::PathBuf;
use accounts::account_core::Account;
use serde::{Deserialize, Serialize};
use zkvm::gas_calculator::GasCalculator;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GasConfig {
@ -22,20 +21,6 @@ pub struct GasConfig {
pub gas_limit_runtime: u64,
}
impl From<GasConfig> for zkvm::gas_calculator::GasCalculator {
fn from(value: GasConfig) -> Self {
GasCalculator::new(
value.gas_fee_per_byte_deploy,
value.gas_fee_per_input_buffer_runtime,
value.gas_fee_per_byte_runtime,
value.gas_cost_runtime,
value.gas_cost_deploy,
value.gas_limit_deploy,
value.gas_limit_runtime,
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WalletConfig {
///Home dir of sequencer storage

View File

@ -1,6 +1,8 @@
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
use anyhow::{anyhow, Result};
use accounts::account_core::Account;
use anyhow::Result;
use nssa::Address;
use crate::{config::WalletConfig, HOME_DIR_ENV_VAR};
@ -19,8 +21,27 @@ pub fn fetch_config() -> Result<WalletConfig> {
}
//ToDo: Replace with structures conversion in future
pub fn produce_account_addr_from_hex(hex_str: String) -> Result<[u8; 32]> {
hex::decode(hex_str)?
.try_into()
.map_err(|_| anyhow!("Failed conversion to 32 bytes"))
pub fn produce_account_addr_from_hex(hex_str: String) -> Result<Address> {
Ok(hex_str.parse()?)
}
///Fetch list of accounts stored at `NSSA_WALLET_HOME_DIR/curr_accounts.json`
///
/// If file not present, it is considered as empty list of persistent accounts
pub fn fetch_persistent_accounts() -> Result<Vec<Account>> {
let home = get_home()?;
let accs_path = home.join("curr_accounts.json");
match File::open(accs_path) {
Ok(file) => {
let reader = BufReader::new(file);
Ok(serde_json::from_reader(reader)?)
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => Ok(vec![]),
_ => {
anyhow::bail!("IO error {err:#?}");
}
},
}
}

View File

@ -5,16 +5,18 @@ use common::{
ExecutionFailureKind,
};
use accounts::account_core::{address::AccountAddress, Account};
use accounts::account_core::Account;
use anyhow::Result;
use chain_storage::WalletChainStore;
use config::WalletConfig;
use log::info;
use tokio::sync::RwLock;
use nssa::Address;
use clap::{Parser, Subcommand};
use crate::helperfunctions::{fetch_config, produce_account_addr_from_hex};
use crate::helperfunctions::{
fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex,
};
pub const HOME_DIR_ENV_VAR: &str = "NSSA_WALLET_HOME_DIR";
pub const BLOCK_GEN_DELAY_SECS: u64 = 20;
@ -24,7 +26,7 @@ pub mod config;
pub mod helperfunctions;
pub struct WalletCore {
pub storage: Arc<RwLock<WalletChainStore>>,
pub storage: WalletChainStore,
pub wallet_config: WalletConfig,
pub sequencer_client: Arc<SequencerClient>,
}
@ -38,64 +40,60 @@ impl WalletCore {
storage.acc_map.insert(acc.address, acc);
}
let wrapped_storage = Arc::new(RwLock::new(storage));
//Persistent accounts take precedence for initial accounts
let persistent_accounts = fetch_persistent_accounts()?;
for acc in persistent_accounts {
storage.acc_map.insert(acc.address, acc);
}
Ok(Self {
storage: wrapped_storage,
storage,
wallet_config: config.clone(),
sequencer_client: client.clone(),
})
}
pub async fn create_new_account(&mut self) -> AccountAddress {
pub async fn create_new_account(&mut self) -> Address {
let account = Account::new();
account.log();
let addr = account.address;
{
let mut write_guard = self.storage.write().await;
write_guard.acc_map.insert(account.address, account);
}
self.storage.acc_map.insert(account.address, account);
addr
}
pub async fn send_public_native_token_transfer(
&self,
from: AccountAddress,
from: Address,
nonce: u128,
to: AccountAddress,
to: Address,
balance_to_move: u128,
) -> Result<SendTxResponse, ExecutionFailureKind> {
{
let read_guard = self.storage.read().await;
let account = self.storage.acc_map.get(&from);
let account = read_guard.acc_map.get(&from);
if let Some(account) = account {
let addresses = vec![from, to];
let nonces = vec![nonce];
let program_id = nssa::program::Program::authenticated_transfer_program().id();
let message = nssa::public_transaction::Message::try_new(
program_id,
addresses,
nonces,
balance_to_move,
)
.unwrap();
if let Some(account) = account {
let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
let nonces = vec![nonce];
let program_id = nssa::program::Program::authenticated_transfer_program().id();
let message = nssa::public_transaction::Message::try_new(
program_id,
addresses,
nonces,
balance_to_move,
)
.unwrap();
let signing_key = account.key_holder.get_pub_account_signing_key();
let witness_set =
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
let signing_key = account.key_holder.get_pub_account_signing_key();
let witness_set =
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
let tx = nssa::PublicTransaction::new(message, witness_set);
let tx = nssa::PublicTransaction::new(message, witness_set);
Ok(self.sequencer_client.send_tx(tx).await?)
} else {
Err(ExecutionFailureKind::AmountMismatchError)
}
Ok(self.sequencer_client.send_tx(tx).await?)
} else {
Err(ExecutionFailureKind::AmountMismatchError)
}
}
}
@ -104,6 +102,7 @@ impl WalletCore {
#[derive(Subcommand, Debug, Clone)]
#[clap(about)]
pub enum Command {
///Send native token transfer from `from` to `to` for `amount`
SendNativeTokenTransfer {
///from - valid 32 byte hex string
#[arg(long)]
@ -149,6 +148,8 @@ pub async fn execute_subcommand(command: Command) -> Result<()> {
.await?;
info!("Results of tx send is {res:#?}");
//ToDo: Insert transaction polling logic here
}
}

View File

@ -1,30 +0,0 @@
[package]
name = "zkvm"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow.workspace = true
serde_json.workspace = true
env_logger.workspace = true
log.workspace = true
serde.workspace = true
thiserror.workspace = true
rand.workspace = true
risc0-zkvm = "2.3.1"
test-methods = { path = "test_methods" }
[dependencies.accounts]
path = "../accounts"
[dependencies.utxo]
path = "../utxo"
[dependencies.common]
path = "../common"
[features]
cuda = ["risc0-zkvm/cuda"]
default = []
prove = ["risc0-zkvm/prove"]

View File

@ -1,113 +0,0 @@
#[derive(Debug, Clone)]
pub struct GasCalculator {
/// Gas spent per deploying one byte of data
gas_fee_per_byte_deploy: u64,
/// Gas spent per reading one byte of data in VM
gas_fee_per_input_buffer_runtime: u64,
/// Gas spent per one byte of contract data in runtime
gas_fee_per_byte_runtime: u64,
/// Cost of one gas of runtime in public balance
gas_cost_runtime: u64,
/// Cost of one gas of deployment in public balance
gas_cost_deploy: u64,
/// Gas limit for deployment
gas_limit_deploy: u64,
/// Gas limit for runtime
gas_limit_runtime: u64,
}
impl GasCalculator {
pub fn new(
gas_fee_per_byte_deploy: u64,
gas_fee_per_input_buffer_runtime: u64,
gas_fee_per_byte_runtime: u64,
gas_cost_runtime: u64,
gas_cost_deploy: u64,
gas_limit_deploy: u64,
gas_limit_runtime: u64,
) -> Self {
Self {
gas_fee_per_byte_deploy,
gas_fee_per_input_buffer_runtime,
gas_fee_per_byte_runtime,
gas_cost_deploy,
gas_cost_runtime,
gas_limit_deploy,
gas_limit_runtime,
}
}
pub fn gas_fee_per_byte_deploy(&self) -> u64 {
self.gas_fee_per_byte_deploy
}
pub fn gas_fee_per_input_buffer_runtime(&self) -> u64 {
self.gas_fee_per_input_buffer_runtime
}
pub fn gas_fee_per_byte_runtime(&self) -> u64 {
self.gas_fee_per_byte_runtime
}
pub fn gas_cost_runtime(&self) -> u64 {
self.gas_cost_runtime
}
pub fn gas_cost_deploy(&self) -> u64 {
self.gas_cost_deploy
}
pub fn gas_limit_deploy(&self) -> u64 {
self.gas_limit_deploy
}
pub fn gas_limit_runtime(&self) -> u64 {
self.gas_limit_runtime
}
///Returns Option<u64>
///
/// Some(_) - in case if `gas` < `gas_limit_deploy`
///
/// None - else
pub fn gas_deploy(&self, elf: &[u8]) -> Option<u64> {
let gas = self.gas_fee_per_byte_deploy() * (elf.len() as u64);
if gas < self.gas_limit_deploy() {
Some(gas)
} else {
None
}
}
pub fn gas_runtime(&self, elf: &[u8]) -> u64 {
self.gas_fee_per_byte_runtime() * (elf.len() as u64)
}
pub fn gas_input_buffer(&self, input_length: usize) -> u64 {
self.gas_fee_per_input_buffer_runtime() * (input_length as u64)
}
///Returns Option<u64>
///
/// Some(_) - in case if `gas` < `gas_limit_runtime`
///
/// None - else
pub fn gas_runtime_full(&self, elf: &[u8], input_length: usize) -> Option<u64> {
let gas = self.gas_runtime(elf) + self.gas_input_buffer(input_length);
if gas < self.gas_limit_runtime() {
Some(gas)
} else {
None
}
}
pub fn deploy_cost(&self, deploy_gas: u64) -> u64 {
deploy_gas * self.gas_cost_deploy()
}
pub fn runtime_cost(&self, runtime_gas: u64) -> u64 {
runtime_gas * self.gas_cost_runtime()
}
}

View File

@ -1,653 +0,0 @@
use accounts::account_core::address::AccountAddress;
use common::ExecutionFailureKind;
use rand::{rngs::OsRng, RngCore};
use risc0_zkvm::{default_executor, default_prover, sha::Digest, ExecutorEnv, Receipt};
use serde::Serialize;
use utxo::utxo_core::{Randomness, UTXOPayload, UTXO};
pub mod gas_calculator;
pub use test_methods;
#[allow(clippy::result_large_err)]
pub fn gas_limits_check<INP: Serialize>(
input_buffer: INP,
elf: &[u8],
gas_calculator: &gas_calculator::GasCalculator,
attached_funds: u64,
) -> Result<(), ExecutionFailureKind> {
let mut input_buffer_len: usize = 0;
input_buffer_len += serde_json::to_vec(&input_buffer).unwrap().len();
let gas_limit = gas_calculator
.gas_runtime_full(elf, input_buffer_len)
.ok_or(ExecutionFailureKind::InsufficientGasError)?;
let cost = gas_calculator.runtime_cost(gas_limit);
if cost > attached_funds {
return Err(ExecutionFailureKind::InsufficientFundsError);
}
Ok(())
}
#[allow(clippy::result_large_err)]
pub fn prove_mint_utxo(
amount_to_mint: u128,
owner: AccountAddress,
) -> Result<(UTXO, Receipt), ExecutionFailureKind> {
let mut builder = ExecutorEnv::builder();
builder
.write(&amount_to_mint)
.map_err(ExecutionFailureKind::write_error)?;
builder
.write(&owner)
.map_err(ExecutionFailureKind::write_error)?;
let mut randomness = Randomness::default();
OsRng.fill_bytes(&mut randomness);
builder
.write(&randomness)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::MINT_UTXO_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: UTXOPayload = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((UTXO::create_utxo_from_payload(digest), receipt))
}
#[allow(clippy::result_large_err)]
pub fn prove_send_utxo(
spent_utxo: UTXO,
owners_parts: Vec<(u128, AccountAddress)>,
) -> Result<(Vec<(UTXO, AccountAddress)>, Receipt), ExecutionFailureKind> {
let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0);
if cumulative_spent != spent_utxo.amount {
return Err(ExecutionFailureKind::AmountMismatchError);
}
let mut builder = ExecutorEnv::builder();
let utxo_payload = spent_utxo.into_payload();
builder
.write(&utxo_payload)
.map_err(ExecutionFailureKind::write_error)?;
let owners_parts_with_randomness = owners_parts
.into_iter()
.map(|(amount, addr)| {
let mut randomness = Randomness::default();
OsRng.fill_bytes(&mut randomness);
(amount, addr, randomness)
})
.collect::<Vec<_>>();
builder
.write(&owners_parts_with_randomness)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::SEND_UTXO_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: Vec<(UTXOPayload, AccountAddress)> = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((
digest
.into_iter()
.map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr))
.collect(),
receipt,
))
}
#[allow(clippy::result_large_err)]
pub fn prove_send_utxo_multiple_assets_one_receiver(
spent_utxos: Vec<UTXO>,
number_to_send: usize,
receiver: AccountAddress,
) -> Result<(Vec<UTXO>, Vec<UTXO>, Receipt), ExecutionFailureKind> {
if number_to_send > spent_utxos.len() {
return Err(ExecutionFailureKind::AmountMismatchError);
}
let mut builder = ExecutorEnv::builder();
let utxo_payload: Vec<UTXOPayload> = spent_utxos
.into_iter()
.map(|spent_utxo| spent_utxo.into_payload())
.collect();
builder
.write(&utxo_payload)
.map_err(ExecutionFailureKind::write_error)?;
builder
.write(&number_to_send)
.map_err(ExecutionFailureKind::write_error)?;
builder
.write(&receiver)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::SEND_UTXO_MULTIPLE_ASSETS_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: (Vec<UTXOPayload>, Vec<UTXOPayload>) = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((
digest
.0
.into_iter()
.map(UTXO::create_utxo_from_payload)
.collect(),
digest
.1
.into_iter()
.map(UTXO::create_utxo_from_payload)
.collect(),
receipt,
))
}
#[allow(clippy::result_large_err)]
pub fn prove_send_utxo_shielded(
owner: AccountAddress,
amount: u128,
owners_parts: Vec<(u128, AccountAddress)>,
) -> Result<(Vec<(UTXO, AccountAddress)>, Receipt), ExecutionFailureKind> {
let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0);
if cumulative_spent != amount {
return Err(ExecutionFailureKind::AmountMismatchError);
}
let temp_utxo_to_spend = UTXO::new(owner, vec![], amount, true);
let utxo_payload = temp_utxo_to_spend.into_payload();
let mut builder = ExecutorEnv::builder();
builder
.write(&utxo_payload)
.map_err(ExecutionFailureKind::write_error)?;
let owners_parts_with_randomness = owners_parts
.into_iter()
.map(|(amount, addr)| {
let mut randomness = Randomness::default();
OsRng.fill_bytes(&mut randomness);
(amount, addr, randomness)
})
.collect::<Vec<_>>();
builder
.write(&owners_parts_with_randomness)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::SEND_UTXO_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: Vec<(UTXOPayload, AccountAddress)> = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((
digest
.into_iter()
.map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr))
.collect(),
receipt,
))
}
#[allow(clippy::result_large_err)]
pub fn prove_send_utxo_deshielded(
spent_utxo: UTXO,
owners_parts: Vec<(u128, AccountAddress)>,
) -> Result<(Vec<(u128, AccountAddress)>, Receipt), ExecutionFailureKind> {
let cumulative_spent = owners_parts.iter().fold(0, |acc, item| acc + item.0);
if cumulative_spent != spent_utxo.amount {
return Err(ExecutionFailureKind::AmountMismatchError);
}
let mut builder = ExecutorEnv::builder();
let utxo_payload = spent_utxo.into_payload();
builder
.write(&utxo_payload)
.map_err(ExecutionFailureKind::write_error)?;
let owners_parts_with_randomness = owners_parts
.into_iter()
.map(|(amount, addr)| {
let mut randomness = Randomness::default();
OsRng.fill_bytes(&mut randomness);
(amount, addr, randomness)
})
.collect::<Vec<_>>();
builder
.write(&owners_parts_with_randomness)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::SEND_UTXO_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: Vec<(UTXOPayload, AccountAddress)> = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((
digest
.into_iter()
.map(|(payload, addr)| (payload.amount, addr))
.collect(),
receipt,
))
}
#[allow(clippy::result_large_err)]
pub fn prove_mint_utxo_multiple_assets(
amount_to_mint: u128,
number_of_assets: usize,
owner: AccountAddress,
) -> Result<(Vec<UTXO>, Receipt), ExecutionFailureKind> {
let mut builder = ExecutorEnv::builder();
builder
.write(&amount_to_mint)
.map_err(ExecutionFailureKind::write_error)?;
builder
.write(&number_of_assets)
.map_err(ExecutionFailureKind::write_error)?;
builder
.write(&owner)
.map_err(ExecutionFailureKind::write_error)?;
let env = builder
.build()
.map_err(ExecutionFailureKind::builder_error)?;
let prover = default_prover();
let receipt = prover
.prove(env, test_methods::MINT_UTXO_MULTIPLE_ASSETS_ELF)
.map_err(ExecutionFailureKind::prove_error)?
.receipt;
let digest: Vec<UTXOPayload> = receipt
.journal
.decode()
.map_err(|e| ExecutionFailureKind::DecodeError(e.to_string()))?;
Ok((
digest
.into_iter()
.map(UTXO::create_utxo_from_payload)
.collect(),
receipt,
))
}
pub fn execute_mint_utxo(
amount_to_mint: u128,
owner: AccountAddress,
randomness: [u8; 32],
) -> anyhow::Result<UTXO> {
let mut builder = ExecutorEnv::builder();
builder.write(&amount_to_mint)?;
builder.write(&owner)?;
builder.write(&randomness)?;
let env = builder.build()?;
let executor = default_executor();
let receipt = executor.execute(env, test_methods::MINT_UTXO_ELF)?;
let digest: UTXOPayload = receipt.journal.decode()?;
Ok(UTXO::create_utxo_from_payload(digest))
}
pub fn execute_send_utxo(
spent_utxo: UTXO,
owners_parts: Vec<(u128, AccountAddress)>,
) -> anyhow::Result<(UTXO, Vec<(UTXO, AccountAddress)>)> {
let mut builder = ExecutorEnv::builder();
let utxo_payload = spent_utxo.into_payload();
builder.write(&utxo_payload)?;
let owners_parts_with_randomness = owners_parts
.into_iter()
.map(|(amount, addr)| {
let mut randomness = Randomness::default();
OsRng.fill_bytes(&mut randomness);
(amount, addr, randomness)
})
.collect::<Vec<_>>();
builder.write(&owners_parts_with_randomness)?;
let env = builder.build()?;
let executor = default_executor();
let receipt = executor.execute(env, test_methods::SEND_UTXO_ELF)?;
let digest: (UTXOPayload, Vec<(UTXOPayload, AccountAddress)>) = receipt.journal.decode()?;
Ok((
UTXO::create_utxo_from_payload(digest.0),
digest
.1
.into_iter()
.map(|(payload, addr)| (UTXO::create_utxo_from_payload(payload), addr))
.collect(),
))
}
pub fn prove<T: serde::ser::Serialize>(
input_vec: Vec<T>,
elf: &[u8],
) -> anyhow::Result<(u64, Receipt)> {
let mut builder = ExecutorEnv::builder();
for input in input_vec {
builder.write(&input)?;
}
let env = builder.build()?;
let prover = default_prover();
let receipt = prover.prove(env, elf)?.receipt;
let digest = receipt.journal.decode()?;
Ok((digest, receipt))
}
// This only executes the program and does not generate a receipt.
pub fn execute<T: serde::ser::Serialize + for<'de> serde::Deserialize<'de>>(
input_vec: Vec<T>,
elf: &[u8],
) -> anyhow::Result<T> {
let mut builder = ExecutorEnv::builder();
for input in input_vec {
builder.write(&input)?;
}
let env = builder.build()?;
let exec = default_executor();
let session = exec.execute(env, elf)?;
// We read the result committed to the journal by the guest code.
let result: T = session.journal.decode()?;
Ok(result)
}
pub fn verify(receipt: Receipt, image_id: impl Into<Digest>) -> anyhow::Result<()> {
Ok(receipt.verify(image_id)?)
}
#[cfg(test)]
mod tests {
use crate::gas_calculator::GasCalculator;
use super::*;
use test_methods::BIG_CALCULATION_ELF;
use test_methods::{MULTIPLICATION_ELF, MULTIPLICATION_ID};
use test_methods::{SUMMATION_ELF, SUMMATION_ID};
#[test]
fn prove_simple_sum() {
let message = 1;
let message_2 = 2;
let (digest, receipt) = prove(vec![message, message_2], SUMMATION_ELF).unwrap();
verify(receipt, SUMMATION_ID).unwrap();
assert_eq!(digest, message + message_2);
}
#[test]
fn prove_bigger_sum() {
let message = 123476;
let message_2 = 2342384;
let (digest, receipt) = prove(vec![message, message_2], SUMMATION_ELF).unwrap();
verify(receipt, SUMMATION_ID).unwrap();
assert_eq!(digest, message + message_2);
}
#[test]
fn prove_simple_multiplication() {
let message = 1;
let message_2 = 2;
let (digest, receipt) = prove(vec![message, message_2], MULTIPLICATION_ELF).unwrap();
verify(receipt, MULTIPLICATION_ID).unwrap();
assert_eq!(digest, message * message_2);
}
#[test]
fn prove_bigger_multiplication() {
let message = 3498;
let message_2 = 438563;
let (digest, receipt) = prove(vec![message, message_2], MULTIPLICATION_ELF).unwrap();
verify(receipt, MULTIPLICATION_ID).unwrap();
assert_eq!(digest, message * message_2);
}
#[test]
fn execute_simple_sum() {
let message: u64 = 1;
let message_2: u64 = 2;
let result = execute(vec![message, message_2], SUMMATION_ELF).unwrap();
assert_eq!(result, message + message_2);
}
#[test]
fn execute_bigger_sum() {
let message: u64 = 123476;
let message_2: u64 = 2342384;
let result = execute(vec![message, message_2], SUMMATION_ELF).unwrap();
assert_eq!(result, message + message_2);
}
#[test]
fn execute_big_calculation() {
let message: u128 = 1;
let message_2: u128 = 2;
let result = execute(vec![message, message_2], BIG_CALCULATION_ELF).unwrap();
assert_eq!(result, big_calculation(message, message_2));
}
#[test]
fn execute_big_calculation_long() {
let message: u128 = 20;
let message_2: u128 = 10;
let result = execute(vec![message, message_2], BIG_CALCULATION_ELF).unwrap();
assert_eq!(result, big_calculation(message, message_2));
}
fn big_calculation(lhs: u128, rhs: u128) -> u128 {
let mut res = 1_u128;
for _ in 0..lhs {
res *= rhs;
res += lhs;
}
res
}
#[test]
fn test_gas_limits_check_sufficient_funds() {
let message = 1;
let message_2 = 2;
let gas_calc = GasCalculator::new(1, 1, 1, 1, 1, 1000000, 1000000);
let result = gas_limits_check(vec![message, message_2], SUMMATION_ELF, &gas_calc, 1000000);
assert!(result.is_ok());
}
#[test]
fn test_gas_limits_check_insufficient_funds() {
let message = 1;
let message_2 = 2;
let gas_calc = GasCalculator::new(1, 1, 1, 1, 1, 1000000, 1000000);
let result = gas_limits_check(vec![message, message_2], SUMMATION_ELF, &gas_calc, 1);
assert!(matches!(
result,
Err(ExecutionFailureKind::InsufficientFundsError)
));
}
#[test]
fn test_execute_mint_utxo() {
let owner = AccountAddress::default();
let amount = 123456789;
let mut randomness = [0u8; 32];
OsRng.fill_bytes(&mut randomness);
let utxo_exec = execute_mint_utxo(amount, owner, randomness).expect("execution failed");
assert_eq!(utxo_exec.amount, amount);
assert_eq!(utxo_exec.owner, owner);
}
#[test]
fn test_prove_mint_utxo() {
let owner = AccountAddress::default();
let amount = 123456789;
let (utxo, _) = prove_mint_utxo(amount, owner).expect("proof failed");
assert_eq!(utxo.amount, amount);
assert_eq!(utxo.owner, owner);
}
#[test]
fn test_prove_send_utxo() {
let owner = AccountAddress::default();
let amount = 100;
let (input_utxo, _) = prove_mint_utxo(amount, owner).expect("mint failed");
let parts = vec![(40, owner), (60, owner)];
let (outputs, _receipt) = prove_send_utxo(input_utxo, parts.clone()).expect("send failed");
let total: u128 = outputs.iter().map(|(utxo, _)| utxo.amount).sum();
assert_eq!(total, amount);
assert_eq!(outputs.len(), 2);
}
#[test]
fn test_prove_send_utxo_deshielded() {
let owner = AccountAddress::default();
let amount = 100;
let (utxo, _) = prove_mint_utxo(amount, owner).unwrap();
let parts = vec![(60, owner), (40, owner)];
let (outputs, _) = prove_send_utxo_deshielded(utxo, parts.clone()).unwrap();
let total: u128 = outputs.iter().map(|(amt, _)| amt).sum();
assert_eq!(total, amount);
assert_eq!(outputs.len(), 2);
}
#[test]
fn test_prove_send_utxo_shielded() {
let owner = AccountAddress::default();
let amount = 100;
let parts = vec![(60, owner), (40, owner)];
let (outputs, _) = prove_send_utxo_shielded(owner, amount, parts.clone()).unwrap();
let total: u128 = outputs.iter().map(|(utxo, _)| utxo.amount).sum();
assert_eq!(total, amount);
assert_eq!(outputs.len(), 2);
}
#[test]
fn test_prove_send_utxo_multiple_assets_one_receiver() {
let owner = AccountAddress::default();
let receiver = AccountAddress::default();
let utxos = vec![
prove_mint_utxo(100, owner).unwrap().0,
prove_mint_utxo(50, owner).unwrap().0,
];
let (to_receiver, to_change, _receipt) =
prove_send_utxo_multiple_assets_one_receiver(utxos, 1, receiver).unwrap();
let total_to_receiver: u128 = to_receiver.iter().map(|u| u.amount).sum();
assert!(total_to_receiver > 0);
assert_eq!(to_receiver.len() + to_change.len(), 2);
}
}

View File

@ -1,10 +0,0 @@
[package]
name = "test-methods"
version = "0.1.0"
edition = "2021"
[build-dependencies]
risc0-build = { version = "2.3.1" }
[package.metadata.risc0]
methods = ["guest"]

View File

@ -1,3 +0,0 @@
fn main() {
risc0_build::embed_methods();
}

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
[package]
name = "test"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
serde_json = "1.0.81"
risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] }
[dependencies.serde]
features = ["derive"]
version = "1.0.60"

View File

@ -1,14 +0,0 @@
use risc0_zkvm::{
guest::env,
};
fn main() {
let lhs: u128 = env::read();
let rhs: u128 = env::read();
let mut res = 1;
for i in 0..lhs {
res *= rhs;
res += lhs;
}
env::commit(&(res));
}

View File

@ -1,32 +0,0 @@
use risc0_zkvm::{
guest::env,
};
use serde::{Deserialize, Serialize};
type AccountAddr = [u8; 32];
#[derive(Serialize, Deserialize)]
pub struct UTXOPayload {
pub owner: AccountAddr,
pub asset: Vec<u8>,
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
pub randomness: [u8; 32],
}
fn main() {
let amount_to_mint: u128 = env::read();
let owner: AccountAddr = env::read();
let randomness: [u8; 32] = env::read();
let payload = UTXOPayload {
owner,
asset: vec![],
amount: amount_to_mint,
privacy_flag: true,
randomness,
};
env::commit(&(payload));
}

View File

@ -1,39 +0,0 @@
use risc0_zkvm::{
guest::env,
};
use serde::{Deserialize, Serialize};
type AccountAddr = [u8; 32];
#[derive(Serialize, Deserialize)]
pub struct UTXOPayload {
pub owner: AccountAddr,
pub asset: Vec<u8>,
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
pub randomness: [u8; 32],
}
fn main() {
let amount_to_mint: u128 = env::read();
let number_of_assets: usize = env::read();
let owner: AccountAddr = env::read();
let randomness: [u8; 32] = env::read();
let mut asseted_utxos = vec![];
for i in 0..number_of_assets {
let payload = UTXOPayload {
owner,
asset: vec![i as u8],
amount: amount_to_mint,
privacy_flag: true,
randomness
};
asseted_utxos.push(payload);
}
env::commit(&(asseted_utxos));
}

View File

@ -1,9 +0,0 @@
use risc0_zkvm::{
guest::env,
};
fn main() {
let lhs: u64 = env::read();
let rhs: u64 = env::read();
env::commit(&(lhs * rhs));
}

View File

@ -1,34 +0,0 @@
use risc0_zkvm::{
guest::env,
};
use serde::{Deserialize, Serialize};
type AccountAddr = [u8; 32];
#[derive(Serialize, Deserialize)]
pub struct UTXOPayload {
pub owner: AccountAddr,
pub asset: Vec<u8>,
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
pub randomness: [u8; 32],
}
fn main() {
let utxo_spent: UTXOPayload = env::read();
let owners_parts: Vec<(u128, AccountAddr, [u8; 32])> = env::read();
let res: Vec<(UTXOPayload, AccountAddr)> = owners_parts.into_iter().map(|(amount, addr, randomness)| (
UTXOPayload {
owner: addr.clone(),
asset: vec![],
amount,
privacy_flag: true,
randomness,
},
addr
)).collect();
env::commit(&(res));
}

View File

@ -1,41 +0,0 @@
use risc0_zkvm::{
guest::env,
};
use serde::{Deserialize, Serialize};
type AccountAddr = [u8; 32];
#[derive(Clone, Serialize, Deserialize)]
pub struct UTXOPayload {
pub owner: AccountAddr,
pub asset: Vec<u8>,
// TODO: change to u256
pub amount: u128,
pub privacy_flag: bool,
pub randomness: [u8; 32],
}
fn main() {
let utxo_spent: Vec<UTXOPayload> = env::read();
let number_to_send = env::read();
let receiver: AccountAddr = env::read();
let mut utxo_received = vec![];
let mut utxo_not_spent = vec![];
for i in 0..utxo_spent.len() {
let mut utxo_payload = utxo_spent[i].clone();
if i < number_to_send {
utxo_payload.owner = receiver;
utxo_received.push(utxo_payload);
} else {
utxo_payload.asset.push(0);
utxo_not_spent.push(utxo_payload);
}
}
env::commit(&(utxo_received, utxo_not_spent));
}

View File

@ -1,9 +0,0 @@
use risc0_zkvm::{
guest::env,
};
fn main() {
let data: u64 = env::read();
let data_2: u64 = env::read();
env::commit(&(data + data_2));
}

View File

@ -1 +0,0 @@
include!(concat!(env!("OUT_DIR"), "/methods.rs"));