Merge branch 'main' into schouhy/add-check-for-repeated-account-ids-in-circuit

This commit is contained in:
Sergio Chouhy 2025-10-07 09:49:53 -03:00
commit ed9682bf58
42 changed files with 938 additions and 1443 deletions

View File

@ -41,9 +41,10 @@ ark-bn254 = "0.5.0"
ark-ff = "0.5.0" ark-ff = "0.5.0"
tiny-keccak = { version = "2.0.2", features = ["keccak"] } tiny-keccak = { version = "2.0.2", features = ["keccak"] }
base64 = "0.22.1" base64 = "0.22.1"
chrono = "0.4.41"
bip39 = "2.2.0" bip39 = "2.2.0"
hmac-sha512 = "1.1.7" hmac-sha512 = "1.1.7"
chrono = "0.4.41"
borsh = "1.5.7"
rocksdb = { version = "0.21.0", default-features = false, features = [ rocksdb = { version = "0.21.0", default-features = false, features = [
"snappy", "snappy",

View File

@ -17,10 +17,7 @@ log.workspace = true
elliptic-curve.workspace = true elliptic-curve.workspace = true
hex.workspace = true hex.workspace = true
nssa-core = { path = "../nssa/core", features = ["host"] } nssa-core = { path = "../nssa/core", features = ["host"] }
borsh.workspace = true
[dependencies.secp256k1-zkp]
workspace = true
features = ["std", "rand-std", "rand", "serde", "global-context"]
[dependencies.nssa] [dependencies.nssa]
path = "../nssa" path = "../nssa"

View File

@ -1,5 +1,5 @@
use borsh::{BorshDeserialize, BorshSerialize};
use rs_merkle::Hasher; use rs_merkle::Hasher;
use std::io::{Cursor, Read};
use crate::{OwnHasher, transaction::EncodedTransaction}; use crate::{OwnHasher, transaction::EncodedTransaction};
@ -27,7 +27,7 @@ pub struct Block {
pub body: BlockBody, pub body: BlockBody,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub struct HashableBlockData { pub struct HashableBlockData {
pub block_id: BlockId, pub block_id: BlockId,
pub prev_block_hash: BlockHash, pub prev_block_hash: BlockHash,
@ -37,7 +37,7 @@ pub struct HashableBlockData {
impl HashableBlockData { impl HashableBlockData {
pub fn into_block(self, signing_key: &nssa::PrivateKey) -> Block { pub fn into_block(self, signing_key: &nssa::PrivateKey) -> Block {
let data_bytes = self.to_bytes(); let data_bytes = borsh::to_vec(&self).unwrap();
let signature = nssa::Signature::new(signing_key, &data_bytes); let signature = nssa::Signature::new(signing_key, &data_bytes);
let hash = OwnHasher::hash(&data_bytes); let hash = OwnHasher::hash(&data_bytes);
Block { Block {
@ -66,75 +66,6 @@ impl From<Block> for HashableBlockData {
} }
} }
impl HashableBlockData {
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.block_id.to_le_bytes());
bytes.extend_from_slice(&self.prev_block_hash);
bytes.extend_from_slice(&self.timestamp.to_le_bytes());
let num_transactions: u32 = self.transactions.len() as u32;
bytes.extend_from_slice(&num_transactions.to_le_bytes());
for tx in &self.transactions {
let transaction_bytes = tx.to_bytes();
let num_transaction_bytes: u32 = transaction_bytes.len() as u32;
bytes.extend_from_slice(&num_transaction_bytes.to_le_bytes());
bytes.extend_from_slice(&tx.to_bytes());
}
bytes
}
// TODO: Improve error handling. Remove unwraps.
pub fn from_bytes(data: &[u8]) -> Self {
let mut cursor = Cursor::new(data);
let block_id = u64_from_cursor(&mut cursor);
let mut prev_block_hash = [0u8; 32];
cursor.read_exact(&mut prev_block_hash).unwrap();
let timestamp = u64_from_cursor(&mut cursor);
let num_transactions = u32_from_cursor(&mut cursor) as usize;
let mut transactions = Vec::with_capacity(num_transactions);
for _ in 0..num_transactions {
let tx_len = u32_from_cursor(&mut cursor) as usize;
let mut tx_bytes = Vec::with_capacity(tx_len);
for _ in 0..tx_len {
let mut buff = [0; 1];
cursor.read_exact(&mut buff).unwrap();
tx_bytes.push(buff[0]);
}
let tx = EncodedTransaction::from_bytes(tx_bytes);
transactions.push(tx);
}
Self {
block_id,
prev_block_hash,
timestamp,
transactions,
}
}
}
// TODO: Improve error handling. Remove unwraps.
pub fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> u32 {
let mut word_buf = [0u8; 4];
cursor.read_exact(&mut word_buf).unwrap();
u32::from_le_bytes(word_buf)
}
// TODO: Improve error handling. Remove unwraps.
pub fn u64_from_cursor(cursor: &mut Cursor<&[u8]>) -> u64 {
let mut word_buf = [0u8; 8];
cursor.read_exact(&mut word_buf).unwrap();
u64::from_le_bytes(word_buf)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{block::HashableBlockData, test_utils}; use crate::{block::HashableBlockData, test_utils};
@ -144,8 +75,8 @@ mod tests {
let transactions = vec![test_utils::produce_dummy_empty_transaction()]; let transactions = vec![test_utils::produce_dummy_empty_transaction()];
let block = test_utils::produce_dummy_block(1, Some([1; 32]), transactions); let block = test_utils::produce_dummy_block(1, Some([1; 32]), transactions);
let hashable = HashableBlockData::from(block); let hashable = HashableBlockData::from(block);
let bytes = hashable.to_bytes(); let bytes = borsh::to_vec(&hashable).unwrap();
let block_from_bytes = HashableBlockData::from_bytes(&bytes); let block_from_bytes = borsh::from_slice::<HashableBlockData>(&bytes).unwrap();
assert_eq!(hashable, block_from_bytes); assert_eq!(hashable, block_from_bytes);
} }
} }

View File

@ -54,7 +54,7 @@ pub struct GetAccountRequest {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetProofByCommitmentRequest { pub struct GetProofForCommitmentRequest {
pub commitment: nssa_core::Commitment, pub commitment: nssa_core::Commitment,
} }
@ -68,7 +68,7 @@ parse_request!(GetInitialTestnetAccountsRequest);
parse_request!(GetAccountBalanceRequest); parse_request!(GetAccountBalanceRequest);
parse_request!(GetTransactionByHashRequest); parse_request!(GetTransactionByHashRequest);
parse_request!(GetAccountsNoncesRequest); parse_request!(GetAccountsNoncesRequest);
parse_request!(GetProofByCommitmentRequest); parse_request!(GetProofForCommitmentRequest);
parse_request!(GetAccountRequest); parse_request!(GetAccountRequest);
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -123,6 +123,6 @@ pub struct GetAccountResponse {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GetProofByCommitmentResponse { pub struct GetProofForCommitmentResponse {
pub membership_proof: Option<nssa_core::MembershipProof>, pub membership_proof: Option<nssa_core::MembershipProof>,
} }

View File

@ -9,7 +9,7 @@ use serde_json::Value;
use crate::rpc_primitives::requests::{ use crate::rpc_primitives::requests::{
GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
GetProofByCommitmentRequest, GetProofByCommitmentResponse, GetTransactionByHashRequest, GetProofForCommitmentRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest,
GetTransactionByHashResponse, GetTransactionByHashResponse,
}; };
use crate::sequencer_client::json::AccountInitialData; use crate::sequencer_client::json::AccountInitialData;
@ -151,7 +151,7 @@ impl SequencerClient {
let transaction = EncodedTransaction::from(NSSATransaction::Public(transaction)); let transaction = EncodedTransaction::from(NSSATransaction::Public(transaction));
let tx_req = SendTxRequest { let tx_req = SendTxRequest {
transaction: transaction.to_bytes(), transaction: borsh::to_vec(&transaction).unwrap(),
}; };
let req = serde_json::to_value(tx_req)?; let req = serde_json::to_value(tx_req)?;
@ -171,7 +171,7 @@ impl SequencerClient {
let transaction = EncodedTransaction::from(NSSATransaction::PrivacyPreserving(transaction)); let transaction = EncodedTransaction::from(NSSATransaction::PrivacyPreserving(transaction));
let tx_req = SendTxRequest { let tx_req = SendTxRequest {
transaction: transaction.to_bytes(), transaction: borsh::to_vec(&transaction).unwrap(),
}; };
let req = serde_json::to_value(tx_req)?; let req = serde_json::to_value(tx_req)?;
@ -222,7 +222,7 @@ impl SequencerClient {
&self, &self,
commitment: nssa_core::Commitment, commitment: nssa_core::Commitment,
) -> Result<Option<nssa_core::MembershipProof>, SequencerClientError> { ) -> Result<Option<nssa_core::MembershipProof>, SequencerClientError> {
let acc_req = GetProofByCommitmentRequest { commitment }; let acc_req = GetProofForCommitmentRequest { commitment };
let req = serde_json::to_value(acc_req).unwrap(); let req = serde_json::to_value(acc_req).unwrap();
@ -231,7 +231,7 @@ impl SequencerClient {
.await .await
.unwrap(); .unwrap();
let resp_deser = serde_json::from_value::<GetProofByCommitmentResponse>(resp) let resp_deser = serde_json::from_value::<GetProofForCommitmentResponse>(resp)
.unwrap() .unwrap()
.membership_proof; .membership_proof;

View File

@ -1,3 +1,4 @@
use borsh::{BorshDeserialize, BorshSerialize};
use k256::ecdsa::{Signature, SigningKey, VerifyingKey}; use k256::ecdsa::{Signature, SigningKey, VerifyingKey};
use log::info; use log::info;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -34,13 +35,15 @@ pub type CipherText = Vec<u8>;
pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>; pub type Nonce = GenericArray<u8, UInt<UInt<UInt<UInt<UTerm, B1>, B1>, B0>, B0>>;
pub type Tag = u8; pub type Tag = u8;
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] #[derive(
Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize,
)]
pub enum TxKind { pub enum TxKind {
Public, Public,
PrivacyPreserving, PrivacyPreserving,
} }
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
///General transaction object ///General transaction object
pub struct EncodedTransaction { pub struct EncodedTransaction {
pub tx_kind: TxKind, pub tx_kind: TxKind,
@ -174,23 +177,12 @@ impl ActionData {
impl EncodedTransaction { impl EncodedTransaction {
/// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`. /// Computes and returns the SHA-256 hash of the JSON-serialized representation of `self`.
pub fn hash(&self) -> TreeHashType { pub fn hash(&self) -> TreeHashType {
let bytes_to_hash = self.to_bytes(); let bytes_to_hash = borsh::to_vec(&self).unwrap();
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
hasher.update(&bytes_to_hash); hasher.update(&bytes_to_hash);
TreeHashType::from(hasher.finalize_fixed()) TreeHashType::from(hasher.finalize_fixed())
} }
pub fn to_bytes(&self) -> Vec<u8> {
// TODO: Remove `unwrap` by implementing a `to_bytes` method
// that deterministically encodes all transaction fields to bytes
// and guarantees serialization will succeed.
serde_json::to_vec(&self).unwrap()
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
serde_json::from_slice(&bytes).unwrap()
}
pub fn log(&self) { pub fn log(&self) {
info!("Transaction hash is {:?}", hex::encode(self.hash())); info!("Transaction hash is {:?}", hex::encode(self.hash()));
info!("Transaction tx_kind is {:?}", self.tx_kind); info!("Transaction tx_kind is {:?}", self.tx_kind);
@ -221,7 +213,7 @@ mod tests {
fn test_transaction_hash_is_sha256_of_json_bytes() { fn test_transaction_hash_is_sha256_of_json_bytes() {
let body = test_transaction_body(); let body = test_transaction_body();
let expected_hash = { let expected_hash = {
let data = body.to_bytes(); let data = borsh::to_vec(&body).unwrap();
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
hasher.update(&data); hasher.update(&data);
TreeHashType::from(hasher.finalize_fixed()) TreeHashType::from(hasher.finalize_fixed())
@ -236,8 +228,8 @@ mod tests {
fn test_to_bytes_from_bytes() { fn test_to_bytes_from_bytes() {
let body = test_transaction_body(); let body = test_transaction_body();
let body_bytes = body.to_bytes(); let body_bytes = borsh::to_vec(&body).unwrap();
let body_new = EncodedTransaction::from_bytes(body_bytes); let body_new = borsh::from_slice::<EncodedTransaction>(&body_bytes).unwrap();
assert_eq!(body, body_new); assert_eq!(body, body_new);
} }

View File

@ -8,12 +8,12 @@ anyhow.workspace = true
env_logger.workspace = true env_logger.workspace = true
log.workspace = true log.workspace = true
actix.workspace = true actix.workspace = true
bytemuck = "1.23.2"
actix-web.workspace = true actix-web.workspace = true
base64.workspace = true
tokio.workspace = true tokio.workspace = true
hex.workspace = true hex.workspace = true
tempfile.workspace = true tempfile.workspace = true
borsh.workspace = true
nssa-core = { path = "../nssa/core", features = ["host"] } nssa-core = { path = "../nssa/core", features = ["host"] }

View File

@ -8,11 +8,11 @@
"port": 3040, "port": 3040,
"initial_accounts": [ "initial_accounts": [
{ {
"addr": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "addr": "0eee24287296ba55278f1e5403be014754866366388730303c2889be17ada065",
"balance": 10000 "balance": 10000
}, },
{ {
"addr": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "addr": "9e3d8e654d440e95293aa2dceceb137899a59535e952f747068e7a0ee30965f2",
"balance": 20000 "balance": 20000
} }
], ],
@ -154,4 +154,4 @@
37, 37,
37 37
] ]
} }

View File

@ -9,7 +9,7 @@
"initial_accounts": [ "initial_accounts": [
{ {
"Public": { "Public": {
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "address": "0eee24287296ba55278f1e5403be014754866366388730303c2889be17ada065",
"pub_sign_key": [ "pub_sign_key": [
1, 1,
1, 1,
@ -48,7 +48,7 @@
}, },
{ {
"Public": { "Public": {
"address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "address": "9e3d8e654d440e95293aa2dceceb137899a59535e952f747068e7a0ee30965f2",
"pub_sign_key": [ "pub_sign_key": [
2, 2,
2, 2,
@ -87,7 +87,7 @@
}, },
{ {
"Private": { "Private": {
"address": "6ffe0893c4b2c956fdb769b11fe4e3b2dd36ac4bd0ad90c810844051747c8c04", "address": "9cb6b0035320266e430eac9d96745769e7efcf30d2b9cc21ff000b3f873dc2a8",
"account": { "account": {
"program_owner": [ "program_owner": [
0, 0,
@ -316,7 +316,7 @@
}, },
{ {
"Private": { "Private": {
"address": "4ee9de60e33da96fd72929f1485fb365bcc9c1634dd44e4ba55b1ab96692674b", "address": "a55f4f98d2f265c91d8a9868564242d8070b9bf7180a29363f52eb76988636fd",
"account": { "account": {
"program_owner": [ "program_owner": [
0, 0,
@ -544,4 +544,4 @@
} }
} }
] ]
} }

View File

@ -1,12 +1,18 @@
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use std::{path::PathBuf, time::Duration}; use std::{path::PathBuf, time::Duration};
use actix_web::dev::ServerHandle; use actix_web::dev::ServerHandle;
use anyhow::Result; use anyhow::Result;
use clap::Parser; use clap::Parser;
use common::sequencer_client::SequencerClient; use common::{
sequencer_client::SequencerClient,
transaction::{EncodedTransaction, NSSATransaction},
};
use log::{info, warn}; use log::{info, warn};
use nssa::program::Program; use nssa::{Address, PrivacyPreservingTransaction, program::Program};
use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; use nssa_core::{
Commitment, NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point,
};
use sequencer_core::config::SequencerConfig; use sequencer_core::config::SequencerConfig;
use sequencer_runner::startup_sequencer; use sequencer_runner::startup_sequencer;
use tempfile::TempDir; use tempfile::TempDir;
@ -14,7 +20,7 @@ use tokio::task::JoinHandle;
use wallet::{ use wallet::{
Command, SubcommandReturnValue, WalletCore, Command, SubcommandReturnValue, WalletCore,
config::PersistentAccountData, config::PersistentAccountData,
helperfunctions::{fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex}, helperfunctions::{fetch_config, fetch_persistent_accounts},
}; };
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -26,13 +32,13 @@ struct Args {
test_name: String, test_name: String,
} }
pub const ACC_SENDER: &str = "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; pub const ACC_SENDER: &str = "0eee24287296ba55278f1e5403be014754866366388730303c2889be17ada065";
pub const ACC_RECEIVER: &str = "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766"; pub const ACC_RECEIVER: &str = "9e3d8e654d440e95293aa2dceceb137899a59535e952f747068e7a0ee30965f2";
pub const ACC_SENDER_PRIVATE: &str = pub const ACC_SENDER_PRIVATE: &str =
"6ffe0893c4b2c956fdb769b11fe4e3b2dd36ac4bd0ad90c810844051747c8c04"; "9cb6b0035320266e430eac9d96745769e7efcf30d2b9cc21ff000b3f873dc2a8";
pub const ACC_RECEIVER_PRIVATE: &str = pub const ACC_RECEIVER_PRIVATE: &str =
"4ee9de60e33da96fd72929f1485fb365bcc9c1634dd44e4ba55b1ab96692674b"; "a55f4f98d2f265c91d8a9868564242d8070b9bf7180a29363f52eb76988636fd";
pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
@ -389,10 +395,7 @@ pub async fn test_success_token_program() {
// Bytes from 1 to 33 represent the id of the token this account is associated with. // Bytes from 1 to 33 represent the id of the token this account is associated with.
// In this example, this is a token account of the newly created token, so it is expected // In this example, this is a token account of the newly created token, so it is expected
// to be equal to the address of the token definition account. // to be equal to the address of the token definition account.
assert_eq!( assert_eq!(&supply_acc.data[1..33], definition_addr.to_bytes());
&supply_acc.data[1..33],
nssa::AccountId::from(&definition_addr).to_bytes()
);
assert_eq!( assert_eq!(
u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()),
37 37
@ -419,10 +422,7 @@ pub async fn test_success_token_program() {
// First byte equal to 1 means it's a token holding account // First byte equal to 1 means it's a token holding account
assert_eq!(supply_acc.data[0], 1); assert_eq!(supply_acc.data[0], 1);
// Bytes from 1 to 33 represent the id of the token this account is associated with. // Bytes from 1 to 33 represent the id of the token this account is associated with.
assert_eq!( assert_eq!(&supply_acc.data[1..33], definition_addr.to_bytes());
&supply_acc.data[1..33],
nssa::AccountId::from(&definition_addr).to_bytes()
);
assert_eq!( assert_eq!(
u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()),
30 30
@ -440,10 +440,7 @@ pub async fn test_success_token_program() {
// First byte equal to 1 means it's a token holding account // First byte equal to 1 means it's a token holding account
assert_eq!(recipient_acc.data[0], 1); assert_eq!(recipient_acc.data[0], 1);
// Bytes from 1 to 33 represent the id of the token this account is associated with. // Bytes from 1 to 33 represent the id of the token this account is associated with.
assert_eq!( assert_eq!(&recipient_acc.data[1..33], definition_addr.to_bytes());
&recipient_acc.data[1..33],
nssa::AccountId::from(&definition_addr).to_bytes()
);
assert_eq!( assert_eq!(
u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()),
7 7
@ -452,159 +449,118 @@ pub async fn test_success_token_program() {
pub async fn test_success_private_transfer_to_another_owned_account() { pub async fn test_success_private_transfer_to_another_owned_account() {
info!("test_success_private_transfer_to_another_owned_account"); info!("test_success_private_transfer_to_another_owned_account");
let command = Command::SendNativeTokenTransferPrivate { let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
from: ACC_SENDER_PRIVATE.to_string(), let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap();
to: ACC_RECEIVER_PRIVATE.to_string(),
let command = Command::SendNativeTokenTransferPrivateOwnedAccount {
from: from.to_string(),
to: to.to_string(),
amount: 100, amount: 100,
}; };
let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
let to = produce_account_addr_from_hex(ACC_RECEIVER_PRIVATE.to_string()).unwrap(); wallet::execute_subcommand(command).await.unwrap()
else {
let wallet_config = fetch_config().unwrap(); panic!("invalid subcommand return value");
};
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
wallet::execute_subcommand(command).await.unwrap();
info!("Waiting for next block creation"); info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = { let command = Command::FetchPrivateAccount {
let from_acc = wallet_storage tx_hash: tx_hash.clone(),
.storage acc_addr: from.to_string(),
.user_data output_id: 0,
.get_private_account_mut(&from)
.unwrap();
from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
from_acc.1.balance -= 100;
from_acc.1.nonce += 1;
nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1)
}; };
wallet::execute_subcommand(command).await.unwrap();
let new_commitment2 = { let command = Command::FetchPrivateAccount {
let to_acc = wallet_storage tx_hash,
.storage acc_addr: to.to_string(),
.user_data output_id: 1,
.get_private_account_mut(&to)
.unwrap();
to_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
to_acc.1.balance += 100;
to_acc.1.nonce += 1;
nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1)
}; };
wallet::execute_subcommand(command).await.unwrap();
let proof1 = seq_client let wallet_config = fetch_config().unwrap();
.get_proof_for_commitment(new_commitment1) let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
.await let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
.unwrap()
.unwrap();
let proof2 = seq_client
.get_proof_for_commitment(new_commitment2)
.await
.unwrap()
.unwrap();
println!("New proof is {proof1:#?}"); let new_commitment1 = wallet_storage
println!("New proof is {proof2:#?}"); .get_private_account_commitment(&from)
.unwrap();
assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await);
let new_commitment2 = wallet_storage.get_private_account_commitment(&to).unwrap();
assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await);
info!("Success!"); info!("Success!");
} }
pub async fn test_success_private_transfer_to_another_foreign_account() { pub async fn test_success_private_transfer_to_another_foreign_account() {
info!("test_success_private_transfer_to_another_foreign_account"); info!("test_success_private_transfer_to_another_foreign_account");
let to_npk_orig = NullifierPublicKey([42; 32]); let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let to_npk = hex::encode(to_npk_orig.0); let to_npk = NullifierPublicKey([42; 32]);
let to_ipk = Secp256k1Point::from_scalar(to_npk_orig.0); let to_npk_string = hex::encode(to_npk.0);
let to_ipk = Secp256k1Point::from_scalar(to_npk.0);
let command = Command::SendNativeTokenTransferPrivateForeignAccount { let command = Command::SendNativeTokenTransferPrivateForeignAccount {
from: ACC_SENDER_PRIVATE.to_string(), from: from.to_string(),
to_npk, to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0), to_ipk: hex::encode(to_ipk.0),
amount: 100, amount: 100,
}; };
let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
wallet::execute_subcommand(command).await.unwrap()
let wallet_config = fetch_config().unwrap(); else {
panic!("invalid subcommand return value");
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); };
let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
println!("SUB RET is {sub_ret:#?}");
info!("Waiting for next block creation"); info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = { let command = Command::FetchPrivateAccount {
let from_acc = wallet_storage tx_hash: tx_hash.clone(),
.storage acc_addr: from.to_string(),
.user_data output_id: 0,
.get_private_account_mut(&from)
.unwrap();
from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
from_acc.1.balance -= 100;
from_acc.1.nonce += 1;
nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1)
}; };
wallet::execute_subcommand(command).await.unwrap();
let new_commitment2 = { let wallet_config = fetch_config().unwrap();
let to_acc = nssa_core::account::Account { let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
program_owner: nssa::program::Program::authenticated_transfer_program().id(), let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
balance: 100,
data: vec![],
nonce: 1,
};
nssa_core::Commitment::new(&to_npk_orig, &to_acc) let new_commitment1 = wallet_storage
}; .get_private_account_commitment(&from)
let proof1 = seq_client
.get_proof_for_commitment(new_commitment1)
.await
.unwrap()
.unwrap();
let proof2 = seq_client
.get_proof_for_commitment(new_commitment2)
.await
.unwrap()
.unwrap(); .unwrap();
println!("New proof is {proof1:#?}"); let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await;
println!("New proof is {proof2:#?}"); assert_eq!(tx.message.new_commitments[0], new_commitment1);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments.into_iter() {
assert!(verify_commitment_is_in_state(commitment, &seq_client).await);
}
info!("Success!"); info!("Success!");
} }
pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() { pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() {
info!("test_success_private_transfer_to_another_owned_account_claiming_path"); info!("test_success_private_transfer_to_another_owned_account_claiming_path");
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let command = Command::RegisterAccountPrivate {}; let command = Command::RegisterAccountPrivate {};
let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
panic!("FAILED TO REGISTER ACCOUNT"); panic!("FAILED TO REGISTER ACCOUNT");
}; };
let wallet_config = fetch_config().unwrap(); let wallet_config = fetch_config().unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let mut wallet_storage = let (to_keys, _) = wallet_storage
WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let (to_keys, mut to_acc) = wallet_storage
.storage .storage
.user_data .user_data
.user_private_accounts .user_private_accounts
@ -613,74 +569,38 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
.unwrap(); .unwrap();
let command = Command::SendNativeTokenTransferPrivateForeignAccount { let command = Command::SendNativeTokenTransferPrivateForeignAccount {
from: ACC_SENDER_PRIVATE.to_string(), from: from.to_string(),
to_npk: hex::encode(to_keys.nullifer_public_key.0), to_npk: hex::encode(to_keys.nullifer_public_key.0),
to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0),
amount: 100, amount: 100,
}; };
let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap();
let sub_ret = wallet::execute_subcommand(command).await.unwrap(); let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else {
panic!("FAILED TO SEND TX"); panic!("FAILED TO SEND TX");
}; };
info!("Waiting for next block creation"); let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await;
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = { let command = Command::FetchPrivateAccount {
let from_acc = wallet_storage
.storage
.user_data
.get_private_account_mut(&from)
.unwrap();
from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
from_acc.1.balance -= 100;
from_acc.1.nonce += 1;
nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1)
};
let new_commitment2 = {
to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id();
to_acc.balance = 100;
to_acc.nonce = 1;
nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc)
};
let proof1 = seq_client
.get_proof_for_commitment(new_commitment1)
.await
.unwrap()
.unwrap();
let proof2 = seq_client
.get_proof_for_commitment(new_commitment2)
.await
.unwrap()
.unwrap();
println!("New proof is {proof1:#?}");
println!("New proof is {proof2:#?}");
let command = Command::ClaimPrivateAccount {
tx_hash, tx_hash,
acc_addr: hex::encode(to_addr), acc_addr: to_addr.to_string(),
ciph_id: 1, output_id: 1,
}; };
wallet::execute_subcommand(command).await.unwrap(); wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let (_, to_res_acc) = wallet_storage let new_commitment1 = wallet_storage
.storage .get_private_account_commitment(&from)
.user_data
.get_private_account(&to_addr)
.unwrap(); .unwrap();
assert_eq!(tx.message.new_commitments[0], new_commitment1);
assert_eq!(tx.message.new_commitments.len(), 2);
for commitment in tx.message.new_commitments.into_iter() {
assert!(verify_commitment_is_in_state(commitment, &seq_client).await);
}
let to_res_acc = wallet_storage.get_account_private(&to_addr).unwrap();
assert_eq!(to_res_acc.balance, 100); assert_eq!(to_res_acc.balance, 100);
@ -689,51 +609,50 @@ pub async fn test_success_private_transfer_to_another_owned_account_claiming_pat
pub async fn test_success_deshielded_transfer_to_another_account() { pub async fn test_success_deshielded_transfer_to_another_account() {
info!("test_success_deshielded_transfer_to_another_account"); info!("test_success_deshielded_transfer_to_another_account");
let from: Address = ACC_SENDER_PRIVATE.parse().unwrap();
let to: Address = ACC_RECEIVER.parse().unwrap();
let command = Command::SendNativeTokenTransferDeshielded { let command = Command::SendNativeTokenTransferDeshielded {
from: ACC_SENDER_PRIVATE.to_string(), from: from.to_string(),
to: ACC_RECEIVER.to_string(), to: to.to_string(),
amount: 100, amount: 100,
}; };
let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap();
let wallet_config = fetch_config().unwrap(); let wallet_config = fetch_config().unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); let from_acc = wallet_storage.get_account_private(&from).unwrap();
assert_eq!(from_acc.balance, 10000);
wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
wallet::execute_subcommand(command).await.unwrap()
else {
panic!("invalid subcommand return value");
};
info!("Waiting for next block creation"); info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment1 = { let command = Command::FetchPrivateAccount {
let from_acc = wallet_storage tx_hash,
.storage acc_addr: from.to_string(),
.user_data output_id: 0,
.get_private_account_mut(&from)
.unwrap();
from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
from_acc.1.balance -= 100;
from_acc.1.nonce += 1;
nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1)
}; };
wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let proof1 = seq_client let from_acc = wallet_storage.get_account_private(&from).unwrap();
.get_proof_for_commitment(new_commitment1) let new_commitment = wallet_storage
.await .get_private_account_commitment(&from)
.unwrap()
.unwrap(); .unwrap();
assert!(verify_commitment_is_in_state(new_commitment, &seq_client).await);
let acc_2_balance = seq_client let acc_2_balance = seq_client
.get_account_balance(ACC_RECEIVER.to_string()) .get_account_balance(to.to_string())
.await .await
.unwrap(); .unwrap();
println!("New proof is {proof1:#?}"); assert_eq!(from_acc.balance, 10000 - 100);
assert_eq!(acc_2_balance.balance, 20100); assert_eq!(acc_2_balance.balance, 20100);
info!("Success!"); info!("Success!");
@ -741,66 +660,59 @@ pub async fn test_success_deshielded_transfer_to_another_account() {
pub async fn test_success_shielded_transfer_to_another_owned_account() { pub async fn test_success_shielded_transfer_to_another_owned_account() {
info!("test_success_shielded_transfer_to_another_owned_account"); info!("test_success_shielded_transfer_to_another_owned_account");
let from: Address = ACC_SENDER.parse().unwrap();
let to: Address = ACC_RECEIVER_PRIVATE.parse().unwrap();
let command = Command::SendNativeTokenTransferShielded { let command = Command::SendNativeTokenTransferShielded {
from: ACC_SENDER.to_string(), from: from.to_string(),
to: ACC_RECEIVER_PRIVATE.to_string(), to: to.to_string(),
amount: 100, amount: 100,
}; };
let to = produce_account_addr_from_hex(ACC_RECEIVER_PRIVATE.to_string()).unwrap();
let wallet_config = fetch_config().unwrap(); let wallet_config = fetch_config().unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
wallet::execute_subcommand(command).await.unwrap()
wallet::execute_subcommand(command).await.unwrap(); else {
panic!("invalid subcommand return value");
};
info!("Waiting for next block creation"); info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment2 = { let command = Command::FetchPrivateAccount {
let to_acc = wallet_storage tx_hash,
.storage acc_addr: to.to_string(),
.user_data output_id: 0,
.get_private_account_mut(&to)
.unwrap();
to_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id();
to_acc.1.balance += 100;
to_acc.1.nonce += 1;
nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1)
}; };
wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let acc_1_balance = seq_client let acc_to = wallet_storage.get_account_private(&to).unwrap();
.get_account_balance(ACC_SENDER.to_string()) let new_commitment = wallet_storage.get_private_account_commitment(&to).unwrap();
assert!(verify_commitment_is_in_state(new_commitment, &seq_client).await);
let acc_from_balance = seq_client
.get_account_balance(from.to_string())
.await .await
.unwrap(); .unwrap();
let proof2 = seq_client assert_eq!(acc_from_balance.balance, 9900);
.get_proof_for_commitment(new_commitment2) assert_eq!(acc_to.balance, 20000 + 100);
.await
.unwrap()
.unwrap();
assert_eq!(acc_1_balance.balance, 9900);
println!("New proof is {proof2:#?}");
info!("Success!"); info!("Success!");
} }
pub async fn test_success_shielded_transfer_to_another_foreign_account() { pub async fn test_success_shielded_transfer_to_another_foreign_account() {
info!("test_success_shielded_transfer_to_another_foreign_account"); info!("test_success_shielded_transfer_to_another_foreign_account");
let to_npk_orig = NullifierPublicKey([42; 32]); let to_npk = NullifierPublicKey([42; 32]);
let to_npk = hex::encode(to_npk_orig.0); let to_npk_string = hex::encode(to_npk.0);
let to_ipk = Secp256k1Point::from_scalar(to_npk_orig.0); let to_ipk = Secp256k1Point::from_scalar(to_npk.0);
let from: Address = ACC_SENDER.parse().unwrap();
let command = Command::SendNativeTokenTransferShieldedForeignAccount { let command = Command::SendNativeTokenTransferShieldedForeignAccount {
from: ACC_SENDER.to_string(), from: from.to_string(),
to_npk, to_npk: to_npk_string,
to_ipk: hex::encode(to_ipk.0), to_ipk: hex::encode(to_ipk.0),
amount: 100, amount: 100,
}; };
@ -809,118 +721,27 @@ pub async fn test_success_shielded_transfer_to_another_foreign_account() {
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
wallet::execute_subcommand(command).await.unwrap(); let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } =
wallet::execute_subcommand(command).await.unwrap()
info!("Waiting for next block creation"); else {
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; panic!("invalid subcommand return value");
let new_commitment2 = {
let to_acc = nssa_core::account::Account {
program_owner: nssa::program::Program::authenticated_transfer_program().id(),
balance: 100,
data: vec![],
nonce: 1,
};
nssa_core::Commitment::new(&to_npk_orig, &to_acc)
};
let acc_1_balance = seq_client
.get_account_balance(ACC_SENDER.to_string())
.await
.unwrap();
let proof2 = seq_client
.get_proof_for_commitment(new_commitment2)
.await
.unwrap()
.unwrap();
assert_eq!(acc_1_balance.balance, 9900);
println!("New proof is {proof2:#?}");
info!("Success!");
}
pub async fn test_success_shielded_transfer_to_another_owned_account_claiming_path() {
info!("test_success_shielded_transfer_to_another_owned_account_claiming_path");
let command = Command::RegisterAccountPrivate {};
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else {
panic!("FAILED TO REGISTER ACCOUNT");
};
let wallet_config = fetch_config().unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap();
let (to_keys, mut to_acc) = wallet_storage
.storage
.user_data
.user_private_accounts
.get(&to_addr)
.cloned()
.unwrap();
let command = Command::SendNativeTokenTransferShieldedForeignAccount {
from: ACC_SENDER.to_string(),
to_npk: hex::encode(to_keys.nullifer_public_key.0),
to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0),
amount: 100,
};
let sub_ret = wallet::execute_subcommand(command).await.unwrap();
let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else {
panic!("FAILED TO SEND TX");
}; };
info!("Waiting for next block creation"); info!("Waiting for next block creation");
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
let new_commitment2 = { let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash).await;
to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id();
to_acc.balance = 100;
to_acc.nonce = 1;
nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc)
};
let acc_1_balance = seq_client let acc_1_balance = seq_client
.get_account_balance(ACC_SENDER.to_string()) .get_account_balance(from.to_string())
.await .await
.unwrap(); .unwrap();
let proof2 = seq_client assert!(
.get_proof_for_commitment(new_commitment2) verify_commitment_is_in_state(tx.message.new_commitments[0].clone(), &seq_client).await
.await );
.unwrap()
.unwrap();
assert_eq!(acc_1_balance.balance, 9900); assert_eq!(acc_1_balance.balance, 9900);
println!("New proof is {proof2:#?}");
let command = Command::ClaimPrivateAccount {
tx_hash,
acc_addr: hex::encode(to_addr),
ciph_id: 0,
};
wallet::execute_subcommand(command).await.unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap();
let (_, to_res_acc) = wallet_storage
.storage
.user_data
.get_private_account(&to_addr)
.unwrap();
assert_eq!(to_res_acc.balance, 100);
info!("Success!"); info!("Success!");
} }
@ -1047,12 +868,6 @@ pub async fn main_tests_runner() -> Result<()> {
test_success_shielded_transfer_to_another_foreign_account test_success_shielded_transfer_to_another_foreign_account
); );
} }
"test_success_shielded_transfer_to_another_owned_account_claiming_path" => {
test_cleanup_wrap!(
home_dir,
test_success_shielded_transfer_to_another_owned_account_claiming_path
);
}
"test_pinata" => { "test_pinata" => {
test_cleanup_wrap!(home_dir, test_pinata); test_cleanup_wrap!(home_dir, test_pinata);
} }
@ -1086,11 +901,33 @@ pub async fn main_tests_runner() -> Result<()> {
home_dir, home_dir,
test_success_private_transfer_to_another_owned_account_claiming_path test_success_private_transfer_to_another_owned_account_claiming_path
); );
test_cleanup_wrap!(home_dir, test_pinata);
}
"all_private" => {
test_cleanup_wrap!( test_cleanup_wrap!(
home_dir, home_dir,
test_success_shielded_transfer_to_another_owned_account_claiming_path test_success_private_transfer_to_another_owned_account
);
test_cleanup_wrap!(
home_dir,
test_success_private_transfer_to_another_foreign_account
);
test_cleanup_wrap!(
home_dir,
test_success_deshielded_transfer_to_another_account
);
test_cleanup_wrap!(
home_dir,
test_success_shielded_transfer_to_another_owned_account
);
test_cleanup_wrap!(
home_dir,
test_success_shielded_transfer_to_another_foreign_account
);
test_cleanup_wrap!(
home_dir,
test_success_private_transfer_to_another_owned_account_claiming_path
); );
test_cleanup_wrap!(home_dir, test_pinata);
} }
_ => { _ => {
anyhow::bail!("Unknown test name"); anyhow::bail!("Unknown test name");
@ -1099,3 +936,37 @@ pub async fn main_tests_runner() -> Result<()> {
Ok(()) Ok(())
} }
async fn fetch_privacy_preserving_tx(
seq_client: &SequencerClient,
tx_hash: String,
) -> PrivacyPreservingTransaction {
let transaction_encoded = seq_client
.get_transaction_by_hash(tx_hash.clone())
.await
.unwrap()
.transaction
.unwrap();
let tx_base64_decode = BASE64.decode(transaction_encoded).unwrap();
match NSSATransaction::try_from(
&borsh::from_slice::<EncodedTransaction>(&tx_base64_decode).unwrap(),
)
.unwrap()
{
NSSATransaction::PrivacyPreserving(privacy_preserving_transaction) => {
privacy_preserving_transaction
}
_ => panic!("Invalid tx type"),
}
}
async fn verify_commitment_is_in_state(
commitment: Commitment,
seq_client: &SequencerClient,
) -> bool {
matches!(
seq_client.get_proof_for_commitment(commitment).await,
Ok(Some(_))
)
}

View File

@ -5,16 +5,13 @@ edition = "2024"
[dependencies] [dependencies]
anyhow.workspace = true anyhow.workspace = true
serde_json.workspace = true
log.workspace = true log.workspace = true
serde.workspace = true serde.workspace = true
k256.workspace = true k256.workspace = true
sha2.workspace = true sha2.workspace = true
rand.workspace = true rand.workspace = true
elliptic-curve.workspace = true
hex.workspace = true hex.workspace = true
aes-gcm.workspace = true aes-gcm.workspace = true
lazy_static.workspace = true
bip39.workspace = true bip39.workspace = true
hmac-sha512.workspace = true hmac-sha512.workspace = true
nssa-core = { path = "../nssa/core", features = ["host"] } nssa-core = { path = "../nssa/core", features = ["host"] }

View File

@ -1,4 +1,3 @@
use log::info;
use nssa_core::{ use nssa_core::{
NullifierPublicKey, SharedSecretKey, NullifierPublicKey, SharedSecretKey,
encryption::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}, encryption::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey},
@ -6,8 +5,6 @@ use nssa_core::{
use rand::{RngCore, rngs::OsRng}; use rand::{RngCore, rngs::OsRng};
use sha2::Digest; use sha2::Digest;
use crate::key_management::secret_holders::OutgoingViewingSecretKey;
#[derive(Debug)] #[derive(Debug)]
///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender. ///Ephemeral secret key holder. Non-clonable as intended for one-time use. Produces ephemeral public keys. Can produce shared secret for sender.
pub struct EphemeralKeyHolder { pub struct EphemeralKeyHolder {
@ -26,21 +23,12 @@ pub fn produce_one_sided_shared_secret_receiver(
} }
impl EphemeralKeyHolder { impl EphemeralKeyHolder {
pub fn new( pub fn new(receiver_nullifier_public_key: &NullifierPublicKey) -> Self {
receiver_nullifier_public_key: NullifierPublicKey, let mut nonce_bytes = [0; 16];
sender_outgoing_viewing_secret_key: OutgoingViewingSecretKey, OsRng.fill_bytes(&mut nonce_bytes);
nonce: u64,
) -> Self {
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
hasher.update(receiver_nullifier_public_key); hasher.update(receiver_nullifier_public_key);
hasher.update(nonce.to_le_bytes()); hasher.update(nonce_bytes);
hasher.update([0; 24]);
let hash_recepient = hasher.finalize();
let mut hasher = sha2::Sha256::new();
hasher.update(sender_outgoing_viewing_secret_key);
hasher.update(hash_recepient);
Self { Self {
ephemeral_secret_key: hasher.finalize().into(), ephemeral_secret_key: hasher.finalize().into(),
@ -53,18 +41,11 @@ impl EphemeralKeyHolder {
pub fn calculate_shared_secret_sender( pub fn calculate_shared_secret_sender(
&self, &self,
receiver_incoming_viewing_public_key: IncomingViewingPublicKey, receiver_incoming_viewing_public_key: &IncomingViewingPublicKey,
) -> SharedSecretKey { ) -> SharedSecretKey {
SharedSecretKey::new( SharedSecretKey::new(
&self.ephemeral_secret_key, &self.ephemeral_secret_key,
&receiver_incoming_viewing_public_key, receiver_incoming_viewing_public_key,
) )
} }
pub fn log(&self) {
info!(
"Ephemeral private key is {:?}",
hex::encode(serde_json::to_vec(&self.ephemeral_secret_key).unwrap())
);
}
} }

View File

@ -1,12 +1,9 @@
use common::TreeHashType;
use log::info;
use nssa_core::{ use nssa_core::{
NullifierPublicKey, SharedSecretKey, NullifierPublicKey, SharedSecretKey,
encryption::{EphemeralPublicKey, IncomingViewingPublicKey}, encryption::{EphemeralPublicKey, IncomingViewingPublicKey},
}; };
use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder}; use secret_holders::{PrivateKeyHolder, SecretSpendingKey, SeedHolder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha2::{Digest, digest::FixedOutput};
pub type PublicAccountSigningKey = [u8; 32]; pub type PublicAccountSigningKey = [u8; 32];
@ -22,18 +19,6 @@ pub struct KeyChain {
pub incoming_viewing_public_key: IncomingViewingPublicKey, pub incoming_viewing_public_key: IncomingViewingPublicKey,
} }
pub fn produce_user_address_foreign_account(
npk: &NullifierPublicKey,
ipk: &IncomingViewingPublicKey,
) -> [u8; 32] {
let mut hasher = sha2::Sha256::new();
hasher.update(npk);
hasher.update(ipk.to_bytes());
<TreeHashType>::from(hasher.finalize_fixed())
}
impl KeyChain { impl KeyChain {
pub fn new_os_random() -> Self { pub fn new_os_random() -> Self {
//Currently dropping SeedHolder at the end of initialization. //Currently dropping SeedHolder at the end of initialization.
@ -54,15 +39,6 @@ impl KeyChain {
} }
} }
pub fn produce_user_address(&self) -> [u8; 32] {
let mut hasher = sha2::Sha256::new();
hasher.update(&self.nullifer_public_key);
hasher.update(self.incoming_viewing_public_key.to_bytes());
<TreeHashType>::from(hasher.finalize_fixed())
}
pub fn calculate_shared_secret_receiver( pub fn calculate_shared_secret_receiver(
&self, &self,
ephemeral_public_key_sender: EphemeralPublicKey, ephemeral_public_key_sender: EphemeralPublicKey,
@ -74,43 +50,13 @@ impl KeyChain {
&ephemeral_public_key_sender, &ephemeral_public_key_sender,
) )
} }
pub fn log(&self) {
info!(
"Secret spending key is {:?}",
hex::encode(serde_json::to_vec(&self.secret_spending_key).unwrap()),
);
info!(
"Nulifier secret key is {:?}",
hex::encode(serde_json::to_vec(&self.private_key_holder.nullifier_secret_key).unwrap()),
);
info!(
"Viewing secret key is {:?}",
hex::encode(
serde_json::to_vec(&self.private_key_holder.incoming_viewing_secret_key).unwrap()
),
);
info!(
"Viewing secret key is {:?}",
hex::encode(
serde_json::to_vec(&self.private_key_holder.outgoing_viewing_secret_key).unwrap()
),
);
info!(
"Nullifier public key is {:?}",
hex::encode(serde_json::to_vec(&self.nullifer_public_key).unwrap()),
);
info!(
"Viewing public key is {:?}",
hex::encode(serde_json::to_vec(&self.incoming_viewing_public_key).unwrap()),
);
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use aes_gcm::aead::OsRng; use aes_gcm::aead::OsRng;
use k256::AffinePoint; use k256::AffinePoint;
use k256::elliptic_curve::group::GroupEncoding;
use rand::RngCore; use rand::RngCore;
use super::*; use super::*;
@ -159,7 +105,7 @@ mod tests {
println!( println!(
"Group generator {:?}", "Group generator {:?}",
hex::encode(serde_json::to_vec(&AffinePoint::GENERATOR).unwrap()) hex::encode(AffinePoint::GENERATOR.to_bytes())
); );
println!(); println!();
@ -176,11 +122,11 @@ mod tests {
println!("Address{:?}", hex::encode(address.value())); println!("Address{:?}", hex::encode(address.value()));
println!( println!(
"Nulifier public key {:?}", "Nulifier public key {:?}",
hex::encode(serde_json::to_vec(&nullifer_public_key).unwrap()) hex::encode(nullifer_public_key.to_byte_array())
); );
println!( println!(
"Viewing public key {:?}", "Viewing public key {:?}",
hex::encode(serde_json::to_vec(&viewing_public_key).unwrap()) hex::encode(viewing_public_key.to_bytes())
); );
} }
} }

View File

@ -22,7 +22,9 @@ impl NSSAUserData {
) -> bool { ) -> bool {
let mut check_res = true; let mut check_res = true;
for (addr, key) in accounts_keys_map { for (addr, key) in accounts_keys_map {
if &nssa::Address::from(&nssa::PublicKey::new_from_private_key(key)) != addr { let expected_addr = nssa::Address::from(&nssa::PublicKey::new_from_private_key(key));
if &expected_addr != addr {
println!("{}, {}", expected_addr, addr);
check_res = false; check_res = false;
} }
} }
@ -34,7 +36,9 @@ impl NSSAUserData {
) -> bool { ) -> bool {
let mut check_res = true; let mut check_res = true;
for (addr, (key, _)) in accounts_keys_map { for (addr, (key, _)) in accounts_keys_map {
if nssa::Address::new(key.produce_user_address()) != *addr { let expected_addr = nssa::Address::from(&key.nullifer_public_key);
if expected_addr != *addr {
println!("{}, {}", expected_addr, addr);
check_res = false; check_res = false;
} }
} }
@ -88,7 +92,7 @@ impl NSSAUserData {
/// Returns the address of new account /// Returns the address of new account
pub fn generate_new_privacy_preserving_transaction_key_chain(&mut self) -> nssa::Address { pub fn generate_new_privacy_preserving_transaction_key_chain(&mut self) -> nssa::Address {
let key_chain = KeyChain::new_os_random(); let key_chain = KeyChain::new_os_random();
let address = nssa::Address::new(key_chain.produce_user_address()); let address = nssa::Address::from(&key_chain.nullifer_public_key);
self.user_private_accounts self.user_private_accounts
.insert(address, (key_chain, nssa_core::account::Account::default())); .insert(address, (key_chain, nssa_core::account::Account::default()));

View File

@ -14,7 +14,6 @@ secp256k1 = "0.31.1"
rand = "0.8" rand = "0.8"
borsh = "1.5.7" borsh = "1.5.7"
hex = "0.4.3" hex = "0.4.3"
k256 = "0.13.3"
[dev-dependencies] [dev-dependencies]
test-program-methods = { path = "test_program_methods" } test-program-methods = { path = "test_program_methods" }

View File

@ -10,7 +10,8 @@ thiserror = { version = "2.0.12", optional = true }
bytemuck = { version = "1.13", optional = true } bytemuck = { version = "1.13", optional = true }
chacha20 = { version = "0.9", default-features = false } chacha20 = { version = "0.9", default-features = false }
k256 = { version = "0.13.3", optional = true } k256 = { version = "0.13.3", optional = true }
hex = { version = "0.4.3", optional = true }
[features] [features]
default = [] default = []
host = ["thiserror", "bytemuck", "k256"] host = ["thiserror", "bytemuck", "k256", "hex"]

View File

@ -1,4 +1,4 @@
use crate::program::ProgramId; use crate::{address::Address, program::ProgramId};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub type Nonce = u128; pub type Nonce = u128;
@ -14,16 +14,7 @@ pub struct Account {
pub nonce: Nonce, pub nonce: Nonce,
} }
/// A fingerprint of the owner of an account. This can be, for example, an `Address` in case the account pub type AccountId = Address;
/// is public, or a `NullifierPublicKey` in case the account is private.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(any(feature = "host", test), derive(Debug))]
pub struct AccountId(pub(super) [u8; 32]);
impl AccountId {
pub fn new(value: [u8; 32]) -> Self {
Self(value)
}
}
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone)]
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] #[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
@ -88,8 +79,7 @@ mod tests {
nonce: 0xdeadbeef, nonce: 0xdeadbeef,
}; };
let fingerprint = AccountId::new([8; 32]); let fingerprint = AccountId::new([8; 32]);
let new_acc_with_metadata = let new_acc_with_metadata = AccountWithMetadata::new(account.clone(), true, fingerprint);
AccountWithMetadata::new(account.clone(), true, fingerprint.clone());
assert_eq!(new_acc_with_metadata.account, account); assert_eq!(new_acc_with_metadata.account, account);
assert!(new_acc_with_metadata.is_authorized); assert!(new_acc_with_metadata.is_authorized);
assert_eq!(new_acc_with_metadata.account_id, fingerprint); assert_eq!(new_acc_with_metadata.account_id, fingerprint);

View File

@ -1,12 +1,13 @@
use serde::{Deserialize, Serialize};
#[cfg(feature = "host")]
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
use nssa_core::account::AccountId; #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
use serde::{Deserialize, Serialize}; #[cfg_attr(
use sha2::{Digest, Sha256}; any(feature = "host", test),
derive(Debug, Copy, PartialOrd, Ord, Default)
use crate::signature::PublicKey; )]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Address { pub struct Address {
value: [u8; 32], value: [u8; 32],
} }
@ -27,13 +28,7 @@ impl AsRef<[u8]> for Address {
} }
} }
impl From<&PublicKey> for Address { #[cfg(feature = "host")]
fn from(value: &PublicKey) -> Self {
// TODO: Check specs
Self::new(*value.value())
}
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum AddressError { pub enum AddressError {
#[error("invalid hex")] #[error("invalid hex")]
@ -42,6 +37,7 @@ pub enum AddressError {
InvalidLength(usize), InvalidLength(usize),
} }
#[cfg(feature = "host")]
impl FromStr for Address { impl FromStr for Address {
type Err = AddressError; type Err = AddressError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
@ -55,50 +51,17 @@ impl FromStr for Address {
} }
} }
#[cfg(feature = "host")]
impl Display for Address { impl Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", hex::encode(self.value)) 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)
}
}
impl From<&Address> for AccountId {
fn from(address: &Address) -> Self {
const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.1/AccountId/Public/\x00\x00\x00\x00";
let mut hasher = Sha256::new();
hasher.update(PUBLIC_ACCOUNT_ID_PREFIX);
hasher.update(address.value);
AccountId::new(hasher.finalize().into())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nssa_core::account::AccountId;
use crate::{Address, address::AddressError}; use super::{Address, AddressError};
#[test] #[test]
fn parse_valid_address() { fn parse_valid_address() {
@ -127,17 +90,4 @@ mod tests {
let result = hex_str.parse::<Address>().unwrap_err(); let result = hex_str.parse::<Address>().unwrap_err();
assert!(matches!(result, AddressError::InvalidLength(_))); assert!(matches!(result, AddressError::InvalidLength(_)));
} }
#[test]
fn test_account_id_from_address() {
let address: Address = "37".repeat(32).parse().unwrap();
let expected_account_id = AccountId::new([
93, 223, 66, 245, 78, 230, 157, 188, 110, 161, 134, 255, 137, 177, 220, 88, 37, 44,
243, 91, 236, 4, 36, 147, 185, 112, 21, 49, 234, 4, 107, 185,
]);
let account_id = AccountId::from(&address);
assert_eq!(account_id, expected_account_id);
}
} }

View File

@ -140,7 +140,7 @@ impl Secp256k1Point {
impl AccountId { impl AccountId {
pub fn to_bytes(&self) -> [u8; 32] { pub fn to_bytes(&self) -> [u8; 32] {
self.0 *self.value()
} }
} }

View File

@ -6,6 +6,8 @@ pub mod encryption;
mod nullifier; mod nullifier;
pub mod program; pub mod program;
pub mod address;
pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput}; pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput};
pub use commitment::{Commitment, CommitmentSetDigest, MembershipProof, compute_digest_for_path}; pub use commitment::{Commitment, CommitmentSetDigest, MembershipProof, compute_digest_for_path};
pub use encryption::{EncryptionScheme, SharedSecretKey}; pub use encryption::{EncryptionScheme, SharedSecretKey};

View File

@ -1,4 +1,3 @@
pub mod address;
pub mod encoding; pub mod encoding;
pub mod error; pub mod error;
mod merkle_tree; mod merkle_tree;
@ -8,8 +7,8 @@ pub mod public_transaction;
mod signature; mod signature;
mod state; mod state;
pub use address::Address;
pub use nssa_core::account::{Account, AccountId}; pub use nssa_core::account::{Account, AccountId};
pub use nssa_core::address::Address;
pub use privacy_preserving_transaction::{ pub use privacy_preserving_transaction::{
PrivacyPreservingTransaction, circuit::execute_and_prove, PrivacyPreservingTransaction, circuit::execute_and_prove,
}; };

View File

@ -94,7 +94,7 @@ impl PrivacyPreservingTransaction {
AccountWithMetadata::new( AccountWithMetadata::new(
state.get_account_by_address(address), state.get_account_by_address(address),
signer_addresses.contains(address), signer_addresses.contains(address),
address, *address,
) )
}) })
.collect(); .collect();

View File

@ -2,13 +2,13 @@ use std::collections::{HashMap, HashSet};
use nssa_core::{ use nssa_core::{
account::{Account, AccountWithMetadata}, account::{Account, AccountWithMetadata},
address::Address,
program::validate_execution, program::validate_execution,
}; };
use sha2::{Digest, digest::FixedOutput}; use sha2::{Digest, digest::FixedOutput};
use crate::{ use crate::{
V01State, V01State,
address::Address,
error::NssaError, error::NssaError,
public_transaction::{Message, WitnessSet}, public_transaction::{Message, WitnessSet},
}; };
@ -95,7 +95,7 @@ impl PublicTransaction {
AccountWithMetadata::new( AccountWithMetadata::new(
state.get_account_by_address(address), state.get_account_by_address(address),
signer_addresses.contains(address), signer_addresses.contains(address),
address, *address,
) )
}) })
.collect(); .collect();
@ -187,12 +187,12 @@ pub mod tests {
let tx = transaction_for_tests(); let tx = transaction_for_tests();
let expected_signer_addresses = vec![ let expected_signer_addresses = vec![
Address::new([ Address::new([
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 14, 238, 36, 40, 114, 150, 186, 85, 39, 143, 30, 84, 3, 190, 1, 71, 84, 134, 99,
24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, 102, 56, 135, 48, 48, 60, 40, 137, 190, 23, 173, 160, 101,
]), ]),
Address::new([ Address::new([
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 158, 61, 142, 101, 77, 68, 14, 149, 41, 58, 162, 220, 236, 235, 19, 120, 153, 165,
234, 216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, 149, 53, 233, 82, 247, 71, 6, 142, 122, 14, 227, 9, 101, 242,
]), ]),
]; ];
let signer_addresses = tx.signer_addresses(); let signer_addresses = tx.signer_addresses();

View File

@ -1,5 +1,9 @@
use nssa_core::address::Address;
use crate::{PrivateKey, error::NssaError}; use crate::{PrivateKey, error::NssaError};
use sha2::{Digest, Sha256};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct PublicKey([u8; 32]); pub struct PublicKey([u8; 32]);
@ -27,6 +31,17 @@ impl PublicKey {
} }
} }
impl From<&PublicKey> for Address {
fn from(key: &PublicKey) -> Self {
const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.1/AccountId/Public/\x00\x00\x00\x00";
let mut hasher = Sha256::new();
hasher.update(PUBLIC_ACCOUNT_ID_PREFIX);
hasher.update(key.0);
Self::new(hasher.finalize().into())
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{PublicKey, error::NssaError, signature::bip340_test_vectors}; use crate::{PublicKey, error::NssaError, signature::bip340_test_vectors};

View File

@ -1,11 +1,12 @@
use crate::{ use crate::{
address::Address, error::NssaError, merkle_tree::MerkleTree, error::NssaError, merkle_tree::MerkleTree,
privacy_preserving_transaction::PrivacyPreservingTransaction, program::Program, privacy_preserving_transaction::PrivacyPreservingTransaction, program::Program,
public_transaction::PublicTransaction, public_transaction::PublicTransaction,
}; };
use nssa_core::{ use nssa_core::{
Commitment, CommitmentSetDigest, MembershipProof, Nullifier, Commitment, CommitmentSetDigest, MembershipProof, Nullifier,
account::Account, account::Account,
address::Address,
program::{DEFAULT_PROGRAM_ID, ProgramId}, program::{DEFAULT_PROGRAM_ID, ProgramId},
}; };
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
@ -810,7 +811,7 @@ pub mod tests {
let sender = AccountWithMetadata::new( let sender = AccountWithMetadata::new(
state.get_account_by_address(&sender_keys.address()), state.get_account_by_address(&sender_keys.address()),
true, true,
&sender_keys.address(), sender_keys.address(),
); );
let sender_nonce = sender.account.nonce; let sender_nonce = sender.account.nonce;
@ -915,7 +916,7 @@ pub mod tests {
let recipient_pre = AccountWithMetadata::new( let recipient_pre = AccountWithMetadata::new(
state.get_account_by_address(recipient_address), state.get_account_by_address(recipient_address),
false, false,
recipient_address, *recipient_address,
); );
let esk = [3; 32]; let esk = [3; 32];

View File

@ -231,13 +231,13 @@ mod tests {
fn setup_sequencer_config() -> SequencerConfig { fn setup_sequencer_config() -> SequencerConfig {
let acc1_addr = vec![ let acc1_addr = vec![
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 14, 238, 36, 40, 114, 150, 186, 85, 39, 143, 30, 84, 3, 190, 1, 71, 84, 134, 99, 102,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, 56, 135, 48, 48, 60, 40, 137, 190, 23, 173, 160, 101,
]; ];
let acc2_addr = vec![ let acc2_addr = vec![
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 158, 61, 142, 101, 77, 68, 14, 149, 41, 58, 162, 220, 236, 235, 19, 120, 153, 165, 149,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, 53, 233, 82, 247, 71, 6, 142, 122, 14, 227, 9, 101, 242,
]; ];
let initial_acc1 = AccountInitialData { let initial_acc1 = AccountInitialData {

View File

@ -12,11 +12,11 @@ actix-cors.workspace = true
futures.workspace = true futures.workspace = true
hex.workspace = true hex.workspace = true
tempfile.workspace = true tempfile.workspace = true
nssa-core = { path = "../nssa/core", features = ["host"] }
base64.workspace = true base64.workspace = true
actix-web.workspace = true actix-web.workspace = true
tokio.workspace = true tokio.workspace = true
borsh.workspace = true
[dependencies.sequencer_core] [dependencies.sequencer_core]
path = "../sequencer_core" path = "../sequencer_core"

View File

@ -14,8 +14,8 @@ use common::{
requests::{ requests::{
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountRequest, GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountRequest,
GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
GetInitialTestnetAccountsRequest, GetProofByCommitmentRequest, GetInitialTestnetAccountsRequest, GetProofForCommitmentRequest,
GetProofByCommitmentResponse, GetTransactionByHashRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest,
GetTransactionByHashResponse, GetTransactionByHashResponse,
}, },
}, },
@ -77,7 +77,7 @@ impl JsonHandler {
async fn process_send_tx(&self, request: Request) -> Result<Value, RpcErr> { async fn process_send_tx(&self, request: Request) -> Result<Value, RpcErr> {
let send_tx_req = SendTxRequest::parse(Some(request.params))?; let send_tx_req = SendTxRequest::parse(Some(request.params))?;
let tx = EncodedTransaction::from_bytes(send_tx_req.transaction); let tx = borsh::from_slice::<EncodedTransaction>(&send_tx_req.transaction).unwrap();
let tx_hash = hex::encode(tx.hash()); let tx_hash = hex::encode(tx.hash());
{ {
@ -107,7 +107,7 @@ impl JsonHandler {
}; };
let helperstruct = GetBlockDataResponse { let helperstruct = GetBlockDataResponse {
block: HashableBlockData::from(block).to_bytes(), block: borsh::to_vec(&HashableBlockData::from(block)).unwrap(),
}; };
respond(helperstruct) respond(helperstruct)
@ -243,7 +243,7 @@ impl JsonHandler {
.store .store
.block_store .block_store
.get_transaction_by_hash(hash) .get_transaction_by_hash(hash)
.map(|tx| tx.to_bytes()) .map(|tx| borsh::to_vec(&tx).unwrap())
}; };
let base64_encoded = transaction.map(|tx| general_purpose::STANDARD.encode(tx)); let base64_encoded = transaction.map(|tx| general_purpose::STANDARD.encode(tx));
let helperstruct = GetTransactionByHashResponse { let helperstruct = GetTransactionByHashResponse {
@ -254,7 +254,7 @@ impl JsonHandler {
/// Returns the commitment proof, corresponding to commitment /// Returns the commitment proof, corresponding to commitment
async fn process_get_proof_by_commitment(&self, request: Request) -> Result<Value, RpcErr> { async fn process_get_proof_by_commitment(&self, request: Request) -> Result<Value, RpcErr> {
let get_proof_req = GetProofByCommitmentRequest::parse(Some(request.params))?; let get_proof_req = GetProofForCommitmentRequest::parse(Some(request.params))?;
let membership_proof = { let membership_proof = {
let state = self.sequencer_state.lock().await; let state = self.sequencer_state.lock().await;
@ -263,7 +263,7 @@ impl JsonHandler {
.state .state
.get_proof_for_commitment(&get_proof_req.commitment) .get_proof_for_commitment(&get_proof_req.commitment)
}; };
let helperstruct = GetProofByCommitmentResponse { membership_proof }; let helperstruct = GetProofForCommitmentResponse { membership_proof };
respond(helperstruct) respond(helperstruct)
} }
@ -308,13 +308,13 @@ mod tests {
let tempdir = tempdir().unwrap(); let tempdir = tempdir().unwrap();
let home = tempdir.path().to_path_buf(); let home = tempdir.path().to_path_buf();
let acc1_addr = vec![ let acc1_addr = vec![
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24, 14, 238, 36, 40, 114, 150, 186, 85, 39, 143, 30, 84, 3, 190, 1, 71, 84, 134, 99, 102,
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, 56, 135, 48, 48, 60, 40, 137, 190, 23, 173, 160, 101,
]; ];
let acc2_addr = vec![ let acc2_addr = vec![
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234, 158, 61, 142, 101, 77, 68, 14, 149, 41, 58, 162, 220, 236, 235, 19, 120, 153, 165, 149,
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102, 53, 233, 82, 247, 71, 6, 142, 122, 14, 227, 9, 101, 242,
]; ];
let initial_acc1 = AccountInitialData { let initial_acc1 = AccountInitialData {
@ -352,8 +352,8 @@ mod tests {
let balance_to_move = 10; let balance_to_move = 10;
let tx = common::test_utils::create_transaction_native_token_transfer( let tx = common::test_utils::create_transaction_native_token_transfer(
[ [
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 14, 238, 36, 40, 114, 150, 186, 85, 39, 143, 30, 84, 3, 190, 1, 71, 84, 134, 99,
24, 52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143, 102, 56, 135, 48, 48, 60, 40, 137, 190, 23, 173, 160, 101,
], ],
0, 0,
[2; 32], [2; 32],
@ -644,7 +644,7 @@ mod tests {
async fn test_get_transaction_by_hash_for_existing_transaction() { async fn test_get_transaction_by_hash_for_existing_transaction() {
let (json_handler, _, tx) = components_for_tests(); let (json_handler, _, tx) = components_for_tests();
let tx_hash_hex = hex::encode(tx.hash()); let tx_hash_hex = hex::encode(tx.hash());
let expected_base64_encoded = general_purpose::STANDARD.encode(tx.to_bytes()); let expected_base64_encoded = general_purpose::STANDARD.encode(borsh::to_vec(&tx).unwrap());
let request = serde_json::json!({ let request = serde_json::json!({
"jsonrpc": "2.0", "jsonrpc": "2.0",

View File

@ -8,12 +8,150 @@
"port": 3040, "port": 3040,
"initial_accounts": [ "initial_accounts": [
{ {
"addr": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "addr": "0eee24287296ba55278f1e5403be014754866366388730303c2889be17ada065",
"balance": 10000 "balance": 10000
}, },
{ {
"addr": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "addr": "9e3d8e654d440e95293aa2dceceb137899a59535e952f747068e7a0ee30965f2",
"balance": 20000 "balance": 20000
} }
],
"initial_commitments": [
{
"npk": [
193,
209,
150,
113,
47,
241,
48,
145,
250,
79,
235,
51,
119,
40,
184,
232,
5,
221,
36,
21,
201,
106,
90,
210,
129,
106,
71,
99,
208,
153,
75,
215
],
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 10000,
"data": [],
"nonce": 0
}
},
{
"npk": [
27,
250,
136,
142,
88,
128,
138,
21,
49,
183,
118,
160,
117,
114,
110,
47,
136,
87,
60,
70,
59,
60,
18,
223,
23,
147,
241,
5,
184,
103,
225,
105
],
"account": {
"program_owner": [
0,
0,
0,
0,
0,
0,
0,
0
],
"balance": 20000,
"data": [],
"nonce": 0
}
}
],
"signing_key": [
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37,
37
] ]
} }

View File

@ -5,9 +5,8 @@ edition = "2024"
[dependencies] [dependencies]
anyhow.workspace = true anyhow.workspace = true
serde_json.workspace = true
serde.workspace = true
thiserror.workspace = true thiserror.workspace = true
borsh.workspace = true
rocksdb.workspace = true rocksdb.workspace = true

View File

@ -7,7 +7,7 @@ pub enum DbError {
}, },
#[error("Serialization error")] #[error("Serialization error")]
SerializationError { SerializationError {
error: serde_json::Error, error: borsh::io::Error,
additional_info: Option<String>, additional_info: Option<String>,
}, },
#[error("Logic Error")] #[error("Logic Error")]
@ -22,9 +22,9 @@ impl DbError {
} }
} }
pub fn serde_cast_message(serr: serde_json::Error, message: Option<String>) -> Self { pub fn borsh_cast_message(berr: borsh::io::Error, message: Option<String>) -> Self {
Self::SerializationError { Self::SerializationError {
error: serr, error: berr,
additional_info: message, additional_info: message,
} }
} }

View File

@ -5,10 +5,8 @@ use error::DbError;
use rocksdb::{ use rocksdb::{
BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options,
}; };
use sc_db_utils::{DataBlob, DataBlobChangeVariant, produce_blob_from_fit_vec};
pub mod error; pub mod error;
pub mod sc_db_utils;
///Maximal size of stored blocks in base ///Maximal size of stored blocks in base
/// ///
@ -22,9 +20,6 @@ pub const BUFF_SIZE_ROCKSDB: usize = usize::MAX;
///Keeping small to not run out of memory ///Keeping small to not run out of memory
pub const CACHE_SIZE: usize = 1000; pub const CACHE_SIZE: usize = 1000;
///Size in bytes of a singular smart contract data blob, stored in db
pub const SC_DATA_BLOB_SIZE: usize = 256;
///Key base for storing metainformation about id of first block in db ///Key base for storing metainformation about id of first block in db
pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db"; pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db";
///Key base for storing metainformation about id of last current block in db ///Key base for storing metainformation about id of last current block in db
@ -36,14 +31,6 @@ pub const DB_META_SC_LIST: &str = "sc_list";
///Key base for storing snapshot which describe block id ///Key base for storing snapshot which describe block id
pub const DB_SNAPSHOT_BLOCK_ID_KEY: &str = "block_id"; pub const DB_SNAPSHOT_BLOCK_ID_KEY: &str = "block_id";
///Key base for storing snapshot which describe commitment
pub const DB_SNAPSHOT_COMMITMENT_KEY: &str = "commitment";
///Key base for storing snapshot which describe transaction
pub const DB_SNAPSHOT_TRANSACTION_KEY: &str = "transaction";
///Key base for storing snapshot which describe nullifier
pub const DB_SNAPSHOT_NULLIFIER_KEY: &str = "nullifier";
///Key base for storing snapshot which describe account
pub const DB_SNAPSHOT_ACCOUNT_KEY: &str = "account";
///Name of block column family ///Name of block column family
pub const CF_BLOCK_NAME: &str = "cf_block"; pub const CF_BLOCK_NAME: &str = "cf_block";
@ -54,9 +41,6 @@ pub const CF_SC_NAME: &str = "cf_sc";
///Name of snapshot column family ///Name of snapshot column family
pub const CF_SNAPSHOT_NAME: &str = "cf_snapshot"; pub const CF_SNAPSHOT_NAME: &str = "cf_snapshot";
///Suffix, used to mark field, which contain length of smart contract
pub const SC_LEN_SUFFIX: &str = "sc_len";
pub type DbResult<T> = Result<T, DbError>; pub type DbResult<T> = Result<T, DbError>;
pub struct RocksDBIO { pub struct RocksDBIO {
@ -142,11 +126,24 @@ impl RocksDBIO {
let cf_meta = self.meta_column(); let cf_meta = self.meta_column();
let res = self let res = self
.db .db
.get_cf(&cf_meta, DB_META_FIRST_BLOCK_IN_DB_KEY) .get_cf(
&cf_meta,
borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()),
)
})?,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res { if let Some(data) = res {
Ok(u64::from_be_bytes(data.try_into().unwrap())) Ok(borsh::from_slice::<u64>(&data).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to deserialize first block".to_string()),
)
})?)
} else { } else {
Err(DbError::db_interaction_error( Err(DbError::db_interaction_error(
"First block not found".to_string(), "First block not found".to_string(),
@ -158,11 +155,24 @@ impl RocksDBIO {
let cf_meta = self.meta_column(); let cf_meta = self.meta_column();
let res = self let res = self
.db .db
.get_cf(&cf_meta, DB_META_LAST_BLOCK_IN_DB_KEY) .get_cf(
&cf_meta,
borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()),
)
})?,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res { if let Some(data) = res {
Ok(u64::from_be_bytes(data.try_into().unwrap())) Ok(borsh::from_slice::<u64>(&data).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to deserialize last block".to_string()),
)
})?)
} else { } else {
Err(DbError::db_interaction_error( Err(DbError::db_interaction_error(
"Last block not found".to_string(), "Last block not found".to_string(),
@ -174,7 +184,15 @@ impl RocksDBIO {
let cf_meta = self.meta_column(); let cf_meta = self.meta_column();
let res = self let res = self
.db .db
.get_cf(&cf_meta, DB_META_FIRST_BLOCK_SET_KEY) .get_cf(
&cf_meta,
borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()),
)
})?,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(res.is_some()) Ok(res.is_some())
@ -185,8 +203,18 @@ impl RocksDBIO {
self.db self.db
.put_cf( .put_cf(
&cf_meta, &cf_meta,
DB_META_FIRST_BLOCK_IN_DB_KEY.as_bytes(), borsh::to_vec(&DB_META_FIRST_BLOCK_IN_DB_KEY).map_err(|err| {
block.header.block_id.to_be_bytes(), DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_FIRST_BLOCK_IN_DB_KEY".to_string()),
)
})?,
borsh::to_vec(&block.header.block_id).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize first block id".to_string()),
)
})?,
) )
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
@ -199,8 +227,18 @@ impl RocksDBIO {
self.db self.db
.put_cf( .put_cf(
&cf_meta, &cf_meta,
DB_META_LAST_BLOCK_IN_DB_KEY.as_bytes(), borsh::to_vec(&DB_META_LAST_BLOCK_IN_DB_KEY).map_err(|err| {
block_id.to_be_bytes(), DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_LAST_BLOCK_IN_DB_KEY".to_string()),
)
})?,
borsh::to_vec(&block_id).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize last block id".to_string()),
)
})?,
) )
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(()) Ok(())
@ -212,8 +250,18 @@ impl RocksDBIO {
self.db self.db
.put_cf( .put_cf(
&cf_meta, &cf_meta,
DB_META_SC_LIST.as_bytes(), borsh::to_vec(&DB_META_SC_LIST).map_err(|err| {
serde_json::to_vec(&sc_list).unwrap(), DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_SC_LIST".to_string()),
)
})?,
borsh::to_vec(&sc_list).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize list of sc".to_string()),
)
})?,
) )
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(()) Ok(())
@ -222,7 +270,16 @@ impl RocksDBIO {
pub fn put_meta_is_first_block_set(&self) -> DbResult<()> { pub fn put_meta_is_first_block_set(&self) -> DbResult<()> {
let cf_meta = self.meta_column(); let cf_meta = self.meta_column();
self.db self.db
.put_cf(&cf_meta, DB_META_FIRST_BLOCK_SET_KEY.as_bytes(), [1u8; 1]) .put_cf(
&cf_meta,
borsh::to_vec(&DB_META_FIRST_BLOCK_SET_KEY).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_META_FIRST_BLOCK_SET_KEY".to_string()),
)
})?,
[1u8; 1],
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(()) Ok(())
} }
@ -241,8 +298,18 @@ impl RocksDBIO {
self.db self.db
.put_cf( .put_cf(
&cf_block, &cf_block,
block.header.block_id.to_be_bytes(), borsh::to_vec(&block.header.block_id).map_err(|err| {
HashableBlockData::from(block).to_bytes(), DbError::borsh_cast_message(
err,
Some("Failed to serialize block id".to_string()),
)
})?,
borsh::to_vec(&HashableBlockData::from(block)).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize block data".to_string()),
)
})?,
) )
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(()) Ok(())
@ -252,11 +319,26 @@ impl RocksDBIO {
let cf_block = self.block_column(); let cf_block = self.block_column();
let res = self let res = self
.db .db
.get_cf(&cf_block, block_id.to_be_bytes()) .get_cf(
&cf_block,
borsh::to_vec(&block_id).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize block id".to_string()),
)
})?,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res { if let Some(data) = res {
Ok(HashableBlockData::from_bytes(&data)) Ok(
borsh::from_slice::<HashableBlockData>(&data).map_err(|serr| {
DbError::borsh_cast_message(
serr,
Some("Failed to deserialize block data".to_string()),
)
})?,
)
} else { } else {
Err(DbError::db_interaction_error( Err(DbError::db_interaction_error(
"Block on this id not found".to_string(), "Block on this id not found".to_string(),
@ -269,17 +351,23 @@ impl RocksDBIO {
let cf_meta = self.meta_column(); let cf_meta = self.meta_column();
let sc_list = self let sc_list = self
.db .db
.get_cf(&cf_meta, DB_META_SC_LIST) .get_cf(
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; &cf_meta,
if let Some(data) = sc_list { borsh::to_vec(&DB_META_SC_LIST).map_err(|err| {
Ok( DbError::borsh_cast_message(
serde_json::from_slice::<Vec<String>>(&data).map_err(|serr| { err,
DbError::serde_cast_message( Some("Failed to serialize DB_META_SC_LIST".to_string()),
serr,
Some("List of Sc Deserialization failed".to_string()),
) )
})?, })?,
) )
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = sc_list {
Ok(borsh::from_slice::<Vec<String>>(&data).map_err(|serr| {
DbError::borsh_cast_message(
serr,
Some("List of Sc Deserialization failed".to_string()),
)
})?)
} else { } else {
Err(DbError::db_interaction_error( Err(DbError::db_interaction_error(
"Sc list not found".to_string(), "Sc list not found".to_string(),
@ -297,250 +385,32 @@ impl RocksDBIO {
Ok(()) Ok(())
} }
///Put/Modify sc state in db
pub fn put_sc_sc_state(
&self,
sc_addr: &str,
length: usize,
modifications: Vec<DataBlobChangeVariant>,
) -> DbResult<()> {
self.put_meta_sc(sc_addr.to_string())?;
let cf_sc = self.sc_column();
let sc_addr_loc = format!("{sc_addr:?}{SC_LEN_SUFFIX}");
let sc_len_addr = sc_addr_loc.as_bytes();
self.db
.put_cf(&cf_sc, sc_len_addr, length.to_be_bytes())
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
for data_change in modifications {
match data_change {
DataBlobChangeVariant::Created { id, blob } => {
let blob_addr = produce_address_for_data_blob_at_id(sc_addr, id);
self.db
.put_cf(&cf_sc, blob_addr, blob)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
}
DataBlobChangeVariant::Modified {
id,
blob_old: _,
blob_new,
} => {
let blob_addr = produce_address_for_data_blob_at_id(sc_addr, id);
self.db
.put_cf(&cf_sc, blob_addr, blob_new)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
}
DataBlobChangeVariant::Deleted { id } => {
let blob_addr = produce_address_for_data_blob_at_id(sc_addr, id);
self.db
.delete_cf(&cf_sc, blob_addr)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
}
}
}
Ok(())
}
///Get sc state length in blobs from DB
pub fn get_sc_sc_state_len(&self, sc_addr: &str) -> DbResult<usize> {
let cf_sc = self.sc_column();
let sc_addr_loc = format!("{sc_addr:?}{SC_LEN_SUFFIX}");
let sc_len_addr = sc_addr_loc.as_bytes();
let sc_len = self
.db
.get_cf(&cf_sc, sc_len_addr)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(sc_len) = sc_len {
Ok(usize::from_be_bytes(sc_len.as_slice().try_into().unwrap()))
} else {
Err(DbError::db_interaction_error(format!(
"Sc len for {sc_addr:?} not found"
)))
}
}
///Get full sc state from DB
pub fn get_sc_sc_state(&self, sc_addr: &str) -> DbResult<Vec<DataBlob>> {
let cf_sc = self.sc_column();
let sc_len = self.get_sc_sc_state_len(sc_addr)?;
let mut data_blob_list = vec![];
for id in 0..sc_len {
let blob_addr = produce_address_for_data_blob_at_id(sc_addr, id);
let blob = self
.db
.get_cf(&cf_sc, blob_addr)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(blob_data) = blob {
data_blob_list.push(produce_blob_from_fit_vec(blob_data));
} else {
return Err(DbError::db_interaction_error(format!(
"Blob for {sc_addr:?} at id {id} not found"
)));
}
}
Ok(data_blob_list)
}
pub fn get_snapshot_block_id(&self) -> DbResult<u64> { pub fn get_snapshot_block_id(&self) -> DbResult<u64> {
let cf_snapshot = self.snapshot_column(); let cf_snapshot = self.snapshot_column();
let res = self let res = self
.db .db
.get_cf(&cf_snapshot, DB_SNAPSHOT_BLOCK_ID_KEY) .get_cf(
&cf_snapshot,
borsh::to_vec(&DB_SNAPSHOT_BLOCK_ID_KEY).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to serialize DB_SNAPSHOT_BLOCK_ID_KEY".to_string()),
)
})?,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res { if let Some(data) = res {
Ok(u64::from_be_bytes(data.try_into().unwrap())) Ok(borsh::from_slice::<u64>(&data).map_err(|err| {
DbError::borsh_cast_message(
err,
Some("Failed to deserialize last block".to_string()),
)
})?)
} else { } else {
Err(DbError::db_interaction_error( Err(DbError::db_interaction_error(
"Snapshot block ID not found".to_string(), "Snapshot block ID not found".to_string(),
)) ))
} }
} }
pub fn get_snapshot_commitment(&self) -> DbResult<Vec<u8>> {
let cf_snapshot = self.snapshot_column();
let res = self
.db
.get_cf(&cf_snapshot, DB_SNAPSHOT_COMMITMENT_KEY)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res {
Ok(data)
} else {
Err(DbError::db_interaction_error(
"Snapshot commitment not found".to_string(),
))
}
}
pub fn get_snapshot_transaction(&self) -> DbResult<Vec<u8>> {
let cf_snapshot = self.snapshot_column();
let res = self
.db
.get_cf(&cf_snapshot, DB_SNAPSHOT_TRANSACTION_KEY)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res {
Ok(data)
} else {
Err(DbError::db_interaction_error(
"Snapshot transaction not found".to_string(),
))
}
}
pub fn get_snapshot_nullifier(&self) -> DbResult<Vec<u8>> {
let cf_snapshot = self.snapshot_column();
let res = self
.db
.get_cf(&cf_snapshot, DB_SNAPSHOT_NULLIFIER_KEY)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res {
Ok(data)
} else {
Err(DbError::db_interaction_error(
"Snapshot nullifier not found".to_string(),
))
}
}
pub fn get_snapshot_account(&self) -> DbResult<Vec<u8>> {
let cf_snapshot = self.snapshot_column();
let res = self
.db
.get_cf(&cf_snapshot, DB_SNAPSHOT_ACCOUNT_KEY)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
if let Some(data) = res {
Ok(data)
} else {
Err(DbError::db_interaction_error(
"Snapshot account not found".to_string(),
))
}
}
pub fn put_snapshot_block_id_db(&self, block_id: u64) -> DbResult<()> {
let cf_snapshot = self.snapshot_column();
self.db
.put_cf(
&cf_snapshot,
DB_SNAPSHOT_BLOCK_ID_KEY.as_bytes(),
block_id.to_be_bytes(),
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(())
}
pub fn put_snapshot_commitement_db(&self, commitment: Vec<u8>) -> DbResult<()> {
let cf_snapshot = self.snapshot_column();
self.db
.put_cf(
&cf_snapshot,
DB_SNAPSHOT_COMMITMENT_KEY.as_bytes(),
commitment,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(())
}
pub fn put_snapshot_transaction_db(&self, transaction: Vec<u8>) -> DbResult<()> {
let cf_snapshot = self.snapshot_column();
self.db
.put_cf(
&cf_snapshot,
DB_SNAPSHOT_TRANSACTION_KEY.as_bytes(),
transaction,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(())
}
pub fn put_snapshot_nullifier_db(&self, nullifier: Vec<u8>) -> DbResult<()> {
let cf_snapshot = self.snapshot_column();
self.db
.put_cf(
&cf_snapshot,
DB_SNAPSHOT_NULLIFIER_KEY.as_bytes(),
nullifier,
)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(())
}
pub fn put_snapshot_account_db(&self, account: Vec<u8>) -> DbResult<()> {
let cf_snapshot = self.snapshot_column();
self.db
.put_cf(&cf_snapshot, DB_SNAPSHOT_ACCOUNT_KEY.as_bytes(), account)
.map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?;
Ok(())
}
}
///Creates address for sc data blob at corresponding id
fn produce_address_for_data_blob_at_id(sc_addr: &str, id: usize) -> Vec<u8> {
let mut prefix_bytes: Vec<u8> = sc_addr.as_bytes().to_vec();
let id_bytes = id.to_be_bytes();
for byte in id_bytes {
prefix_bytes.push(byte);
}
prefix_bytes
} }

View File

@ -1,160 +0,0 @@
use serde::{Deserialize, Serialize, de::Error};
use crate::SC_DATA_BLOB_SIZE;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DataBlob(pub [u8; SC_DATA_BLOB_SIZE]);
impl From<[u8; SC_DATA_BLOB_SIZE]> for DataBlob {
fn from(value: [u8; SC_DATA_BLOB_SIZE]) -> Self {
Self(value)
}
}
impl Serialize for DataBlob {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let data_vec = self.0.to_vec();
data_vec.serialize(serializer)
}
}
impl AsRef<[u8]> for DataBlob {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for DataBlob {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let data_vec = Vec::<u8>::deserialize(deserializer)?;
let chunk: [u8; SC_DATA_BLOB_SIZE] = data_vec
.try_into()
.map_err(|data| {
anyhow::anyhow!("failed to fit vec {data:?} to {:?}", SC_DATA_BLOB_SIZE)
})
.map_err(D::Error::custom)?;
Ok(Self(chunk))
}
}
impl DataBlob {
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum DataBlobChangeVariant {
Created {
id: usize,
blob: DataBlob,
},
Modified {
id: usize,
blob_old: DataBlob,
blob_new: DataBlob,
},
Deleted {
id: usize,
},
}
///Produce `DataBlob` from vector of size <= `SC_DATA_BLOB_SIZE`
///
///Extends to `SC_DATA_BLOB_SIZE`, if necessary.
///
///Panics, if size > `SC_DATA_BLOB_SIZE`
pub fn produce_blob_from_fit_vec(data: Vec<u8>) -> DataBlob {
let data_len = data.len();
assert!(data_len <= SC_DATA_BLOB_SIZE);
let mut blob: DataBlob = [0; SC_DATA_BLOB_SIZE].into();
for (idx, item) in data.into_iter().enumerate() {
blob.0[idx] = item
}
blob
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
const TEST_BLOB_SIZE: usize = 256; // Define a test blob size for simplicity
static SC_DATA_BLOB_SIZE: usize = TEST_BLOB_SIZE;
fn sample_vec() -> Vec<u8> {
(0..SC_DATA_BLOB_SIZE)
.collect::<Vec<usize>>()
.iter()
.map(|&x| x as u8)
.collect()
}
fn sample_data_blob() -> DataBlob {
let vec: Vec<u8> = sample_vec();
produce_blob_from_fit_vec(vec)
}
#[test]
fn test_serialize_data_blob() {
let blob = sample_data_blob();
let json = serde_json::to_string(&blob).unwrap();
let expected_json = serde_json::to_string(&sample_vec()).unwrap();
assert_eq!(json, expected_json);
}
#[test]
fn test_deserialize_data_blob() {
let data = sample_vec();
let json = serde_json::to_string(&data).unwrap();
let deserialized: DataBlob = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.to_vec(), data);
}
#[test]
fn test_serialize_deserialize_data_blob_change_variant() {
let blob1 = sample_data_blob();
let blob2 = produce_blob_from_fit_vec((50..50 + SC_DATA_BLOB_SIZE as u8).collect());
let variants = vec![
DataBlobChangeVariant::Created { id: 1, blob: blob1 },
DataBlobChangeVariant::Modified {
id: 2,
blob_old: blob1,
blob_new: blob2,
},
DataBlobChangeVariant::Deleted { id: 3 },
];
for variant in variants {
let json = serde_json::to_string(&variant).unwrap();
let deserialized: DataBlobChangeVariant = serde_json::from_str(&json).unwrap();
assert_eq!(variant, deserialized);
}
}
#[test]
fn test_produce_blob_from_fit_vec() {
let data = (0..255).collect();
let blob = produce_blob_from_fit_vec(data);
assert_eq!(blob.0[..4], [0, 1, 2, 3]);
}
#[test]
#[should_panic]
fn test_produce_blob_from_fit_vec_panic() {
let data = vec![0; SC_DATA_BLOB_SIZE + 1];
let _ = produce_blob_from_fit_vec(data);
}
}

View File

@ -16,7 +16,9 @@ nssa-core = { path = "../nssa/core" }
base64.workspace = true base64.workspace = true
k256 = { version = "0.13.3" } k256 = { version = "0.13.3" }
bytemuck = "1.23.2" bytemuck = "1.23.2"
borsh.workspace = true
hex.workspace = true hex.workspace = true
rand.workspace = true
[dependencies.key_protocol] [dependencies.key_protocol]
path = "../key_protocol" path = "../key_protocol"

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use anyhow::Result; use anyhow::Result;
use key_protocol::key_protocol_core::NSSAUserData; use key_protocol::key_protocol_core::NSSAUserData;
use nssa::program::Program;
use crate::config::{InitialAccountData, PersistentAccountData, WalletConfig}; use crate::config::{InitialAccountData, PersistentAccountData, WalletConfig};
@ -18,10 +19,15 @@ impl WalletChainStore {
for init_acc_data in config.initial_accounts.clone() { for init_acc_data in config.initial_accounts.clone() {
match init_acc_data { match init_acc_data {
InitialAccountData::Public(data) => { InitialAccountData::Public(data) => {
public_init_acc_map.insert(data.address, data.pub_sign_key); public_init_acc_map.insert(data.address.parse()?, data.pub_sign_key);
} }
InitialAccountData::Private(data) => { InitialAccountData::Private(data) => {
private_init_acc_map.insert(data.address, (data.key_chain, data.account)); let mut account = data.account;
// TODO: Program owner is only known after code is compiled and can't be set in
// the config. Therefore we overwrite it here on startup. Fix this when program
// id can be fetched from the node and queried from the wallet.
account.program_owner = Program::authenticated_transfer_program().id();
private_init_acc_map.insert(data.address.parse()?, (data.key_chain, account));
} }
} }
} }
@ -37,6 +43,7 @@ impl WalletChainStore {
addr: nssa::Address, addr: nssa::Address,
account: nssa_core::account::Account, account: nssa_core::account::Account,
) { ) {
println!("inserting at addres {}, this account {:?}", addr, account);
self.user_data self.user_data
.user_private_accounts .user_private_accounts
.entry(addr) .entry(addr)
@ -70,14 +77,14 @@ mod tests {
fn create_initial_accounts() -> Vec<InitialAccountData> { fn create_initial_accounts() -> Vec<InitialAccountData> {
let initial_acc1 = serde_json::from_str(r#"{ let initial_acc1 = serde_json::from_str(r#"{
"Public": { "Public": {
"address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", "address": "0eee24287296ba55278f1e5403be014754866366388730303c2889be17ada065",
"pub_sign_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] "pub_sign_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]
} }
}"#).unwrap(); }"#).unwrap();
let initial_acc2 = serde_json::from_str(r#"{ let initial_acc2 = serde_json::from_str(r#"{
"Public": { "Public": {
"address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", "address": "9e3d8e654d440e95293aa2dceceb137899a59535e952f747068e7a0ee30965f2",
"pub_sign_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] "pub_sign_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]
} }
}"#).unwrap(); }"#).unwrap();

View File

@ -4,7 +4,7 @@ use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitialAccountDataPublic { pub struct InitialAccountDataPublic {
pub address: nssa::Address, pub address: String,
pub pub_sign_key: nssa::PrivateKey, pub pub_sign_key: nssa::PrivateKey,
} }
@ -16,7 +16,7 @@ pub struct PersistentAccountDataPublic {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InitialAccountDataPrivate { pub struct InitialAccountDataPrivate {
pub address: nssa::Address, pub address: String,
pub account: nssa_core::account::Account, pub account: nssa_core::account::Account,
pub key_chain: KeyChain, pub key_chain: KeyChain,
} }
@ -49,8 +49,8 @@ pub enum PersistentAccountData {
impl InitialAccountData { impl InitialAccountData {
pub fn address(&self) -> nssa::Address { pub fn address(&self) -> nssa::Address {
match &self { match &self {
Self::Public(acc) => acc.address, Self::Public(acc) => acc.address.parse().unwrap(),
Self::Private(acc) => acc.address, Self::Private(acc) => acc.address.parse().unwrap(),
} }
} }
} }

View File

@ -1,9 +1,11 @@
use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
use nssa_core::account::Nonce;
use rand::{RngCore, rngs::OsRng};
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr}; use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};
use anyhow::Result; use anyhow::Result;
use key_protocol::key_protocol_core::NSSAUserData; use key_protocol::key_protocol_core::NSSAUserData;
use nssa::{Account, Address}; use nssa::Account;
use serde::Serialize; use serde::Serialize;
use crate::{ use crate::{
@ -28,11 +30,6 @@ pub fn fetch_config() -> Result<WalletConfig> {
Ok(serde_json::from_reader(reader)?) Ok(serde_json::from_reader(reader)?)
} }
// ToDo: Replace with structures conversion in future
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` /// 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 /// If file not present, it is considered as empty list of persistent accounts
@ -82,6 +79,12 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec<PersistentAccou
vec_for_storage vec_for_storage
} }
pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
let mut result = vec![[0; 16]; size];
result.iter_mut().for_each(|bytes| OsRng.fill_bytes(bytes));
result.into_iter().map(Nonce::from_le_bytes).collect()
}
/// Human-readable representation of an account. /// Human-readable representation of an account.
#[derive(Serialize)] #[derive(Serialize)]
pub(crate) struct HumanReadableAccount { pub(crate) struct HumanReadableAccount {

View File

@ -14,11 +14,12 @@ use log::info;
use nssa::{Account, Address}; use nssa::{Account, Address};
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use nssa_core::Commitment;
use crate::{ use crate::{
helperfunctions::{ helperfunctions::{
HumanReadableAccount, fetch_config, fetch_persistent_accounts, get_home, HumanReadableAccount, fetch_config, fetch_persistent_accounts, get_home,
produce_account_addr_from_hex, produce_data_for_storage, produce_data_for_storage,
}, },
poller::TxPoller, poller::TxPoller,
}; };
@ -180,16 +181,29 @@ impl WalletCore {
} }
///Get account ///Get account
pub async fn get_account(&self, addr: Address) -> Result<Account> { pub async fn get_account_public(&self, addr: Address) -> Result<Account> {
let response = self.sequencer_client.get_account(addr.to_string()).await?; let response = self.sequencer_client.get_account(addr.to_string()).await?;
Ok(response.account) Ok(response.account)
} }
pub fn get_account_private(&self, addr: &Address) -> Option<Account> {
self.storage
.user_data
.user_private_accounts
.get(addr)
.map(|value| value.1.clone())
}
pub fn get_private_account_commitment(&self, addr: &Address) -> Option<Commitment> {
let (keys, account) = self.storage.user_data.user_private_accounts.get(addr)?;
Some(Commitment::new(&keys.nullifer_public_key, account))
}
///Poll transactions ///Poll transactions
pub async fn poll_native_token_transfer(&self, hash: String) -> Result<NSSATransaction> { pub async fn poll_native_token_transfer(&self, hash: String) -> Result<NSSATransaction> {
let transaction_encoded = self.poller.poll_tx(hash).await?; let transaction_encoded = self.poller.poll_tx(hash).await?;
let tx_base64_decode = BASE64.decode(transaction_encoded)?; let tx_base64_decode = BASE64.decode(transaction_encoded)?;
let pub_tx = EncodedTransaction::from_bytes(tx_base64_decode); let pub_tx = borsh::from_slice::<EncodedTransaction>(&tx_base64_decode).unwrap();
Ok(NSSATransaction::try_from(&pub_tx)?) Ok(NSSATransaction::try_from(&pub_tx)?)
} }
@ -216,7 +230,7 @@ pub enum Command {
///Send native token transfer from `from` to `to` for `amount` ///Send native token transfer from `from` to `to` for `amount`
/// ///
/// Private operation /// Private operation
SendNativeTokenTransferPrivate { SendNativeTokenTransferPrivateOwnedAccount {
///from - valid 32 byte hex string ///from - valid 32 byte hex string
#[arg(long)] #[arg(long)]
from: String, from: String,
@ -290,16 +304,21 @@ pub enum Command {
amount: u128, amount: u128,
}, },
///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id`
ClaimPrivateAccount { FetchPrivateAccount {
///tx_hash - valid 32 byte hex string ///tx_hash - valid 32 byte hex string
#[arg(long)] #[arg(long)]
tx_hash: String, tx_hash: String,
///acc_addr - valid 32 byte hex string ///acc_addr - valid 32 byte hex string
#[arg(long)] #[arg(long)]
acc_addr: String, acc_addr: String,
///ciph_id - id of cipher in transaction ///output_id - id of the output in the transaction
#[arg(long)] #[arg(long)]
ciph_id: usize, output_id: usize,
},
///Get private account with `addr` from storage
GetPrivateAccount {
#[arg(short, long)]
addr: String,
}, },
///Register new public account ///Register new public account
RegisterAccountPublic {}, RegisterAccountPublic {},
@ -311,17 +330,17 @@ pub enum Command {
tx_hash: String, tx_hash: String,
}, },
///Get account `addr` balance ///Get account `addr` balance
GetAccountBalance { GetPublicAccountBalance {
#[arg(short, long)] #[arg(short, long)]
addr: String, addr: String,
}, },
///Get account `addr` nonce ///Get account `addr` nonce
GetAccountNonce { GetPublicAccountNonce {
#[arg(short, long)] #[arg(short, long)]
addr: String, addr: String,
}, },
///Get account at address `addr` ///Get account at address `addr`
GetAccount { GetPublicAccount {
#[arg(short, long)] #[arg(short, long)]
addr: String, addr: String,
}, },
@ -373,6 +392,7 @@ pub struct Args {
pub enum SubcommandReturnValue { pub enum SubcommandReturnValue {
PrivacyPreservingTransfer { tx_hash: String }, PrivacyPreservingTransfer { tx_hash: String },
RegisterAccount { addr: nssa::Address }, RegisterAccount { addr: nssa::Address },
Account(nssa::Account),
Empty, Empty,
} }
@ -382,8 +402,8 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
let subcommand_ret = match command { let subcommand_ret = match command {
Command::SendNativeTokenTransferPublic { from, to, amount } => { Command::SendNativeTokenTransferPublic { from, to, amount } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to = produce_account_addr_from_hex(to)?; let to: Address = to.parse().unwrap();
let res = wallet_core let res = wallet_core
.send_public_native_token_transfer(from, to, amount) .send_public_native_token_transfer(from, to, amount)
@ -401,12 +421,12 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty SubcommandReturnValue::Empty
} }
Command::SendNativeTokenTransferPrivate { from, to, amount } => { Command::SendNativeTokenTransferPrivateOwnedAccount { from, to, amount } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to = produce_account_addr_from_hex(to)?; let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core let (res, [secret_from, secret_to]) = wallet_core
.send_private_native_token_transfer(from, to, amount) .send_private_native_token_transfer_owned_account(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send is {res:#?}");
@ -425,15 +445,19 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
let res_acc_from = nssa_core::EncryptionScheme::decrypt( let res_acc_from = nssa_core::EncryptionScheme::decrypt(
&from_ebc.ciphertext, &from_ebc.ciphertext,
&secret, &secret_from,
&from_comm, &from_comm,
0, 0,
) )
.unwrap(); .unwrap();
let res_acc_to = let res_acc_to = nssa_core::EncryptionScheme::decrypt(
nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 1) &to_ebc.ciphertext,
.unwrap(); &secret_to,
&to_comm,
1,
)
.unwrap();
println!("Received new from acc {res_acc_from:#?}"); println!("Received new from acc {res_acc_from:#?}");
println!("Received new to acc {res_acc_to:#?}"); println!("Received new to acc {res_acc_to:#?}");
@ -460,7 +484,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
to_ipk, to_ipk,
amount, amount,
} => { } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?; let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32]; let mut to_npk = [0; 32];
to_npk.copy_from_slice(&to_npk_res); to_npk.copy_from_slice(&to_npk_res);
@ -472,7 +496,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
let to_ipk = let to_ipk =
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, secret) = wallet_core let (res, [secret_from, secret_to]) = wallet_core
.send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
.await?; .await?;
@ -492,15 +516,19 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
let res_acc_from = nssa_core::EncryptionScheme::decrypt( let res_acc_from = nssa_core::EncryptionScheme::decrypt(
&from_ebc.ciphertext, &from_ebc.ciphertext,
&secret, &secret_from,
&from_comm, &from_comm,
0, 0,
) )
.unwrap(); .unwrap();
let res_acc_to = let res_acc_to = nssa_core::EncryptionScheme::decrypt(
nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 1) &to_ebc.ciphertext,
.unwrap(); &secret_to,
&to_comm,
1,
)
.unwrap();
println!("RES acc {res_acc_from:#?}"); println!("RES acc {res_acc_from:#?}");
println!("RES acc to {res_acc_to:#?}"); println!("RES acc to {res_acc_to:#?}");
@ -519,8 +547,8 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
} }
Command::SendNativeTokenTransferDeshielded { from, to, amount } => { Command::SendNativeTokenTransferDeshielded { from, to, amount } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to = produce_account_addr_from_hex(to)?; let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core let (res, secret) = wallet_core
.send_deshielded_native_token_transfer(from, to, amount) .send_deshielded_native_token_transfer(from, to, amount)
@ -561,11 +589,11 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
} }
Command::SendNativeTokenTransferShielded { from, to, amount } => { Command::SendNativeTokenTransferShielded { from, to, amount } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to = produce_account_addr_from_hex(to)?; let to: Address = to.parse().unwrap();
let (res, secret) = wallet_core let (res, secret) = wallet_core
.send_shiedled_native_token_transfer(from, to, amount) .send_shielded_native_token_transfer(from, to, amount)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send is {res:#?}");
@ -600,7 +628,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
to_ipk, to_ipk,
amount, amount,
} => { } => {
let from = produce_account_addr_from_hex(from)?; let from: Address = from.parse().unwrap();
let to_npk_res = hex::decode(to_npk)?; let to_npk_res = hex::decode(to_npk)?;
let mut to_npk = [0; 32]; let mut to_npk = [0; 32];
@ -614,9 +642,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec());
let (res, secret) = wallet_core let (res, secret) = wallet_core
.send_shielded_native_token_transfer_maybe_outer_account( .send_shielded_native_token_transfer_outer_account(from, to_npk, to_ipk, amount)
from, to_npk, to_ipk, amount,
)
.await?; .await?;
println!("Results of tx send is {res:#?}"); println!("Results of tx send is {res:#?}");
@ -645,12 +671,12 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }
} }
Command::ClaimPrivateAccount { Command::FetchPrivateAccount {
tx_hash, tx_hash,
acc_addr, acc_addr,
ciph_id, output_id: ciph_id,
} => { } => {
let acc_addr = produce_account_addr_from_hex(acc_addr)?; let acc_addr: Address = acc_addr.parse().unwrap();
let account_key_chain = wallet_core let account_key_chain = wallet_core
.storage .storage
@ -706,15 +732,18 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
Command::RegisterAccountPrivate {} => { Command::RegisterAccountPrivate {} => {
let addr = wallet_core.create_new_account_private(); let addr = wallet_core.create_new_account_private();
let (key, account) = wallet_core let (key, _) = wallet_core
.storage .storage
.user_data .user_data
.get_private_account(&addr) .get_private_account(&addr)
.unwrap(); .unwrap();
println!("Generated new account with addr {addr:#?}"); println!("Generated new account with addr {addr}");
println!("With key {key:#?}"); println!("With npk {}", hex::encode(&key.nullifer_public_key));
println!("With account {account:#?}"); println!(
"With ipk {}",
hex::encode(key.incoming_viewing_public_key.to_bytes())
);
let path = wallet_core.store_persistent_accounts()?; let path = wallet_core.store_persistent_accounts()?;
@ -732,7 +761,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty SubcommandReturnValue::Empty
} }
Command::GetAccountBalance { addr } => { Command::GetPublicAccountBalance { addr } => {
let addr = Address::from_str(&addr)?; let addr = Address::from_str(&addr)?;
let balance = wallet_core.get_account_balance(addr).await?; let balance = wallet_core.get_account_balance(addr).await?;
@ -740,7 +769,7 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty SubcommandReturnValue::Empty
} }
Command::GetAccountNonce { addr } => { Command::GetPublicAccountNonce { addr } => {
let addr = Address::from_str(&addr)?; let addr = Address::from_str(&addr)?;
let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0];
@ -748,11 +777,21 @@ pub async fn execute_subcommand(command: Command) -> Result<SubcommandReturnValu
SubcommandReturnValue::Empty SubcommandReturnValue::Empty
} }
Command::GetAccount { addr } => { Command::GetPublicAccount { addr } => {
let addr: Address = addr.parse()?; let addr: Address = addr.parse()?;
let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into(); let account = wallet_core.get_account_public(addr).await?;
println!("{}", serde_json::to_string(&account).unwrap()); let account_hr: HumanReadableAccount = account.clone().into();
println!("{}", serde_json::to_string(&account_hr).unwrap());
SubcommandReturnValue::Account(account)
}
Command::GetPrivateAccount { addr } => {
let addr: Address = addr.parse()?;
if let Some(account) = wallet_core.get_account_private(&addr) {
println!("{}", serde_json::to_string(&account).unwrap());
} else {
println!("Private account not found.");
}
SubcommandReturnValue::Empty SubcommandReturnValue::Empty
} }
Command::CreateNewToken { Command::CreateNewToken {

View File

@ -1,9 +1,13 @@
use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse};
use k256::elliptic_curve::rand_core::{OsRng, RngCore}; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
use nssa::Address; use nssa::{
use nssa_core::{SharedSecretKey, encryption::EphemeralPublicKey}; Address, PrivacyPreservingTransaction,
privacy_preserving_transaction::{circuit, message::Message, witness_set::WitnessSet},
program::Program,
};
use nssa_core::{Commitment, account::AccountWithMetadata};
use crate::WalletCore; use crate::{WalletCore, helperfunctions::produce_random_nonces};
impl WalletCore { impl WalletCore {
pub async fn send_deshielded_native_token_transfer( pub async fn send_deshielded_native_token_transfer(
@ -12,48 +16,36 @@ impl WalletCore {
to: Address, to: Address,
balance_to_move: u128, balance_to_move: u128,
) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> {
let from_data = self.storage.user_data.get_private_account(&from).cloned(); let Some((from_keys, from_acc)) =
let to_data = self.get_account(to).await; self.storage.user_data.get_private_account(&from).cloned()
else {
let Some((from_keys, mut from_acc)) = from_data else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let Ok(to_acc) = to_data else { let Ok(to_acc) = self.get_account_public(to).await else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
if from_acc.balance >= balance_to_move { if from_acc.balance >= balance_to_move {
let program = nssa::program::Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
from_acc.program_owner = program.id(); let npk_from = from_keys.nullifer_public_key;
let ipk_from = from_keys.incoming_viewing_public_key;
let sender_commitment = let sender_commitment = Commitment::new(&npk_from, &from_acc);
nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc);
let sender_pre = nssa_core::account::AccountWithMetadata { let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &npk_from);
account: from_acc.clone(), let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, to);
is_authorized: true,
account_id: (&from_keys.nullifer_public_key).into(),
};
let recipient_pre = nssa_core::account::AccountWithMetadata {
account: to_acc.clone(),
is_authorized: false,
account_id: (&to).into(),
};
//Move into different function let eph_holder = EphemeralKeyHolder::new(&npk_from);
let mut esk = [0; 32]; let shared_secret = eph_holder.calculate_shared_secret_sender(&ipk_from);
OsRng.fill_bytes(&mut esk);
let shared_secret = SharedSecretKey::new(&esk, &from_keys.incoming_viewing_public_key);
let epk = EphemeralPublicKey::from_scalar(esk);
let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( let (output, proof) = circuit::execute_and_prove(
&[sender_pre, recipient_pre], &[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), &Program::serialize_instruction(balance_to_move).unwrap(),
&[1, 0], &[1, 0],
&[from_acc.nonce + 1], &produce_random_nonces(1),
&[(from_keys.nullifer_public_key.clone(), shared_secret.clone())], &[(npk_from.clone(), shared_secret.clone())],
&[( &[(
from_keys.private_key_holder.nullifier_secret_key, from_keys.private_key_holder.nullifier_secret_key,
self.sequencer_client self.sequencer_client
@ -66,30 +58,21 @@ impl WalletCore {
) )
.unwrap(); .unwrap();
let message = let message = Message::try_from_circuit_output(
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( vec![to],
vec![to], vec![],
vec![], vec![(
vec![( npk_from.clone(),
from_keys.nullifer_public_key.clone(), ipk_from.clone(),
from_keys.incoming_viewing_public_key.clone(), eph_holder.generate_ephemeral_public_key(),
epk, )],
)], output,
output, )
) .unwrap();
.unwrap();
let witness_set = let witness_set = WitnessSet::for_message(&message, proof, &[]);
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
&message,
proof,
&[],
);
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( let tx = PrivacyPreservingTransaction::new(message, witness_set);
message,
witness_set,
);
Ok(( Ok((
self.sequencer_client.send_tx_private(tx).await?, self.sequencer_client.send_tx_private(tx).await?,

View File

@ -1,61 +1,58 @@
use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse};
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
use nssa::Address; use nssa::{
Address, PrivacyPreservingTransaction,
privacy_preserving_transaction::{circuit, message::Message, witness_set::WitnessSet},
program::Program,
};
use nssa_core::{
Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
encryption::IncomingViewingPublicKey,
};
use crate::WalletCore; use crate::{WalletCore, helperfunctions::produce_random_nonces};
impl WalletCore { impl WalletCore {
pub async fn send_private_native_token_transfer_outer_account( pub async fn send_private_native_token_transfer_outer_account(
&self, &self,
from: Address, from: Address,
to_npk: nssa_core::NullifierPublicKey, to_npk: NullifierPublicKey,
to_ipk: nssa_core::encryption::IncomingViewingPublicKey, to_ipk: IncomingViewingPublicKey,
balance_to_move: u128, balance_to_move: u128,
) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
let from_data = self.storage.user_data.get_private_account(&from).cloned(); let Some((from_keys, from_acc)) =
self.storage.user_data.get_private_account(&from).cloned()
let Some((from_keys, mut from_acc)) = from_data else { else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let to_acc = nssa_core::account::Account::default(); let to_acc = nssa_core::account::Account::default();
if from_acc.balance >= balance_to_move { if from_acc.balance >= balance_to_move {
let program = nssa::program::Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
from_acc.program_owner = program.id(); let from_npk = from_keys.nullifer_public_key;
let from_ipk = from_keys.incoming_viewing_public_key;
let sender_commitment = let sender_commitment = Commitment::new(&from_npk, &from_acc);
nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc);
let sender_pre = nssa_core::account::AccountWithMetadata { let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
account: from_acc.clone(),
is_authorized: true,
account_id: (&from_keys.nullifer_public_key).into(),
};
let recipient_pre = nssa_core::account::AccountWithMetadata { let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk);
account: to_acc.clone(),
is_authorized: false,
account_id: (&to_npk).into(),
};
let eph_holder = EphemeralKeyHolder::new( let eph_holder = EphemeralKeyHolder::new(&to_npk);
to_npk.clone(),
from_keys.private_key_holder.outgoing_viewing_secret_key,
from_acc.nonce.try_into().unwrap(),
);
let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); let shared_secret_from = eph_holder.calculate_shared_secret_sender(&from_ipk);
let shared_secret_to = eph_holder.calculate_shared_secret_sender(&to_ipk);
let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( let (output, proof) = circuit::execute_and_prove(
&[sender_pre, recipient_pre], &[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), &Program::serialize_instruction(balance_to_move).unwrap(),
&[1, 2], &[1, 2],
&[from_acc.nonce + 1, to_acc.nonce + 1], &produce_random_nonces(2),
&[ &[
(from_keys.nullifer_public_key.clone(), shared_secret.clone()), (from_npk.clone(), shared_secret_from.clone()),
(to_npk.clone(), shared_secret.clone()), (to_npk.clone(), shared_secret_to.clone()),
], ],
&[( &[(
from_keys.private_key_holder.nullifier_secret_key, from_keys.private_key_holder.nullifier_secret_key,
@ -69,105 +66,83 @@ impl WalletCore {
) )
.unwrap(); .unwrap();
let message = let message = Message::try_from_circuit_output(
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( vec![],
vec![], vec![],
vec![], vec![
vec![ (
( from_npk.clone(),
from_keys.nullifer_public_key.clone(), from_ipk.clone(),
from_keys.incoming_viewing_public_key.clone(), eph_holder.generate_ephemeral_public_key(),
eph_holder.generate_ephemeral_public_key(), ),
), (
( to_npk.clone(),
to_npk.clone(), to_ipk.clone(),
to_ipk.clone(), eph_holder.generate_ephemeral_public_key(),
eph_holder.generate_ephemeral_public_key(), ),
), ],
], output,
output, )
) .unwrap();
.unwrap();
let witness_set = let witness_set = WitnessSet::for_message(&message, proof, &[]);
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
&message,
proof,
&[],
);
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( let tx = PrivacyPreservingTransaction::new(message, witness_set);
message,
witness_set,
);
Ok(( Ok((
self.sequencer_client.send_tx_private(tx).await?, self.sequencer_client.send_tx_private(tx).await?,
shared_secret, [shared_secret_from, shared_secret_to],
)) ))
} else { } else {
Err(ExecutionFailureKind::InsufficientFundsError) Err(ExecutionFailureKind::InsufficientFundsError)
} }
} }
pub async fn send_private_native_token_transfer( pub async fn send_private_native_token_transfer_owned_account(
&self, &self,
from: Address, from: Address,
to: Address, to: Address,
balance_to_move: u128, balance_to_move: u128,
) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { ) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
let from_data = self.storage.user_data.get_private_account(&from).cloned(); let Some((from_keys, from_acc)) =
let to_data = self.storage.user_data.get_private_account(&to).cloned(); self.storage.user_data.get_private_account(&from).cloned()
else {
let Some((from_keys, mut from_acc)) = from_data else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let Some((to_keys, mut to_acc)) = to_data else { let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned()
else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let from_npk = from_keys.nullifer_public_key;
let from_ipk = from_keys.incoming_viewing_public_key;
let to_npk = to_keys.nullifer_public_key.clone(); let to_npk = to_keys.nullifer_public_key.clone();
let to_ipk = to_keys.incoming_viewing_public_key.clone(); let to_ipk = to_keys.incoming_viewing_public_key.clone();
if from_acc.balance >= balance_to_move { if from_acc.balance >= balance_to_move {
let program = nssa::program::Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
from_acc.program_owner = program.id(); let sender_commitment = Commitment::new(&from_npk, &from_acc);
to_acc.program_owner = program.id(); let receiver_commitment = Commitment::new(&to_npk, &to_acc);
let sender_commitment = let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc); let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk);
let receiver_commitment =
nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc);
let sender_pre = nssa_core::account::AccountWithMetadata { let eph_holder_from = EphemeralKeyHolder::new(&from_npk);
account: from_acc.clone(), let shared_secret_from = eph_holder_from.calculate_shared_secret_sender(&from_ipk);
is_authorized: true,
account_id: (&from_keys.nullifer_public_key).into(),
};
let recipient_pre = nssa_core::account::AccountWithMetadata {
account: to_acc.clone(),
is_authorized: true,
account_id: (&to_npk).into(),
};
let eph_holder = EphemeralKeyHolder::new( let eph_holder_to = EphemeralKeyHolder::new(&to_npk);
to_npk.clone(), let shared_secret_to = eph_holder_to.calculate_shared_secret_sender(&to_ipk);
from_keys.private_key_holder.outgoing_viewing_secret_key,
from_acc.nonce.try_into().unwrap(),
);
let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); let (output, proof) = circuit::execute_and_prove(
let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove(
&[sender_pre, recipient_pre], &[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), &Program::serialize_instruction(balance_to_move).unwrap(),
&[1, 1], &[1, 1],
&[from_acc.nonce + 1, to_acc.nonce + 1], &produce_random_nonces(2),
&[ &[
(from_keys.nullifer_public_key.clone(), shared_secret.clone()), (from_npk.clone(), shared_secret_from.clone()),
(to_npk.clone(), shared_secret.clone()), (to_npk.clone(), shared_secret_to.clone()),
], ],
&[ &[
( (
@ -191,41 +166,31 @@ impl WalletCore {
) )
.unwrap(); .unwrap();
let message = let message = Message::try_from_circuit_output(
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( vec![],
vec![], vec![],
vec![], vec![
vec![ (
( from_npk.clone(),
from_keys.nullifer_public_key.clone(), from_ipk.clone(),
from_keys.incoming_viewing_public_key.clone(), eph_holder_from.generate_ephemeral_public_key(),
eph_holder.generate_ephemeral_public_key(), ),
), (
( to_npk.clone(),
to_npk.clone(), to_ipk.clone(),
to_ipk.clone(), eph_holder_to.generate_ephemeral_public_key(),
eph_holder.generate_ephemeral_public_key(), ),
), ],
], output,
output, )
) .unwrap();
.unwrap();
let witness_set = let witness_set = WitnessSet::for_message(&message, proof, &[]);
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( let tx = PrivacyPreservingTransaction::new(message, witness_set);
&message,
proof,
&[],
);
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new(
message,
witness_set,
);
Ok(( Ok((
self.sequencer_client.send_tx_private(tx).await?, self.sequencer_client.send_tx_private(tx).await?,
shared_secret, [shared_secret_from, shared_secret_to],
)) ))
} else { } else {
Err(ExecutionFailureKind::InsufficientFundsError) Err(ExecutionFailureKind::InsufficientFundsError)

View File

@ -1,5 +1,9 @@
use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse};
use nssa::Address; use nssa::{
Address, PublicTransaction,
program::Program,
public_transaction::{Message, WitnessSet},
};
use crate::WalletCore; use crate::WalletCore;
@ -20,14 +24,8 @@ impl WalletCore {
}; };
let addresses = vec![from, to]; let addresses = vec![from, to];
let program_id = nssa::program::Program::authenticated_transfer_program().id(); let program_id = Program::authenticated_transfer_program().id();
let message = nssa::public_transaction::Message::try_new( let message = Message::try_new(program_id, addresses, nonces, balance_to_move).unwrap();
program_id,
addresses,
nonces,
balance_to_move,
)
.unwrap();
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
@ -35,10 +33,9 @@ impl WalletCore {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let witness_set = let witness_set = WitnessSet::for_message(&message, &[signing_key]);
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
let tx = nssa::PublicTransaction::new(message, witness_set); let tx = PublicTransaction::new(message, witness_set);
Ok(self.sequencer_client.send_tx_public(tx).await?) Ok(self.sequencer_client.send_tx_public(tx).await?)
} else { } else {

View File

@ -1,24 +1,30 @@
use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse};
use key_protocol::key_management::ephemeral_key_holder::produce_one_sided_shared_secret_receiver; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
use nssa::Address; use nssa::{
Account, Address, PrivacyPreservingTransaction,
privacy_preserving_transaction::{circuit, message::Message, witness_set::WitnessSet},
program::Program,
};
use nssa_core::{
Commitment, NullifierPublicKey, SharedSecretKey, account::AccountWithMetadata,
encryption::IncomingViewingPublicKey,
};
use crate::WalletCore; use crate::{WalletCore, helperfunctions::produce_random_nonces};
impl WalletCore { impl WalletCore {
pub async fn send_shiedled_native_token_transfer( pub async fn send_shielded_native_token_transfer(
&self, &self,
from: Address, from: Address,
to: Address, to: Address,
balance_to_move: u128, balance_to_move: u128,
) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
let from_data = self.get_account(from).await; let Ok(from_acc) = self.get_account_public(from).await else {
let to_data = self.storage.user_data.get_private_account(&to).cloned();
let Ok(from_acc) = from_data else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let Some((to_keys, mut to_acc)) = to_data else { let Some((to_keys, to_acc)) = self.storage.user_data.get_private_account(&to).cloned()
else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
@ -26,31 +32,21 @@ impl WalletCore {
let to_ipk = to_keys.incoming_viewing_public_key.clone(); let to_ipk = to_keys.incoming_viewing_public_key.clone();
if from_acc.balance >= balance_to_move { if from_acc.balance >= balance_to_move {
let program = nssa::program::Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
to_acc.program_owner = program.id(); let receiver_commitment = Commitment::new(&to_npk, &to_acc);
let receiver_commitment = let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from);
nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc); let recipient_pre = AccountWithMetadata::new(to_acc.clone(), true, &to_npk);
let sender_pre = nssa_core::account::AccountWithMetadata { let eph_holder = EphemeralKeyHolder::new(&to_npk);
account: from_acc.clone(), let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk);
is_authorized: true,
account_id: (&from).into(),
};
let recipient_pre = nssa_core::account::AccountWithMetadata {
account: to_acc.clone(),
is_authorized: true,
account_id: (&to_npk).into(),
};
let (shared_secret, epk) = produce_one_sided_shared_secret_receiver(&to_ipk); let (output, proof) = circuit::execute_and_prove(
let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove(
&[sender_pre, recipient_pre], &[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(),
&[0, 1], &[0, 1],
&[to_acc.nonce + 1], &produce_random_nonces(1),
&[(to_npk.clone(), shared_secret.clone())], &[(to_npk.clone(), shared_secret.clone())],
&[( &[(
to_keys.private_key_holder.nullifier_secret_key, to_keys.private_key_holder.nullifier_secret_key,
@ -64,14 +60,17 @@ impl WalletCore {
) )
.unwrap(); .unwrap();
let message = let message = Message::try_from_circuit_output(
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( vec![from],
vec![from], vec![from_acc.nonce],
vec![from_acc.nonce], vec![(
vec![(to_npk.clone(), to_ipk.clone(), epk)], to_npk.clone(),
output, to_ipk.clone(),
) eph_holder.generate_ephemeral_public_key(),
.unwrap(); )],
output,
)
.unwrap();
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
@ -79,17 +78,9 @@ impl WalletCore {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let witness_set = let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]);
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
&message,
proof,
&[signing_key],
);
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( let tx = PrivacyPreservingTransaction::new(message, witness_set);
message,
witness_set,
);
Ok(( Ok((
self.sequencer_client.send_tx_private(tx).await?, self.sequencer_client.send_tx_private(tx).await?,
@ -100,57 +91,50 @@ impl WalletCore {
} }
} }
pub async fn send_shielded_native_token_transfer_maybe_outer_account( pub async fn send_shielded_native_token_transfer_outer_account(
&self, &self,
from: Address, from: Address,
to_npk: nssa_core::NullifierPublicKey, to_npk: NullifierPublicKey,
to_ipk: nssa_core::encryption::IncomingViewingPublicKey, to_ipk: IncomingViewingPublicKey,
balance_to_move: u128, balance_to_move: u128,
) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { ) -> Result<(SendTxResponse, SharedSecretKey), ExecutionFailureKind> {
let from_data = self.get_account(from).await; let Ok(from_acc) = self.get_account_public(from).await else {
let Ok(from_acc) = from_data else {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let to_acc = nssa_core::account::Account::default(); let to_acc = Account::default();
if from_acc.balance >= balance_to_move { if from_acc.balance >= balance_to_move {
let program = nssa::program::Program::authenticated_transfer_program(); let program = Program::authenticated_transfer_program();
let sender_pre = nssa_core::account::AccountWithMetadata { let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, from);
account: from_acc.clone(), let recipient_pre = AccountWithMetadata::new(to_acc.clone(), false, &to_npk);
is_authorized: true,
account_id: (&from).into(),
};
let recipient_pre = nssa_core::account::AccountWithMetadata { let eph_holder = EphemeralKeyHolder::new(&to_npk);
account: to_acc.clone(), let shared_secret = eph_holder.calculate_shared_secret_sender(&to_ipk);
is_authorized: false,
account_id: (&to_npk).into(),
};
let (shared_secret, epk) = produce_one_sided_shared_secret_receiver(&to_ipk); let (output, proof) = circuit::execute_and_prove(
let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove(
&[sender_pre, recipient_pre], &[sender_pre, recipient_pre],
&nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), &Program::serialize_instruction(balance_to_move).unwrap(),
&[0, 2], &[0, 2],
&[to_acc.nonce + 1], &produce_random_nonces(1),
&[(to_npk.clone(), shared_secret.clone())], &[(to_npk.clone(), shared_secret.clone())],
&[], &[],
&program, &program,
) )
.unwrap(); .unwrap();
let message = let message = Message::try_from_circuit_output(
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( vec![from],
vec![from], vec![from_acc.nonce],
vec![from_acc.nonce], vec![(
vec![(to_npk.clone(), to_ipk.clone(), epk)], to_npk.clone(),
output, to_ipk.clone(),
) eph_holder.generate_ephemeral_public_key(),
.unwrap(); )],
output,
)
.unwrap();
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
@ -158,17 +142,8 @@ impl WalletCore {
return Err(ExecutionFailureKind::KeyNotFoundError); return Err(ExecutionFailureKind::KeyNotFoundError);
}; };
let witness_set = let witness_set = WitnessSet::for_message(&message, proof, &[signing_key]);
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( let tx = PrivacyPreservingTransaction::new(message, witness_set);
&message,
proof,
&[signing_key],
);
let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new(
message,
witness_set,
);
Ok(( Ok((
self.sequencer_client.send_tx_private(tx).await?, self.sequencer_client.send_tx_private(tx).await?,