mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
Merge branch 'Pravdyvy/deterministic-key-derivation' into Pravdyvy/keys-restoration-from-mnemonic
This commit is contained in:
commit
0f617a8601
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@ -1,7 +1,7 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
@ -21,15 +21,14 @@ jobs:
|
||||
name: ubuntu-latest-pipeline
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install stable toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Install active toolchain
|
||||
run: rustup install
|
||||
|
||||
- name: Install nightly toolchain for rustfmt
|
||||
run: rustup install nightly --profile minimal --component rustfmt
|
||||
|
||||
- name: lint - ubuntu-latest
|
||||
if: success() || failure()
|
||||
run: chmod 777 ./ci_scripts/lint-ubuntu.sh && ./ci_scripts/lint-ubuntu.sh
|
||||
- name: test ubuntu-latest
|
||||
if: success() || failure()
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
set -e
|
||||
|
||||
cargo install taplo-cli --locked
|
||||
cargo +nightly fmt -- --check
|
||||
|
||||
cargo fmt -- --check
|
||||
cargo install taplo-cli --locked
|
||||
taplo fmt --check
|
||||
|
||||
RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::transaction::EncodedTransaction;
|
||||
pub type HashType = [u8; 32];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
///Our own hasher.
|
||||
/// Our own hasher.
|
||||
/// Currently it is SHA256 hasher wrapper. May change in a future.
|
||||
pub struct OwnHasher {}
|
||||
|
||||
|
||||
@ -4,8 +4,8 @@ pub mod rpc_primitives;
|
||||
pub mod sequencer_client;
|
||||
pub mod transaction;
|
||||
|
||||
//Module for tests utility functions
|
||||
//TODO: Compile only for tests
|
||||
// Module for tests utility functions
|
||||
// TODO: Compile only for tests
|
||||
pub mod test_utils;
|
||||
pub type HashType = [u8; 32];
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use serde_json::{Value, to_value};
|
||||
use std::fmt;
|
||||
|
||||
use serde_json::{Value, to_value};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct RpcParseError(pub String);
|
||||
|
||||
|
||||
@ -9,11 +9,14 @@
|
||||
//!
|
||||
//! The main entrypoint here is the [Message](enum.Message.html). The others are just building
|
||||
//! blocks and you should generally work with `Message` instead.
|
||||
use serde::de::{Deserializer, Error, Unexpected, Visitor};
|
||||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde_json::{Result as JsonResult, Value};
|
||||
use std::fmt::{Formatter, Result as FmtResult};
|
||||
|
||||
use serde::{
|
||||
de::{Deserializer, Error, Unexpected, Visitor},
|
||||
ser::{SerializeStruct, Serializer},
|
||||
};
|
||||
use serde_json::{Result as JsonResult, Value};
|
||||
|
||||
use super::errors::RpcError;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@ -69,6 +72,7 @@ impl Request {
|
||||
id: self.id.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Answer the request with an error.
|
||||
pub fn error(&self, error: RpcError) -> Message {
|
||||
Message::Response(Response {
|
||||
@ -207,6 +211,7 @@ impl Message {
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a top-level error (without an ID).
|
||||
pub fn error(error: RpcError) -> Self {
|
||||
Message::Response(Response {
|
||||
@ -215,6 +220,7 @@ impl Message {
|
||||
id: Value::Null,
|
||||
})
|
||||
}
|
||||
|
||||
/// A constructor for a notification.
|
||||
pub fn notification(method: String, params: Value) -> Self {
|
||||
Message::Notification(Notification {
|
||||
@ -223,6 +229,7 @@ impl Message {
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/// A constructor for a response.
|
||||
pub fn response(id: Value, result: Result<Value, RpcError>) -> Self {
|
||||
Message::Response(Response {
|
||||
@ -231,6 +238,7 @@ impl Message {
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns id or Null if there is no id.
|
||||
pub fn id(&self) -> Value {
|
||||
match self {
|
||||
@ -315,10 +323,7 @@ impl From<Message> for Vec<u8> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::Value;
|
||||
use serde_json::de::from_slice;
|
||||
use serde_json::json;
|
||||
use serde_json::ser::to_vec;
|
||||
use serde_json::{Value, de::from_slice, json, ser::to_vec};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod errors;
|
||||
@ -7,21 +5,6 @@ pub mod message;
|
||||
pub mod parser;
|
||||
pub mod requests;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
pub struct RpcPollingConfig {
|
||||
pub polling_interval: Duration,
|
||||
pub polling_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Default for RpcPollingConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
polling_interval: Duration::from_millis(500),
|
||||
polling_timeout: Duration::from_secs(10),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct RpcLimitsConfig {
|
||||
/// Maximum byte size of the json payload.
|
||||
@ -40,7 +23,6 @@ impl Default for RpcLimitsConfig {
|
||||
pub struct RpcConfig {
|
||||
pub addr: String,
|
||||
pub cors_allowed_origins: Vec<String>,
|
||||
pub polling_config: RpcPollingConfig,
|
||||
#[serde(default)]
|
||||
pub limits_config: RpcLimitsConfig,
|
||||
}
|
||||
@ -50,7 +32,6 @@ impl Default for RpcConfig {
|
||||
RpcConfig {
|
||||
addr: "0.0.0.0:3040".to_owned(),
|
||||
cors_allowed_origins: vec!["*".to_owned()],
|
||||
polling_config: RpcPollingConfig::default(),
|
||||
limits_config: RpcLimitsConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::parse_request;
|
||||
|
||||
use super::errors::RpcParseError;
|
||||
use super::parser::RpcRequest;
|
||||
use super::parser::parse_params;
|
||||
use nssa_core::program::ProgramId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{
|
||||
errors::RpcParseError,
|
||||
parser::{RpcRequest, parse_params},
|
||||
};
|
||||
use crate::parse_request;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct HelloRequest {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RegisterAccountRequest {
|
||||
pub address: [u8; 32],
|
||||
pub account_id: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -38,7 +39,7 @@ pub struct GetInitialTestnetAccountsRequest {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountBalanceRequest {
|
||||
pub address: String,
|
||||
pub account_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -48,12 +49,12 @@ pub struct GetTransactionByHashRequest {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountsNoncesRequest {
|
||||
pub addresses: Vec<String>,
|
||||
pub account_ids: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetAccountRequest {
|
||||
pub address: String,
|
||||
pub account_id: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
//Requests
|
||||
// Requests
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxRequest {
|
||||
pub transaction: Vec<u8>,
|
||||
}
|
||||
|
||||
//Responses
|
||||
// Responses
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SendTxResponse {
|
||||
@ -15,7 +15,7 @@ pub struct SendTxResponse {
|
||||
pub tx_hash: String,
|
||||
}
|
||||
|
||||
//General
|
||||
// General
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SequencerRpcRequest {
|
||||
@ -31,7 +31,7 @@ impl SequencerRpcRequest {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
method,
|
||||
params: payload,
|
||||
//ToDo: Correct checking of id
|
||||
// ToDo: Correct checking of id
|
||||
id: 1,
|
||||
}
|
||||
}
|
||||
@ -45,9 +45,9 @@ pub struct SequencerRpcResponse {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
///Helperstruct for account serialization
|
||||
/// Helperstruct for account serialization
|
||||
pub struct AccountInitialData {
|
||||
///Hex encoded `AccountAddress`
|
||||
pub addr: String,
|
||||
/// Hex encoded account id
|
||||
pub account_id: String,
|
||||
pub balance: u64,
|
||||
}
|
||||
|
||||
@ -1,24 +1,26 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::rpc_primitives::requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetBlockDataRequest, GetBlockDataResponse,
|
||||
GetGenesisIdRequest, GetGenesisIdResponse, GetInitialTestnetAccountsRequest,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use json::{SendTxRequest, SendTxResponse, SequencerRpcRequest, SequencerRpcResponse};
|
||||
use nssa_core::program::ProgramId;
|
||||
use reqwest::Client;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::error::{SequencerClientError, SequencerRpcError};
|
||||
use crate::rpc_primitives::requests::{
|
||||
GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||
GetLastBlockRequest, GetLastBlockResponse, GetProgramIdsRequest, GetProgramIdsResponse,
|
||||
GetProofForCommitmentRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
use super::rpc_primitives::requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetBlockDataRequest, GetBlockDataResponse,
|
||||
GetGenesisIdRequest, GetGenesisIdResponse, GetInitialTestnetAccountsRequest,
|
||||
};
|
||||
use crate::{
|
||||
error::{SequencerClientError, SequencerRpcError},
|
||||
rpc_primitives::requests::{
|
||||
GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||
GetLastBlockRequest, GetLastBlockResponse, GetProgramIdsRequest, GetProgramIdsResponse,
|
||||
GetProofForCommitmentRequest, GetProofForCommitmentResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse,
|
||||
},
|
||||
sequencer_client::json::AccountInitialData,
|
||||
transaction::{EncodedTransaction, NSSATransaction},
|
||||
};
|
||||
use crate::sequencer_client::json::AccountInitialData;
|
||||
use crate::transaction::{EncodedTransaction, NSSATransaction};
|
||||
|
||||
pub mod json;
|
||||
|
||||
@ -62,7 +64,7 @@ impl SequencerClient {
|
||||
}
|
||||
}
|
||||
|
||||
///Get block data at `block_id` from sequencer
|
||||
/// Get block data at `block_id` from sequencer
|
||||
pub async fn get_block(
|
||||
&self,
|
||||
block_id: u64,
|
||||
@ -78,7 +80,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get last known `blokc_id` from sequencer
|
||||
/// Get last known `blokc_id` from sequencer
|
||||
pub async fn get_last_block(&self) -> Result<GetLastBlockResponse, SequencerClientError> {
|
||||
let block_req = GetLastBlockRequest {};
|
||||
|
||||
@ -91,12 +93,13 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get account public balance for `address`. `address` must be a valid hex-string for 32 bytes.
|
||||
/// Get account public balance for `account_id`. `account_id` must be a valid hex-string for 32
|
||||
/// bytes.
|
||||
pub async fn get_account_balance(
|
||||
&self,
|
||||
address: String,
|
||||
account_id: String,
|
||||
) -> Result<GetAccountBalanceResponse, SequencerClientError> {
|
||||
let block_req = GetAccountBalanceRequest { address };
|
||||
let block_req = GetAccountBalanceRequest { account_id };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
@ -109,12 +112,13 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get accounts nonces for `addresses`. `addresses` must be a list of valid hex-strings for 32 bytes.
|
||||
/// Get accounts nonces for `account_ids`. `account_ids` must be a list of valid hex-strings for
|
||||
/// 32 bytes.
|
||||
pub async fn get_accounts_nonces(
|
||||
&self,
|
||||
addresses: Vec<String>,
|
||||
account_ids: Vec<String>,
|
||||
) -> Result<GetAccountsNoncesResponse, SequencerClientError> {
|
||||
let block_req = GetAccountsNoncesRequest { addresses };
|
||||
let block_req = GetAccountsNoncesRequest { account_ids };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
@ -129,9 +133,9 @@ impl SequencerClient {
|
||||
|
||||
pub async fn get_account(
|
||||
&self,
|
||||
address: String,
|
||||
account_id: String,
|
||||
) -> Result<GetAccountResponse, SequencerClientError> {
|
||||
let block_req = GetAccountRequest { address };
|
||||
let block_req = GetAccountRequest { account_id };
|
||||
|
||||
let req = serde_json::to_value(block_req)?;
|
||||
|
||||
@ -142,7 +146,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get transaction details for `hash`.
|
||||
/// Get transaction details for `hash`.
|
||||
pub async fn get_transaction_by_hash(
|
||||
&self,
|
||||
hash: String,
|
||||
@ -160,7 +164,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Send transaction to sequencer
|
||||
/// Send transaction to sequencer
|
||||
pub async fn send_tx_public(
|
||||
&self,
|
||||
transaction: nssa::PublicTransaction,
|
||||
@ -180,7 +184,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Send transaction to sequencer
|
||||
/// Send transaction to sequencer
|
||||
pub async fn send_tx_private(
|
||||
&self,
|
||||
transaction: nssa::PrivacyPreservingTransaction,
|
||||
@ -200,7 +204,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get genesis id from sequencer
|
||||
/// Get genesis id from sequencer
|
||||
pub async fn get_genesis_id(&self) -> Result<GetGenesisIdResponse, SequencerClientError> {
|
||||
let genesis_req = GetGenesisIdRequest {};
|
||||
|
||||
@ -216,7 +220,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get initial testnet accounts from sequencer
|
||||
/// Get initial testnet accounts from sequencer
|
||||
pub async fn get_initial_testnet_accounts(
|
||||
&self,
|
||||
) -> Result<Vec<AccountInitialData>, SequencerClientError> {
|
||||
@ -234,7 +238,7 @@ impl SequencerClient {
|
||||
Ok(resp_deser)
|
||||
}
|
||||
|
||||
///Get proof for commitment
|
||||
/// Get proof for commitment
|
||||
pub async fn get_proof_for_commitment(
|
||||
&self,
|
||||
commitment: nssa_core::Commitment,
|
||||
|
||||
@ -3,15 +3,15 @@ use crate::{
|
||||
transaction::{EncodedTransaction, NSSATransaction},
|
||||
};
|
||||
|
||||
//Helpers
|
||||
// Helpers
|
||||
|
||||
pub fn sequencer_sign_key_for_testing() -> nssa::PrivateKey {
|
||||
nssa::PrivateKey::try_new([37; 32]).unwrap()
|
||||
}
|
||||
|
||||
//Dummy producers
|
||||
// Dummy producers
|
||||
|
||||
///Produce dummy block with
|
||||
/// Produce dummy block with
|
||||
///
|
||||
/// `id` - block id, provide zero for genesis
|
||||
///
|
||||
@ -35,12 +35,16 @@ pub fn produce_dummy_block(
|
||||
|
||||
pub fn produce_dummy_empty_transaction() -> EncodedTransaction {
|
||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||
let addresses = vec![];
|
||||
let account_ids = vec![];
|
||||
let nonces = vec![];
|
||||
let instruction_data: u128 = 0;
|
||||
let message =
|
||||
nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction_data)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction_data,
|
||||
)
|
||||
.unwrap();
|
||||
let private_key = nssa::PrivateKey::try_new([1; 32]).unwrap();
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&private_key]);
|
||||
|
||||
@ -56,12 +60,16 @@ pub fn create_transaction_native_token_transfer(
|
||||
balance_to_move: u128,
|
||||
signing_key: nssa::PrivateKey,
|
||||
) -> EncodedTransaction {
|
||||
let addresses = vec![nssa::Address::new(from), nssa::Address::new(to)];
|
||||
let account_ids = vec![nssa::AccountId::new(from), nssa::AccountId::new(to)];
|
||||
let nonces = vec![nonce];
|
||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
||||
let message =
|
||||
nssa::public_transaction::Message::try_new(program_id, addresses, nonces, balance_to_move)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
balance_to_move,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]);
|
||||
|
||||
let nssa_tx = nssa::PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
pub type HashType = [u8; 32];
|
||||
@ -41,10 +40,10 @@ pub enum TxKind {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
///General transaction object
|
||||
/// General transaction object
|
||||
pub struct EncodedTransaction {
|
||||
pub tx_kind: TxKind,
|
||||
///Encoded blobs of data
|
||||
/// Encoded blobs of data
|
||||
pub encoded_transaction_data: Vec<u8>,
|
||||
}
|
||||
|
||||
|
||||
@ -9,11 +9,11 @@
|
||||
"port": 3040,
|
||||
"initial_accounts": [
|
||||
{
|
||||
"addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"balance": 10000
|
||||
},
|
||||
{
|
||||
"addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"balance": 20000
|
||||
}
|
||||
],
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"initial_accounts": [
|
||||
{
|
||||
"Public": {
|
||||
"address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"pub_sign_key": [
|
||||
16,
|
||||
162,
|
||||
@ -47,7 +47,7 @@
|
||||
},
|
||||
{
|
||||
"Public": {
|
||||
"address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"pub_sign_key": [
|
||||
113,
|
||||
121,
|
||||
@ -86,7 +86,7 @@
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"address": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw",
|
||||
"account_id": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw",
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
@ -315,7 +315,7 @@
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"address": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX",
|
||||
"account_id": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX",
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use actix_web::dev::ServerHandle;
|
||||
use anyhow::Result;
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use clap::Parser;
|
||||
use common::{
|
||||
sequencer_client::SequencerClient,
|
||||
@ -44,19 +44,19 @@ pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12;
|
||||
|
||||
pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &[u8] = include_bytes!("data_changer.bin");
|
||||
|
||||
fn make_public_account_input_from_str(addr: &str) -> String {
|
||||
format!("Public/{addr}")
|
||||
fn make_public_account_input_from_str(account_id: &str) -> String {
|
||||
format!("Public/{account_id}")
|
||||
}
|
||||
|
||||
fn make_private_account_input_from_str(addr: &str) -> String {
|
||||
format!("Private/{addr}")
|
||||
fn make_private_account_input_from_str(account_id: &str) -> String {
|
||||
format!("Private/{account_id}")
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub async fn pre_test(
|
||||
home_dir: PathBuf,
|
||||
) -> Result<(ServerHandle, JoinHandle<Result<()>>, TempDir)> {
|
||||
wallet::execute_setup("test_pass".to_string()).await?;
|
||||
wallet::execute_setup("test_pass".to_owned()).await?;
|
||||
|
||||
let home_dir_sequencer = home_dir.join("sequencer");
|
||||
|
||||
@ -98,13 +98,13 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle<Result<()>>, TempDir)
|
||||
let wallet_home = wallet::helperfunctions::get_home().unwrap();
|
||||
let persistent_data_home = wallet_home.join("storage.json");
|
||||
|
||||
//Removing persistent accounts after run to not affect other executions
|
||||
//Not necessary an error, if fails as there is tests for failure scenario
|
||||
// Removing persistent accounts after run to not affect other executions
|
||||
// Not necessary an error, if fails as there is tests for failure scenario
|
||||
let _ = std::fs::remove_file(persistent_data_home)
|
||||
.inspect_err(|err| warn!("Failed to remove persistent data with err {err:#?}"));
|
||||
|
||||
//At this point all of the references to sequencer_core must be lost.
|
||||
//So they are dropped and tempdirs will be dropped too,
|
||||
// At this point all of the references to sequencer_core must be lost.
|
||||
// So they are dropped and tempdirs will be dropped too,
|
||||
}
|
||||
|
||||
pub async fn main_tests_runner() -> Result<()> {
|
||||
@ -176,14 +176,14 @@ mod tests {
|
||||
use crate::{make_private_account_input_from_str, make_public_account_input_from_str};
|
||||
|
||||
#[test]
|
||||
fn correct_addr_from_prefix() {
|
||||
let addr1 = "cafecafe";
|
||||
let addr2 = "deadbeaf";
|
||||
fn correct_account_id_from_prefix() {
|
||||
let account_id1 = "cafecafe";
|
||||
let account_id2 = "deadbeaf";
|
||||
|
||||
let addr1_pub = make_public_account_input_from_str(addr1);
|
||||
let addr2_priv = make_private_account_input_from_str(addr2);
|
||||
let account_id1_pub = make_public_account_input_from_str(account_id1);
|
||||
let account_id2_priv = make_private_account_input_from_str(account_id2);
|
||||
|
||||
assert_eq!(addr1_pub, "Public/cafecafe".to_string());
|
||||
assert_eq!(addr2_priv, "Private/deadbeaf".to_string());
|
||||
assert_eq!(account_id1_pub, "Public/cafecafe".to_string());
|
||||
assert_eq!(account_id2_priv, "Private/deadbeaf".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use integration_tests::main_tests_runner;
|
||||
|
||||
pub const NUM_THREADS: usize = 8;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use nssa::{
|
||||
Account, AccountId, Address, PrivacyPreservingTransaction, PrivateKey, PublicKey,
|
||||
PublicTransaction,
|
||||
Account, AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey, PublicTransaction,
|
||||
privacy_preserving_transaction::{self as pptx, circuit},
|
||||
program::Program,
|
||||
public_transaction as putx,
|
||||
@ -15,13 +14,13 @@ use nssa_core::{
|
||||
use sequencer_core::config::{AccountInitialData, CommitmentsInitialData, SequencerConfig};
|
||||
|
||||
pub(crate) struct TpsTestManager {
|
||||
public_keypairs: Vec<(PrivateKey, Address)>,
|
||||
public_keypairs: Vec<(PrivateKey, AccountId)>,
|
||||
target_tps: u64,
|
||||
}
|
||||
|
||||
impl TpsTestManager {
|
||||
/// Generates public account keypairs. These are used to populate the config and to generate valid
|
||||
/// public transactions for the tps test.
|
||||
/// Generates public account keypairs. These are used to populate the config and to generate
|
||||
/// valid public transactions for the tps test.
|
||||
pub(crate) fn new(target_tps: u64, number_transactions: usize) -> Self {
|
||||
let public_keypairs = (1..(number_transactions + 2))
|
||||
.map(|i| {
|
||||
@ -29,8 +28,8 @@ impl TpsTestManager {
|
||||
private_key_bytes[..8].copy_from_slice(&i.to_le_bytes());
|
||||
let private_key = PrivateKey::try_new(private_key_bytes).unwrap();
|
||||
let public_key = PublicKey::new_from_private_key(&private_key);
|
||||
let address = Address::from(&public_key);
|
||||
(private_key, address)
|
||||
let account_id = AccountId::from(&public_key);
|
||||
(private_key, account_id)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Self {
|
||||
@ -44,7 +43,6 @@ impl TpsTestManager {
|
||||
Duration::from_secs_f64(number_transactions as f64 / self.target_tps as f64)
|
||||
}
|
||||
|
||||
///
|
||||
/// Build a batch of public transactions to submit to the node.
|
||||
pub fn build_public_txs(&self) -> Vec<PublicTransaction> {
|
||||
// Create valid public transactions
|
||||
@ -71,15 +69,15 @@ impl TpsTestManager {
|
||||
}
|
||||
|
||||
/// Generates a sequencer configuration with initial balance in a number of public accounts.
|
||||
/// The transactions generated with the function `build_public_txs` will be valid in a node started
|
||||
/// with the config from this method.
|
||||
/// The transactions generated with the function `build_public_txs` will be valid in a node
|
||||
/// started with the config from this method.
|
||||
pub(crate) fn generate_tps_test_config(&self) -> SequencerConfig {
|
||||
// Create public public keypairs
|
||||
let initial_public_accounts = self
|
||||
.public_keypairs
|
||||
.iter()
|
||||
.map(|(_, addr)| AccountInitialData {
|
||||
addr: addr.to_string(),
|
||||
.map(|(_, account_id)| AccountInitialData {
|
||||
account_id: account_id.to_string(),
|
||||
balance: 10,
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -6,7 +6,8 @@ use rand::{RngCore, rngs::OsRng};
|
||||
use sha2::Digest;
|
||||
|
||||
#[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 {
|
||||
ephemeral_secret_key: EphemeralSecretKey,
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ impl FromStr for ChainIndex {
|
||||
type Err = ChainIndexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if !s.starts_with("/") {
|
||||
if !s.starts_with('/') {
|
||||
return Err(ChainIndexError::NoRootFound);
|
||||
}
|
||||
|
||||
@ -42,19 +42,25 @@ impl FromStr for ChainIndex {
|
||||
impl Display for ChainIndex {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "/")?;
|
||||
if *self != Self::root() {
|
||||
for cci in &self.0[..(self.0.len() - 1)] {
|
||||
write!(f, "{cci}/")?;
|
||||
}
|
||||
write!(f, "{}", self.0.last().unwrap())?;
|
||||
for cci in &self.0[..(self.0.len().saturating_sub(1))] {
|
||||
write!(f, "{cci}/")?;
|
||||
}
|
||||
if let Some(last) = self.0.last() {
|
||||
write!(f, "{}", last)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChainIndex {
|
||||
fn default() -> Self {
|
||||
ChainIndex::from_str("/").expect("Root parsing failure")
|
||||
}
|
||||
}
|
||||
|
||||
impl ChainIndex {
|
||||
pub fn root() -> Self {
|
||||
ChainIndex::from_str("/").unwrap()
|
||||
ChainIndex::default()
|
||||
}
|
||||
|
||||
pub fn chain(&self) -> &[u32] {
|
||||
@ -63,7 +69,7 @@ impl ChainIndex {
|
||||
|
||||
pub fn next_in_line(&self) -> ChainIndex {
|
||||
let mut chain = self.0.clone();
|
||||
//ToDo: Add overflow check
|
||||
// ToDo: Add overflow check
|
||||
if let Some(last_p) = chain.last_mut() {
|
||||
*last_p += 1
|
||||
}
|
||||
@ -71,7 +77,7 @@ impl ChainIndex {
|
||||
ChainIndex(chain)
|
||||
}
|
||||
|
||||
pub fn n_th_child(&self, child_id: u32) -> ChainIndex {
|
||||
pub fn nth_child(&self, child_id: u32) -> ChainIndex {
|
||||
let mut chain = self.0.clone();
|
||||
chain.push(child_id);
|
||||
|
||||
@ -108,6 +114,23 @@ mod tests {
|
||||
assert_eq!(chain_id.chain(), &[257]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_deser_failure_no_root() {
|
||||
let chain_index_error = ChainIndex::from_str("257").err().unwrap();
|
||||
|
||||
assert!(matches!(chain_index_error, ChainIndexError::NoRootFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_deser_failure_int_parsing_failure() {
|
||||
let chain_index_error = ChainIndex::from_str("/hello").err().unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
chain_index_error,
|
||||
ChainIndexError::ParseIntError(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chain_id_next_in_line_correct() {
|
||||
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||
@ -119,7 +142,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_chain_id_child_correct() {
|
||||
let chain_id = ChainIndex::from_str("/257").unwrap();
|
||||
let child = chain_id.n_th_child(3);
|
||||
let child = chain_id.nth_child(3);
|
||||
|
||||
assert_eq!(child, ChainIndex::from_str("/257/3").unwrap());
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ use crate::key_management::{
|
||||
pub struct ChildKeysPrivate {
|
||||
pub value: (KeyChain, nssa::Account),
|
||||
pub ccc: [u8; 32],
|
||||
///Can be None if root
|
||||
/// Can be [`None`] if root
|
||||
pub cci: Option<u32>,
|
||||
}
|
||||
|
||||
@ -20,8 +20,14 @@ impl KeyNode for ChildKeysPrivate {
|
||||
fn root(seed: [u8; 64]) -> Self {
|
||||
let hash_value = hmac_sha512::HMAC::mac(seed, "NSSA_master_priv");
|
||||
|
||||
let ssk = SecretSpendingKey(*hash_value.first_chunk::<32>().unwrap());
|
||||
let ccc = *hash_value.last_chunk::<32>().unwrap();
|
||||
let ssk = SecretSpendingKey(
|
||||
*hash_value
|
||||
.first_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
||||
);
|
||||
let ccc = *hash_value
|
||||
.last_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
||||
|
||||
let nsk = ssk.generate_nullifier_secret_key();
|
||||
let isk = ssk.generate_incoming_viewing_secret_key();
|
||||
@ -49,7 +55,7 @@ impl KeyNode for ChildKeysPrivate {
|
||||
}
|
||||
}
|
||||
|
||||
fn n_th_child(&self, cci: u32) -> Self {
|
||||
fn nth_child(&self, cci: u32) -> Self {
|
||||
let parent_pt = Scalar::from_repr(
|
||||
self.value
|
||||
.0
|
||||
@ -57,9 +63,9 @@ impl KeyNode for ChildKeysPrivate {
|
||||
.outgoing_viewing_secret_key
|
||||
.into(),
|
||||
)
|
||||
.unwrap()
|
||||
.expect("Key generated as scalar, must be valid representation")
|
||||
+ Scalar::from_repr(self.value.0.private_key_holder.nullifier_secret_key.into())
|
||||
.unwrap()
|
||||
.expect("Key generated as scalar, must be valid representation")
|
||||
* Scalar::from_repr(
|
||||
self.value
|
||||
.0
|
||||
@ -67,7 +73,7 @@ impl KeyNode for ChildKeysPrivate {
|
||||
.incoming_viewing_secret_key
|
||||
.into(),
|
||||
)
|
||||
.unwrap();
|
||||
.expect("Key generated as scalar, must be valid representation");
|
||||
let mut input = vec![];
|
||||
|
||||
input.extend_from_slice(b"NSSA_seed_priv");
|
||||
@ -76,8 +82,14 @@ impl KeyNode for ChildKeysPrivate {
|
||||
|
||||
let hash_value = hmac_sha512::HMAC::mac(input, self.ccc);
|
||||
|
||||
let ssk = SecretSpendingKey(*hash_value.first_chunk::<32>().unwrap());
|
||||
let ccc = *hash_value.last_chunk::<32>().unwrap();
|
||||
let ssk = SecretSpendingKey(
|
||||
*hash_value
|
||||
.first_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
||||
);
|
||||
let ccc = *hash_value
|
||||
.last_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
||||
|
||||
let nsk = ssk.generate_nullifier_secret_key();
|
||||
let isk = ssk.generate_incoming_viewing_secret_key();
|
||||
@ -109,12 +121,12 @@ impl KeyNode for ChildKeysPrivate {
|
||||
&self.ccc
|
||||
}
|
||||
|
||||
fn child_index(&self) -> &Option<u32> {
|
||||
&self.cci
|
||||
fn child_index(&self) -> Option<u32> {
|
||||
self.cci
|
||||
}
|
||||
|
||||
fn address(&self) -> nssa::Address {
|
||||
nssa::Address::from(&self.value.0.nullifer_public_key)
|
||||
fn account_id(&self) -> nssa::AccountId {
|
||||
nssa::AccountId::from(&self.value.0.nullifer_public_key)
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +149,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_keys_deterministic_generation() {
|
||||
let root_keys = ChildKeysPrivate::root([42; 64]);
|
||||
let child_keys = root_keys.n_th_child(5);
|
||||
let child_keys = root_keys.nth_child(5);
|
||||
|
||||
assert_eq!(root_keys.cci, None);
|
||||
assert_eq!(child_keys.cci, Some(5));
|
||||
|
||||
@ -7,7 +7,7 @@ pub struct ChildKeysPublic {
|
||||
pub csk: nssa::PrivateKey,
|
||||
pub cpk: nssa::PublicKey,
|
||||
pub ccc: [u8; 32],
|
||||
///Can be None if root
|
||||
/// Can be [`None`] if root
|
||||
pub cci: Option<u32>,
|
||||
}
|
||||
|
||||
@ -27,15 +27,22 @@ impl KeyNode for ChildKeysPublic {
|
||||
}
|
||||
}
|
||||
|
||||
fn n_th_child(&self, cci: u32) -> Self {
|
||||
fn nth_child(&self, cci: u32) -> Self {
|
||||
let mut hash_input = vec![];
|
||||
hash_input.extend_from_slice(self.csk.value());
|
||||
hash_input.extend_from_slice(&cci.to_le_bytes());
|
||||
|
||||
let hash_value = hmac_sha512::HMAC::mac(&hash_input, self.ccc);
|
||||
|
||||
let csk = nssa::PrivateKey::try_new(*hash_value.first_chunk::<32>().unwrap()).unwrap();
|
||||
let ccc = *hash_value.last_chunk::<32>().unwrap();
|
||||
let csk = nssa::PrivateKey::try_new(
|
||||
*hash_value
|
||||
.first_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get first 32"),
|
||||
)
|
||||
.unwrap();
|
||||
let ccc = *hash_value
|
||||
.last_chunk::<32>()
|
||||
.expect("hash_value is 64 bytes, must be safe to get last 32");
|
||||
let cpk = nssa::PublicKey::new_from_private_key(&csk);
|
||||
|
||||
Self {
|
||||
@ -50,12 +57,12 @@ impl KeyNode for ChildKeysPublic {
|
||||
&self.ccc
|
||||
}
|
||||
|
||||
fn child_index(&self) -> &Option<u32> {
|
||||
&self.cci
|
||||
fn child_index(&self) -> Option<u32> {
|
||||
self.cci
|
||||
}
|
||||
|
||||
fn address(&self) -> nssa::Address {
|
||||
nssa::Address::from(&self.cpk)
|
||||
fn account_id(&self) -> nssa::AccountId {
|
||||
nssa::AccountId::from(&self.cpk)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +79,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_keys_deterministic_generation() {
|
||||
let root_keys = ChildKeysPublic::root([42; 64]);
|
||||
let child_keys = root_keys.n_th_child(5);
|
||||
let child_keys = root_keys.nth_child(5);
|
||||
|
||||
assert_eq!(root_keys.cci, None);
|
||||
assert_eq!(child_keys.cci, Some(5));
|
||||
|
||||
@ -21,120 +21,122 @@ pub mod keys_public;
|
||||
pub mod traits;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct KeyTree<Node: KeyNode> {
|
||||
pub key_map: BTreeMap<ChainIndex, Node>,
|
||||
pub addr_map: HashMap<nssa::Address, ChainIndex>,
|
||||
pub struct KeyTree<N: KeyNode> {
|
||||
pub key_map: BTreeMap<ChainIndex, N>,
|
||||
pub account_id_map: HashMap<nssa::AccountId, ChainIndex>,
|
||||
}
|
||||
|
||||
pub type KeyTreePublic = KeyTree<ChildKeysPublic>;
|
||||
pub type KeyTreePrivate = KeyTree<ChildKeysPrivate>;
|
||||
|
||||
impl<Node: KeyNode> KeyTree<Node> {
|
||||
impl<N: KeyNode> KeyTree<N> {
|
||||
pub fn new(seed: &SeedHolder) -> Self {
|
||||
let seed_fit: [u8; 64] = seed.seed.clone().try_into().unwrap();
|
||||
let seed_fit: [u8; 64] = seed
|
||||
.seed
|
||||
.clone()
|
||||
.try_into()
|
||||
.expect("SeedHolder seed is 64 bytes long");
|
||||
|
||||
let root_keys = Node::root(seed_fit);
|
||||
let address = root_keys.address();
|
||||
let root_keys = N::root(seed_fit);
|
||||
let account_id = root_keys.account_id();
|
||||
|
||||
let mut key_map = BTreeMap::new();
|
||||
let mut addr_map = HashMap::new();
|
||||
let key_map = BTreeMap::from_iter([(ChainIndex::root(), root_keys)]);
|
||||
let account_id_map = HashMap::from_iter([(account_id, ChainIndex::root())]);
|
||||
|
||||
key_map.insert(ChainIndex::root(), root_keys);
|
||||
addr_map.insert(address, ChainIndex::root());
|
||||
|
||||
Self { key_map, addr_map }
|
||||
Self {
|
||||
key_map,
|
||||
account_id_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_root(root: Node) -> Self {
|
||||
let mut key_map = BTreeMap::new();
|
||||
let mut addr_map = HashMap::new();
|
||||
pub fn new_from_root(root: N) -> Self {
|
||||
let account_id_map = HashMap::from_iter([(root.account_id(), ChainIndex::root())]);
|
||||
let key_map = BTreeMap::from_iter([(ChainIndex::root(), root)]);
|
||||
|
||||
addr_map.insert(root.address(), ChainIndex::root());
|
||||
key_map.insert(ChainIndex::root(), root);
|
||||
|
||||
Self { key_map, addr_map }
|
||||
Self {
|
||||
key_map,
|
||||
account_id_map,
|
||||
}
|
||||
}
|
||||
|
||||
//ToDo: Add function to create a tree from list of nodes with consistency check.
|
||||
// ToDo: Add function to create a tree from list of nodes with consistency check.
|
||||
|
||||
pub fn find_next_last_child_of_id(&self, parent_id: &ChainIndex) -> Option<u32> {
|
||||
if !self.key_map.contains_key(parent_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let leftmost_child = parent_id.n_th_child(u32::MIN);
|
||||
let leftmost_child = parent_id.nth_child(u32::MIN);
|
||||
|
||||
if !self.key_map.contains_key(&leftmost_child) {
|
||||
Some(0)
|
||||
} else {
|
||||
let mut right = u32::MAX - 1;
|
||||
let mut left_border = u32::MIN;
|
||||
let mut right_border = u32::MAX;
|
||||
return Some(0);
|
||||
}
|
||||
|
||||
loop {
|
||||
let rightmost_child = parent_id.n_th_child(right);
|
||||
let mut right = u32::MAX - 1;
|
||||
let mut left_border = u32::MIN;
|
||||
let mut right_border = u32::MAX;
|
||||
|
||||
let rightmost_ref = self.key_map.get(&rightmost_child);
|
||||
let rightmost_ref_next = self.key_map.get(&rightmost_child.next_in_line());
|
||||
loop {
|
||||
let rightmost_child = parent_id.nth_child(right);
|
||||
|
||||
match (&rightmost_ref, &rightmost_ref_next) {
|
||||
(Some(_), Some(_)) => {
|
||||
left_border = right;
|
||||
right = (right + right_border) / 2;
|
||||
}
|
||||
(Some(_), None) => {
|
||||
break Some(right + 1);
|
||||
}
|
||||
(None, None) => {
|
||||
right_border = right;
|
||||
right = (left_border + right) / 2;
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
unreachable!();
|
||||
}
|
||||
let rightmost_ref = self.key_map.get(&rightmost_child);
|
||||
let rightmost_ref_next = self.key_map.get(&rightmost_child.next_in_line());
|
||||
|
||||
match (&rightmost_ref, &rightmost_ref_next) {
|
||||
(Some(_), Some(_)) => {
|
||||
left_border = right;
|
||||
right = (right + right_border) / 2;
|
||||
}
|
||||
(Some(_), None) => {
|
||||
break Some(right + 1);
|
||||
}
|
||||
(None, None) => {
|
||||
right_border = right;
|
||||
right = (left_border + right) / 2;
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_new_node(&mut self, parent_cci: ChainIndex) -> Option<nssa::Address> {
|
||||
if !self.key_map.contains_key(&parent_cci) {
|
||||
return None;
|
||||
}
|
||||
pub fn generate_new_node(&mut self, parent_cci: ChainIndex) -> Option<nssa::AccountId> {
|
||||
let father_keys = self.key_map.get(&parent_cci)?;
|
||||
let next_child_id = self
|
||||
.find_next_last_child_of_id(&parent_cci)
|
||||
.expect("Can be None only if parent is not present");
|
||||
let next_cci = parent_cci.nth_child(next_child_id);
|
||||
|
||||
let father_keys = self.key_map.get(&parent_cci).unwrap();
|
||||
let next_child_id = self.find_next_last_child_of_id(&parent_cci).unwrap();
|
||||
let next_cci = parent_cci.n_th_child(next_child_id);
|
||||
let child_keys = father_keys.nth_child(next_child_id);
|
||||
|
||||
let child_keys = father_keys.n_th_child(next_child_id);
|
||||
|
||||
let address = child_keys.address();
|
||||
let account_id = child_keys.account_id();
|
||||
|
||||
self.key_map.insert(next_cci.clone(), child_keys);
|
||||
self.addr_map.insert(address, next_cci);
|
||||
self.account_id_map.insert(account_id, next_cci);
|
||||
|
||||
Some(address)
|
||||
Some(account_id)
|
||||
}
|
||||
|
||||
pub fn get_node(&self, addr: nssa::Address) -> Option<&Node> {
|
||||
self.addr_map
|
||||
.get(&addr)
|
||||
pub fn get_node(&self, account_id: nssa::AccountId) -> Option<&N> {
|
||||
self.account_id_map
|
||||
.get(&account_id)
|
||||
.and_then(|chain_id| self.key_map.get(chain_id))
|
||||
}
|
||||
|
||||
pub fn get_node_mut(&mut self, addr: nssa::Address) -> Option<&mut Node> {
|
||||
self.addr_map
|
||||
.get(&addr)
|
||||
pub fn get_node_mut(&mut self, account_id: nssa::AccountId) -> Option<&mut N> {
|
||||
self.account_id_map
|
||||
.get(&account_id)
|
||||
.and_then(|chain_id| self.key_map.get_mut(chain_id))
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, addr: nssa::Address, chain_index: ChainIndex, node: Node) {
|
||||
self.addr_map.insert(addr, chain_index.clone());
|
||||
pub fn insert(&mut self, account_id: nssa::AccountId, chain_index: ChainIndex, node: N) {
|
||||
self.account_id_map.insert(account_id, chain_index.clone());
|
||||
self.key_map.insert(chain_index, node);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, addr: nssa::Address) -> Option<Node> {
|
||||
let chain_index = self.addr_map.remove(&addr).unwrap();
|
||||
pub fn remove(&mut self, addr: nssa::AccountId) -> Option<N> {
|
||||
let chain_index = self.account_id_map.remove(&addr).unwrap();
|
||||
self.key_map.remove(&chain_index)
|
||||
}
|
||||
|
||||
@ -144,7 +146,7 @@ impl<Node: KeyNode> KeyTree<Node> {
|
||||
while let Some(curr_id) = id_stack.pop() {
|
||||
self.generate_new_node(curr_id.clone());
|
||||
|
||||
let mut next_id = curr_id.n_th_child(0);
|
||||
let mut next_id = curr_id.nth_child(0);
|
||||
|
||||
while (next_id.depth()) < depth - 1 {
|
||||
id_stack.push(next_id.clone());
|
||||
@ -163,11 +165,11 @@ impl KeyTree<ChildKeysPrivate> {
|
||||
&& node.value.1 == nssa::Account::default()
|
||||
&& curr_id != ChainIndex::root()
|
||||
{
|
||||
let addr = node.address();
|
||||
let addr = node.account_id();
|
||||
self.remove(addr);
|
||||
}
|
||||
|
||||
let mut next_id = curr_id.n_th_child(0);
|
||||
let mut next_id = curr_id.nth_child(0);
|
||||
|
||||
while (next_id.depth()) < depth - 1 {
|
||||
id_stack.push(next_id.clone());
|
||||
@ -187,7 +189,7 @@ impl KeyTree<ChildKeysPublic> {
|
||||
|
||||
while let Some(curr_id) = id_stack.pop() {
|
||||
if let Some(node) = self.key_map.get(&curr_id) {
|
||||
let address = node.address();
|
||||
let address = node.account_id();
|
||||
let node_acc = client.get_account(address.to_string()).await?.account;
|
||||
|
||||
if node_acc == nssa::Account::default() && curr_id != ChainIndex::root() {
|
||||
@ -195,7 +197,7 @@ impl KeyTree<ChildKeysPublic> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_id = curr_id.n_th_child(0);
|
||||
let mut next_id = curr_id.nth_child(0);
|
||||
|
||||
while (next_id.depth()) < depth - 1 {
|
||||
id_stack.push(next_id.clone());
|
||||
@ -211,7 +213,7 @@ impl KeyTree<ChildKeysPublic> {
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
|
||||
use super::*;
|
||||
|
||||
@ -228,7 +230,7 @@ mod tests {
|
||||
let tree = KeyTreePublic::new(&seed_holder);
|
||||
|
||||
assert!(tree.key_map.contains_key(&ChainIndex::root()));
|
||||
assert!(tree.addr_map.contains_key(&Address::new([
|
||||
assert!(tree.account_id_map.contains_key(&AccountId::new([
|
||||
46, 223, 229, 177, 59, 18, 189, 219, 153, 31, 249, 90, 112, 230, 180, 164, 80, 25, 106,
|
||||
159, 14, 238, 1, 192, 91, 8, 210, 165, 199, 41, 60, 104,
|
||||
])));
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
/// Trait, that reperesents a Node in hierarchical key tree
|
||||
pub trait KeyNode {
|
||||
/// Tree root node
|
||||
fn root(seed: [u8; 64]) -> Self;
|
||||
|
||||
fn n_th_child(&self, cci: u32) -> Self;
|
||||
/// `cci`'s child of node
|
||||
fn nth_child(&self, cci: u32) -> Self;
|
||||
|
||||
fn chain_code(&self) -> &[u8; 32];
|
||||
|
||||
fn child_index(&self) -> &Option<u32>;
|
||||
fn child_index(&self) -> Option<u32>;
|
||||
|
||||
fn address(&self) -> nssa::Address;
|
||||
fn account_id(&self) -> nssa::AccountId;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ pub mod key_tree;
|
||||
pub mod secret_holders;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
///Entrypoint to key management
|
||||
/// Entrypoint to key management
|
||||
pub struct KeyChain {
|
||||
pub secret_spending_key: SecretSpendingKey,
|
||||
pub private_key_holder: PrivateKeyHolder,
|
||||
@ -22,8 +22,8 @@ pub struct KeyChain {
|
||||
|
||||
impl KeyChain {
|
||||
pub fn new_os_random() -> Self {
|
||||
//Currently dropping SeedHolder at the end of initialization.
|
||||
//Now entirely sure if we need it in the future.
|
||||
// Currently dropping SeedHolder at the end of initialization.
|
||||
// Now entirely sure if we need it in the future.
|
||||
let seed_holder = SeedHolder::new_os_random();
|
||||
let secret_spending_key = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
@ -41,8 +41,8 @@ impl KeyChain {
|
||||
}
|
||||
|
||||
pub fn new_mnemonic(passphrase: String) -> Self {
|
||||
//Currently dropping SeedHolder at the end of initialization.
|
||||
//Now entirely sure if we need it in the future.
|
||||
// Currently dropping SeedHolder at the end of initialization.
|
||||
// Not entirely sure if we need it in the future.
|
||||
let seed_holder = SeedHolder::new_mnemonic(passphrase);
|
||||
let secret_spending_key = seed_holder.produce_top_secret_key_holder();
|
||||
|
||||
@ -76,8 +76,7 @@ impl KeyChain {
|
||||
mod tests {
|
||||
use aes_gcm::aead::OsRng;
|
||||
use base58::ToBase58;
|
||||
use k256::AffinePoint;
|
||||
use k256::elliptic_curve::group::GroupEncoding;
|
||||
use k256::{AffinePoint, elliptic_curve::group::GroupEncoding};
|
||||
use rand::RngCore;
|
||||
|
||||
use super::*;
|
||||
@ -85,15 +84,18 @@ mod tests {
|
||||
#[test]
|
||||
fn test_new_os_random() {
|
||||
// Ensure that a new KeyChain instance can be created without errors.
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
let account_id_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Check that key holder fields are initialized with expected types
|
||||
assert_ne!(address_key_holder.nullifer_public_key.as_ref(), &[0u8; 32]);
|
||||
assert_ne!(
|
||||
account_id_key_holder.nullifer_public_key.as_ref(),
|
||||
&[0u8; 32]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_shared_secret_receiver() {
|
||||
let address_key_holder = KeyChain::new_os_random();
|
||||
let account_id_key_holder = KeyChain::new_os_random();
|
||||
|
||||
// Generate a random ephemeral public key sender
|
||||
let mut scalar = [0; 32];
|
||||
@ -102,7 +104,7 @@ mod tests {
|
||||
|
||||
// Calculate shared secret
|
||||
let _shared_secret =
|
||||
address_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
account_id_key_holder.calculate_shared_secret_receiver(ephemeral_public_key_sender);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -119,7 +121,7 @@ mod tests {
|
||||
|
||||
let public_key = nssa::PublicKey::new_from_private_key(&pub_account_signing_key);
|
||||
|
||||
let address = nssa::Address::from(&public_key);
|
||||
let account = nssa::AccountId::from(&public_key);
|
||||
|
||||
println!("======Prerequisites======");
|
||||
println!();
|
||||
@ -140,7 +142,7 @@ mod tests {
|
||||
|
||||
println!("======Public data======");
|
||||
println!();
|
||||
println!("Address{:?}", address.value().to_base58());
|
||||
println!("Account {:?}", account.value().to_base58());
|
||||
println!(
|
||||
"Nulifier public key {:?}",
|
||||
hex::encode(nullifer_public_key.to_byte_array())
|
||||
|
||||
@ -8,23 +8,26 @@ use rand::{RngCore, rngs::OsRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
const NSSA_ENTROPY_BYTES: [u8; 32] = [0; 32];
|
||||
|
||||
#[derive(Debug)]
|
||||
///Seed holder. Non-clonable to ensure that different holders use different seeds.
|
||||
/// Seed holder. Non-clonable to ensure that different holders use different seeds.
|
||||
/// Produces `TopSecretKeyHolder` objects.
|
||||
pub struct SeedHolder {
|
||||
//ToDo: Needs to be vec as serde derives is not implemented for [u8; 64]
|
||||
// ToDo: Needs to be vec as serde derives is not implemented for [u8; 64]
|
||||
pub(crate) seed: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
///Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||
/// Secret spending key object. Can produce `PrivateKeyHolder` objects.
|
||||
pub struct SecretSpendingKey(pub(crate) [u8; 32]);
|
||||
|
||||
pub type IncomingViewingSecretKey = Scalar;
|
||||
pub type OutgoingViewingSecretKey = Scalar;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
///Private key holder. Produces public keys. Can produce address. Can produce shared secret for recepient.
|
||||
/// Private key holder. Produces public keys. Can produce account_id. Can produce shared secret for
|
||||
/// recepient.
|
||||
pub struct PrivateKeyHolder {
|
||||
pub nullifier_secret_key: NullifierSecretKey,
|
||||
pub(crate) incoming_viewing_secret_key: IncomingViewingSecretKey,
|
||||
@ -36,7 +39,8 @@ impl SeedHolder {
|
||||
let mut enthopy_bytes: [u8; 32] = [0; 32];
|
||||
OsRng.fill_bytes(&mut enthopy_bytes);
|
||||
|
||||
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap();
|
||||
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes)
|
||||
.expect("Enthropy must be a multiple of 32 bytes");
|
||||
let seed_wide = mnemonic.to_seed("mnemonic");
|
||||
|
||||
Self {
|
||||
@ -45,10 +49,8 @@ impl SeedHolder {
|
||||
}
|
||||
|
||||
pub fn new_mnemonic(passphrase: String) -> Self {
|
||||
//Enthropy bytes must be deterministic as well
|
||||
let enthopy_bytes: [u8; 32] = [0; 32];
|
||||
|
||||
let mnemonic = Mnemonic::from_entropy(&enthopy_bytes).unwrap();
|
||||
let mnemonic = Mnemonic::from_entropy(&NSSA_ENTROPY_BYTES)
|
||||
.expect("Enthropy must be a multiple of 32 bytes");
|
||||
let seed_wide = mnemonic.to_seed(passphrase);
|
||||
|
||||
Self {
|
||||
@ -63,7 +65,7 @@ impl SeedHolder {
|
||||
hash = hmac_sha512::HMAC::mac(hash, "NSSA_seed");
|
||||
}
|
||||
|
||||
//Safe unwrap
|
||||
// Safe unwrap
|
||||
*hash.first_chunk::<32>().unwrap()
|
||||
}
|
||||
|
||||
|
||||
@ -14,11 +14,11 @@ pub type PublicKey = AffinePoint;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct NSSAUserData {
|
||||
///Default public accounts
|
||||
pub default_pub_account_signing_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
||||
///Default private accounts
|
||||
/// Default public accounts
|
||||
pub default_pub_account_signing_keys: HashMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
/// Default private accounts
|
||||
pub default_user_private_accounts:
|
||||
HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
|
||||
HashMap<nssa::AccountId, (KeyChain, nssa_core::account::Account)>,
|
||||
/// Tree of public keys
|
||||
pub public_key_tree: KeyTreePublic,
|
||||
/// Tree of private keys
|
||||
@ -27,13 +27,14 @@ pub struct NSSAUserData {
|
||||
|
||||
impl NSSAUserData {
|
||||
fn valid_public_key_transaction_pairing_check(
|
||||
accounts_keys_map: &HashMap<nssa::Address, nssa::PrivateKey>,
|
||||
accounts_keys_map: &HashMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (addr, key) in accounts_keys_map {
|
||||
let expected_addr = nssa::Address::from(&nssa::PublicKey::new_from_private_key(key));
|
||||
if &expected_addr != addr {
|
||||
println!("{}, {}", expected_addr, addr);
|
||||
for (account_id, key) in accounts_keys_map {
|
||||
let expected_account_id =
|
||||
nssa::AccountId::from(&nssa::PublicKey::new_from_private_key(key));
|
||||
if &expected_account_id != account_id {
|
||||
println!("{}, {}", expected_account_id, account_id);
|
||||
check_res = false;
|
||||
}
|
||||
}
|
||||
@ -41,13 +42,13 @@ impl NSSAUserData {
|
||||
}
|
||||
|
||||
fn valid_private_key_transaction_pairing_check(
|
||||
accounts_keys_map: &HashMap<nssa::Address, (KeyChain, nssa_core::account::Account)>,
|
||||
accounts_keys_map: &HashMap<nssa::AccountId, (KeyChain, nssa_core::account::Account)>,
|
||||
) -> bool {
|
||||
let mut check_res = true;
|
||||
for (addr, (key, _)) in accounts_keys_map {
|
||||
let expected_addr = nssa::Address::from(&key.nullifer_public_key);
|
||||
if expected_addr != *addr {
|
||||
println!("{}, {}", expected_addr, addr);
|
||||
for (account_id, (key, _)) in accounts_keys_map {
|
||||
let expected_account_id = nssa::AccountId::from(&key.nullifer_public_key);
|
||||
if expected_account_id != *account_id {
|
||||
println!("{}, {}", expected_account_id, account_id);
|
||||
check_res = false;
|
||||
}
|
||||
}
|
||||
@ -55,9 +56,9 @@ impl NSSAUserData {
|
||||
}
|
||||
|
||||
pub fn new_with_accounts(
|
||||
default_accounts_keys: HashMap<nssa::Address, nssa::PrivateKey>,
|
||||
default_accounts_keys: HashMap<nssa::AccountId, nssa::PrivateKey>,
|
||||
default_accounts_key_chains: HashMap<
|
||||
nssa::Address,
|
||||
nssa::AccountId,
|
||||
(KeyChain, nssa_core::account::Account),
|
||||
>,
|
||||
public_key_tree: KeyTreePublic,
|
||||
@ -65,13 +66,13 @@ impl NSSAUserData {
|
||||
) -> Result<Self> {
|
||||
if !Self::valid_public_key_transaction_pairing_check(&default_accounts_keys) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"
|
||||
"Key transaction pairing check not satisfied, there is account_ids, which is not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
if !Self::valid_private_key_transaction_pairing_check(&default_accounts_key_chains) {
|
||||
anyhow::bail!(
|
||||
"Key transaction pairing check not satisfied, there is addresses, which is not derived from keys"
|
||||
"Key transaction pairing check not satisfied, there is account_ids, which is not derived from keys"
|
||||
);
|
||||
}
|
||||
|
||||
@ -85,63 +86,65 @@ impl NSSAUserData {
|
||||
|
||||
/// Generated new private key for public transaction signatures
|
||||
///
|
||||
/// Returns the address of new account
|
||||
/// Returns the account_id of new account
|
||||
pub fn generate_new_public_transaction_private_key(
|
||||
&mut self,
|
||||
parent_cci: ChainIndex,
|
||||
) -> nssa::Address {
|
||||
) -> nssa::AccountId {
|
||||
self.public_key_tree.generate_new_node(parent_cci).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures
|
||||
pub fn get_pub_account_signing_key(
|
||||
&self,
|
||||
address: &nssa::Address,
|
||||
account_id: &nssa::AccountId,
|
||||
) -> Option<&nssa::PrivateKey> {
|
||||
//First seek in defaults
|
||||
if let Some(key) = self.default_pub_account_signing_keys.get(address) {
|
||||
// First seek in defaults
|
||||
if let Some(key) = self.default_pub_account_signing_keys.get(account_id) {
|
||||
Some(key)
|
||||
//Then seek in tree
|
||||
// Then seek in tree
|
||||
} else {
|
||||
self.public_key_tree.get_node(*address).map(Into::into)
|
||||
self.public_key_tree.get_node(*account_id).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated new private key for privacy preserving transactions
|
||||
///
|
||||
/// Returns the address of new account
|
||||
/// Returns the account_id of new account
|
||||
pub fn generate_new_privacy_preserving_transaction_key_chain(
|
||||
&mut self,
|
||||
parent_cci: ChainIndex,
|
||||
) -> nssa::Address {
|
||||
) -> nssa::AccountId {
|
||||
self.private_key_tree.generate_new_node(parent_cci).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures
|
||||
pub fn get_private_account(
|
||||
&self,
|
||||
address: &nssa::Address,
|
||||
account_id: &nssa::AccountId,
|
||||
) -> Option<&(KeyChain, nssa_core::account::Account)> {
|
||||
//First seek in defaults
|
||||
if let Some(key) = self.default_user_private_accounts.get(address) {
|
||||
// First seek in defaults
|
||||
if let Some(key) = self.default_user_private_accounts.get(account_id) {
|
||||
Some(key)
|
||||
//Then seek in tree
|
||||
// Then seek in tree
|
||||
} else {
|
||||
self.private_key_tree.get_node(*address).map(Into::into)
|
||||
self.private_key_tree.get_node(*account_id).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the signing key for public transaction signatures
|
||||
pub fn get_private_account_mut(
|
||||
&mut self,
|
||||
address: &nssa::Address,
|
||||
account_id: &nssa::AccountId,
|
||||
) -> Option<&mut (KeyChain, nssa_core::account::Account)> {
|
||||
//First seek in defaults
|
||||
if let Some(key) = self.default_user_private_accounts.get_mut(address) {
|
||||
// First seek in defaults
|
||||
if let Some(key) = self.default_user_private_accounts.get_mut(account_id) {
|
||||
Some(key)
|
||||
//Then seek in tree
|
||||
// Then seek in tree
|
||||
} else {
|
||||
self.private_key_tree.get_node_mut(*address).map(Into::into)
|
||||
self.private_key_tree
|
||||
.get_node_mut(*account_id)
|
||||
.map(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,21 +169,27 @@ mod tests {
|
||||
fn test_new_account() {
|
||||
let mut user_data = NSSAUserData::default();
|
||||
|
||||
let addr_pub = user_data.generate_new_public_transaction_private_key(ChainIndex::root());
|
||||
let addr_private =
|
||||
let account_id_pub =
|
||||
user_data.generate_new_public_transaction_private_key(ChainIndex::root());
|
||||
let account_id_private =
|
||||
user_data.generate_new_privacy_preserving_transaction_key_chain(ChainIndex::root());
|
||||
|
||||
let is_private_key_generated = user_data.get_pub_account_signing_key(&addr_pub).is_some();
|
||||
let is_private_key_generated = user_data
|
||||
.get_pub_account_signing_key(&account_id_pub)
|
||||
.is_some();
|
||||
|
||||
assert!(is_private_key_generated);
|
||||
|
||||
let is_key_chain_generated = user_data.get_private_account(&addr_private).is_some();
|
||||
let is_key_chain_generated = user_data.get_private_account(&account_id_private).is_some();
|
||||
|
||||
assert!(is_key_chain_generated);
|
||||
|
||||
let addr_private_str = addr_private.to_string();
|
||||
println!("{addr_private_str:#?}");
|
||||
let key_chain = &user_data.get_private_account(&addr_private).unwrap().0;
|
||||
let account_id_private_str = account_id_private.to_string();
|
||||
println!("{account_id_private_str:#?}");
|
||||
let key_chain = &user_data
|
||||
.get_private_account(&account_id_private)
|
||||
.unwrap()
|
||||
.0;
|
||||
println!("{key_chain:#?}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,3 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@ -1,231 +1,99 @@
|
||||
use std::collections::VecDeque;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
pub struct MemPool<Item> {
|
||||
items: VecDeque<Item>,
|
||||
pub struct MemPool<T> {
|
||||
receiver: Receiver<T>,
|
||||
}
|
||||
|
||||
impl<Item> MemPool<Item> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
items: VecDeque::new(),
|
||||
}
|
||||
impl<T> MemPool<T> {
|
||||
pub fn new(max_size: usize) -> (Self, MemPoolHandle<T>) {
|
||||
let (sender, receiver) = tokio::sync::mpsc::channel(max_size);
|
||||
|
||||
let mem_pool = Self { receiver };
|
||||
let sender = MemPoolHandle::new(sender);
|
||||
(mem_pool, sender)
|
||||
}
|
||||
|
||||
pub fn pop_last(&mut self) -> Option<Item> {
|
||||
self.items.pop_front()
|
||||
}
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
use tokio::sync::mpsc::error::TryRecvError;
|
||||
|
||||
pub fn peek_last(&self) -> Option<&Item> {
|
||||
self.items.front()
|
||||
}
|
||||
|
||||
pub fn push_item(&mut self, item: Item) {
|
||||
self.items.push_back(item);
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.items.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn pop_size(&mut self, size: usize) -> Vec<Item> {
|
||||
let mut ret_vec = vec![];
|
||||
|
||||
for _ in 0..size {
|
||||
let item = self.pop_last();
|
||||
|
||||
match item {
|
||||
Some(item) => ret_vec.push(item),
|
||||
None => break,
|
||||
match self.receiver.try_recv() {
|
||||
Ok(item) => Some(item),
|
||||
Err(TryRecvError::Empty) => None,
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
panic!("Mempool senders disconnected, cannot receive items, this is a bug")
|
||||
}
|
||||
}
|
||||
|
||||
ret_vec
|
||||
}
|
||||
|
||||
pub fn drain_size(&mut self, remainder: usize) -> Vec<Item> {
|
||||
self.pop_size(self.len().saturating_sub(remainder))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item> Default for MemPool<Item> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
pub struct MemPoolHandle<T> {
|
||||
sender: Sender<T>,
|
||||
}
|
||||
|
||||
impl<T> MemPoolHandle<T> {
|
||||
fn new(sender: Sender<T>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
/// Send an item to the mempool blocking if max size is reached
|
||||
pub async fn push(&self, item: T) -> Result<(), tokio::sync::mpsc::error::SendError<T>> {
|
||||
self.sender.send(item).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::vec;
|
||||
use tokio::test;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type ItemId = u64;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TestItem {
|
||||
id: ItemId,
|
||||
}
|
||||
|
||||
fn test_item_with_id(id: u64) -> TestItem {
|
||||
TestItem { id }
|
||||
#[test]
|
||||
async fn test_mempool_new() {
|
||||
let (mut pool, _handle): (MemPool<u64>, _) = MemPool::new(10);
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_empty_mempool() {
|
||||
let _: MemPool<TestItem> = MemPool::new();
|
||||
async fn test_push_and_pop() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
|
||||
let item = pool.pop();
|
||||
assert_eq!(item, Some(1));
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mempool_new() {
|
||||
let pool: MemPool<TestItem> = MemPool::new();
|
||||
assert!(pool.is_empty());
|
||||
assert_eq!(pool.len(), 0);
|
||||
async fn test_multiple_push_pop() {
|
||||
let (mut pool, handle) = MemPool::new(10);
|
||||
|
||||
handle.push(1).await.unwrap();
|
||||
handle.push(2).await.unwrap();
|
||||
handle.push(3).await.unwrap();
|
||||
|
||||
assert_eq!(pool.pop(), Some(1));
|
||||
assert_eq!(pool.pop(), Some(2));
|
||||
assert_eq!(pool.pop(), Some(3));
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_item() {
|
||||
let mut pool = MemPool::new();
|
||||
pool.push_item(test_item_with_id(1));
|
||||
assert!(!pool.is_empty());
|
||||
assert_eq!(pool.len(), 1);
|
||||
async fn test_pop_empty() {
|
||||
let (mut pool, _handle): (MemPool<u64>, _) = MemPool::new(10);
|
||||
assert_eq!(pool.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop_last() {
|
||||
let mut pool = MemPool::new();
|
||||
pool.push_item(test_item_with_id(1));
|
||||
pool.push_item(test_item_with_id(2));
|
||||
let item = pool.pop_last();
|
||||
assert_eq!(item, Some(test_item_with_id(1)));
|
||||
assert_eq!(pool.len(), 1);
|
||||
}
|
||||
async fn test_max_size() {
|
||||
let (mut pool, handle) = MemPool::new(2);
|
||||
|
||||
#[test]
|
||||
fn test_peek_last() {
|
||||
let mut pool = MemPool::new();
|
||||
pool.push_item(test_item_with_id(1));
|
||||
pool.push_item(test_item_with_id(2));
|
||||
let item = pool.peek_last();
|
||||
assert_eq!(item, Some(&test_item_with_id(1)));
|
||||
}
|
||||
handle.push(1).await.unwrap();
|
||||
handle.push(2).await.unwrap();
|
||||
|
||||
#[test]
|
||||
fn test_pop_size() {
|
||||
let mut pool = MemPool::new();
|
||||
pool.push_item(test_item_with_id(1));
|
||||
pool.push_item(test_item_with_id(2));
|
||||
pool.push_item(test_item_with_id(3));
|
||||
|
||||
let items = pool.pop_size(2);
|
||||
assert_eq!(items, vec![test_item_with_id(1), test_item_with_id(2)]);
|
||||
assert_eq!(pool.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drain_size() {
|
||||
let mut pool = MemPool::new();
|
||||
pool.push_item(test_item_with_id(1));
|
||||
pool.push_item(test_item_with_id(2));
|
||||
pool.push_item(test_item_with_id(3));
|
||||
pool.push_item(test_item_with_id(4));
|
||||
|
||||
let items = pool.drain_size(2);
|
||||
assert_eq!(items, vec![test_item_with_id(1), test_item_with_id(2)]);
|
||||
assert_eq!(pool.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default() {
|
||||
let pool: MemPool<TestItem> = MemPool::default();
|
||||
assert!(pool.is_empty());
|
||||
assert_eq!(pool.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_empty() {
|
||||
let mut pool = MemPool::new();
|
||||
assert!(pool.is_empty());
|
||||
pool.push_item(test_item_with_id(1));
|
||||
assert!(!pool.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_pop() {
|
||||
let mut mempool: MemPool<TestItem> = MemPool::new();
|
||||
|
||||
let items = vec![
|
||||
test_item_with_id(1),
|
||||
test_item_with_id(2),
|
||||
test_item_with_id(3),
|
||||
];
|
||||
|
||||
for item in items {
|
||||
mempool.push_item(item);
|
||||
}
|
||||
assert_eq!(mempool.len(), 3);
|
||||
|
||||
let item = mempool.pop_last();
|
||||
|
||||
assert_eq!(item, Some(TestItem { id: 1 }));
|
||||
assert_eq!(mempool.len(), 2);
|
||||
|
||||
let item = mempool.pop_last();
|
||||
|
||||
assert_eq!(item, Some(TestItem { id: 2 }));
|
||||
assert_eq!(mempool.len(), 1);
|
||||
|
||||
let item = mempool.pop_last();
|
||||
|
||||
assert_eq!(item, Some(TestItem { id: 3 }));
|
||||
assert_eq!(mempool.len(), 0);
|
||||
|
||||
let item = mempool.pop_last();
|
||||
|
||||
assert_eq!(item, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop_many() {
|
||||
let mut mempool: MemPool<TestItem> = MemPool::new();
|
||||
|
||||
let mut items = vec![];
|
||||
|
||||
for i in 1..11 {
|
||||
items.push(test_item_with_id(i));
|
||||
}
|
||||
|
||||
for item in items {
|
||||
mempool.push_item(item);
|
||||
}
|
||||
|
||||
assert_eq!(mempool.len(), 10);
|
||||
|
||||
let items1 = mempool.pop_size(4);
|
||||
assert_eq!(
|
||||
items1,
|
||||
vec![
|
||||
test_item_with_id(1),
|
||||
test_item_with_id(2),
|
||||
test_item_with_id(3),
|
||||
test_item_with_id(4)
|
||||
]
|
||||
);
|
||||
assert_eq!(mempool.len(), 6);
|
||||
|
||||
let items2 = mempool.drain_size(2);
|
||||
assert_eq!(
|
||||
items2,
|
||||
vec![
|
||||
test_item_with_id(5),
|
||||
test_item_with_id(6),
|
||||
test_item_with_id(7),
|
||||
test_item_with_id(8)
|
||||
]
|
||||
);
|
||||
assert_eq!(mempool.len(), 2);
|
||||
// This should block if buffer is full, but we'll use try_send in a real scenario
|
||||
// For now, just verify we can pop items
|
||||
assert_eq!(pool.pop(), Some(1));
|
||||
assert_eq!(pool.pop(), Some(2));
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ chacha20 = { version = "0.9", default-features = false }
|
||||
k256 = { version = "0.13.3", optional = true }
|
||||
base58 = { version = "0.2.0", optional = true }
|
||||
anyhow = { version = "1.0.98", optional = true }
|
||||
borsh.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
use crate::{address::Address, program::ProgramId};
|
||||
#[cfg(feature = "host")]
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use base58::{FromBase58, ToBase58};
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::program::ProgramId;
|
||||
|
||||
pub type Nonce = u128;
|
||||
pub type Data = Vec<u8>;
|
||||
|
||||
/// Account to be used both in public and private contexts
|
||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Default, PartialEq, Eq, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug))]
|
||||
pub struct Account {
|
||||
pub program_owner: ProgramId,
|
||||
@ -14,8 +23,6 @@ pub struct Account {
|
||||
pub nonce: Nonce,
|
||||
}
|
||||
|
||||
pub type AccountId = Address;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))]
|
||||
pub struct AccountWithMetadata {
|
||||
@ -35,11 +42,68 @@ impl AccountWithMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Debug, Copy, PartialOrd, Ord, Default)
|
||||
)]
|
||||
pub struct AccountId {
|
||||
value: [u8; 32],
|
||||
}
|
||||
|
||||
impl AccountId {
|
||||
pub fn new(value: [u8; 32]) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &[u8; 32] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for AccountId {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AccountIdError {
|
||||
#[error("invalid base58")]
|
||||
InvalidBase58(#[from] anyhow::Error),
|
||||
#[error("invalid length: expected 32 bytes, got {0}")]
|
||||
InvalidLength(usize),
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl FromStr for AccountId {
|
||||
type Err = AccountIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = s
|
||||
.from_base58()
|
||||
.map_err(|err| anyhow::anyhow!("Invalid base58 err {err:?}"))?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(AccountIdError::InvalidLength(bytes.len()));
|
||||
}
|
||||
let mut value = [0u8; 32];
|
||||
value.copy_from_slice(&bytes);
|
||||
Ok(AccountId { value })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl Display for AccountId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value.to_base58())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::program::DEFAULT_PROGRAM_ID;
|
||||
|
||||
use super::*;
|
||||
use crate::program::DEFAULT_PROGRAM_ID;
|
||||
|
||||
#[test]
|
||||
fn test_zero_balance_account_data_creation() {
|
||||
@ -84,4 +148,32 @@ mod tests {
|
||||
assert!(new_acc_with_metadata.is_authorized);
|
||||
assert_eq!(new_acc_with_metadata.account_id, fingerprint);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_valid_account_id() {
|
||||
let base58_str = "11111111111111111111111111111111";
|
||||
let account_id: AccountId = base58_str.parse().unwrap();
|
||||
assert_eq!(account_id.value, [0u8; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_base58() {
|
||||
let base58_str = "00".repeat(32); // invalid base58 chars
|
||||
let result = base58_str.parse::<AccountId>().unwrap_err();
|
||||
assert!(matches!(result, AccountIdError::InvalidBase58(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_wrong_length_short() {
|
||||
let base58_str = "11".repeat(31); // 62 chars = 31 bytes
|
||||
let result = base58_str.parse::<AccountId>().unwrap_err();
|
||||
assert!(matches!(result, AccountIdError::InvalidLength(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_wrong_length_long() {
|
||||
let base58_str = "11".repeat(33); // 66 chars = 33 bytes
|
||||
let result = base58_str.parse::<AccountId>().unwrap_err();
|
||||
assert!(matches!(result, AccountIdError::InvalidLength(_)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,98 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use base58::{FromBase58, ToBase58};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(
|
||||
any(feature = "host", test),
|
||||
derive(Debug, Copy, PartialOrd, Ord, Default)
|
||||
)]
|
||||
pub struct Address {
|
||||
value: [u8; 32],
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn new(value: [u8; 32]) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &[u8; 32] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Address {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AddressError {
|
||||
#[error("invalid base58")]
|
||||
InvalidBase58(#[from] anyhow::Error),
|
||||
#[error("invalid length: expected 32 bytes, got {0}")]
|
||||
InvalidLength(usize),
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl FromStr for Address {
|
||||
type Err = AddressError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = s
|
||||
.from_base58()
|
||||
.map_err(|err| anyhow::anyhow!("Invalid base58 err {err:?}"))?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(AddressError::InvalidLength(bytes.len()));
|
||||
}
|
||||
let mut value = [0u8; 32];
|
||||
value.copy_from_slice(&bytes);
|
||||
Ok(Address { value })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
impl Display for Address {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value.to_base58())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{Address, AddressError};
|
||||
|
||||
#[test]
|
||||
fn parse_valid_address() {
|
||||
let base58_str = "11111111111111111111111111111111";
|
||||
let addr: Address = base58_str.parse().unwrap();
|
||||
assert_eq!(addr.value, [0u8; 32]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_base58() {
|
||||
let base58_str = "00".repeat(32); // invalid base58 chars
|
||||
let result = base58_str.parse::<Address>().unwrap_err();
|
||||
assert!(matches!(result, AddressError::InvalidBase58(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_wrong_length_short() {
|
||||
let base58_str = "11".repeat(31); // 62 chars = 31 bytes
|
||||
let result = base58_str.parse::<Address>().unwrap_err();
|
||||
assert!(matches!(result, AddressError::InvalidLength(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_wrong_length_long() {
|
||||
let base58_str = "11".repeat(33); // 66 chars = 33 bytes
|
||||
let result = base58_str.parse::<Address>().unwrap_err();
|
||||
assert!(matches!(result, AddressError::InvalidLength(_)));
|
||||
}
|
||||
}
|
||||
@ -38,12 +38,13 @@ impl PrivacyPreservingCircuitOutput {
|
||||
#[cfg(feature = "host")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use risc0_zkvm::serde::from_slice;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
Commitment, Nullifier, NullifierPublicKey,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
};
|
||||
use risc0_zkvm::serde::from_slice;
|
||||
|
||||
#[test]
|
||||
fn test_privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() {
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{NullifierPublicKey, account::Account};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))]
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
|
||||
@ -1,25 +1,20 @@
|
||||
// TODO: Consider switching to deriving Borsh
|
||||
#[cfg(feature = "host")]
|
||||
use std::io::Cursor;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use std::io::Read;
|
||||
|
||||
use crate::account::Account;
|
||||
|
||||
use crate::account::AccountId;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::encryption::shared_key_derivation::Secp256k1Point;
|
||||
|
||||
use crate::encryption::Ciphertext;
|
||||
|
||||
#[cfg(feature = "host")]
|
||||
use crate::error::NssaCoreError;
|
||||
|
||||
use crate::Commitment;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::Nullifier;
|
||||
use crate::NullifierPublicKey;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::encryption::shared_key_derivation::Secp256k1Point;
|
||||
#[cfg(feature = "host")]
|
||||
use crate::error::NssaCoreError;
|
||||
use crate::{
|
||||
Commitment, NullifierPublicKey,
|
||||
account::{Account, AccountId},
|
||||
encryption::Ciphertext,
|
||||
};
|
||||
|
||||
impl Account {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@ -55,7 +50,7 @@ impl Account {
|
||||
cursor.read_exact(&mut u128_bytes)?;
|
||||
let nonce = u128::from_le_bytes(u128_bytes);
|
||||
|
||||
//data
|
||||
// data
|
||||
cursor.read_exact(&mut u32_bytes)?;
|
||||
let data_length = u32::from_le_bytes(u32_bytes);
|
||||
let mut data = vec![0; data_length as usize];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use chacha20::{
|
||||
ChaCha20,
|
||||
cipher::{KeyIvInit, StreamCipher},
|
||||
@ -20,7 +21,7 @@ pub struct SharedSecretKey(pub [u8; 32]);
|
||||
|
||||
pub struct EncryptionScheme;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))]
|
||||
pub struct Ciphertext(pub(crate) Vec<u8>);
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use k256::{
|
||||
AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint,
|
||||
elliptic_curve::{
|
||||
@ -7,10 +6,11 @@ use k256::{
|
||||
sec1::{FromEncodedPoint, ToEncodedPoint},
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{SharedSecretKey, encryption::Scalar};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Secp256k1Point(pub Vec<u8>);
|
||||
|
||||
impl Secp256k1Point {
|
||||
|
||||
@ -6,8 +6,6 @@ pub mod encryption;
|
||||
mod nullifier;
|
||||
pub mod program;
|
||||
|
||||
pub mod address;
|
||||
|
||||
pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput};
|
||||
pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -40,7 +41,7 @@ impl From<&NullifierSecretKey> for NullifierPublicKey {
|
||||
|
||||
pub type NullifierSecretKey = [u8; 32];
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))]
|
||||
pub struct Nullifier(pub(super) [u8; 32]);
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::account::{Account, AccountWithMetadata};
|
||||
use risc0_zkvm::serde::Deserializer;
|
||||
use risc0_zkvm::{DeserializeOwned, guest::env};
|
||||
use risc0_zkvm::{DeserializeOwned, guest::env, serde::Deserializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::account::{Account, AccountWithMetadata};
|
||||
|
||||
pub type ProgramId = [u32; 8];
|
||||
pub type InstructionData = Vec<u32>;
|
||||
pub const DEFAULT_PROGRAM_ID: ProgramId = [0; 8];
|
||||
@ -17,7 +17,7 @@ pub struct ProgramInput<T> {
|
||||
pub struct ChainedCall {
|
||||
pub program_id: ProgramId,
|
||||
pub instruction_data: InstructionData,
|
||||
pub account_indices: Vec<usize>,
|
||||
pub pre_states: Vec<AccountWithMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -25,7 +25,7 @@ pub struct ChainedCall {
|
||||
pub struct ProgramOutput {
|
||||
pub pre_states: Vec<AccountWithMetadata>,
|
||||
pub post_states: Vec<Account>,
|
||||
pub chained_call: Option<ChainedCall>,
|
||||
pub chained_calls: Vec<ChainedCall>,
|
||||
}
|
||||
|
||||
pub fn read_nssa_inputs<T: DeserializeOwned>() -> ProgramInput<T> {
|
||||
@ -42,7 +42,7 @@ pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec
|
||||
let output = ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
chained_call: None,
|
||||
chained_calls: Vec::new(),
|
||||
};
|
||||
env::commit(&output);
|
||||
}
|
||||
@ -50,12 +50,12 @@ pub fn write_nssa_outputs(pre_states: Vec<AccountWithMetadata>, post_states: Vec
|
||||
pub fn write_nssa_outputs_with_chained_call(
|
||||
pre_states: Vec<AccountWithMetadata>,
|
||||
post_states: Vec<Account>,
|
||||
chained_call: Option<ChainedCall>,
|
||||
chained_calls: Vec<ChainedCall>,
|
||||
) {
|
||||
let output = ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
chained_call,
|
||||
chained_calls,
|
||||
};
|
||||
env::commit(&output);
|
||||
}
|
||||
@ -103,7 +103,8 @@ pub fn validate_execution(
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. If a post state has default program owner, the pre state must have been a default account
|
||||
// 6. If a post state has default program owner, the pre state must have been a default
|
||||
// account
|
||||
if post.program_owner == DEFAULT_PROGRAM_ID && pre.account != Account::default() {
|
||||
return false;
|
||||
}
|
||||
|
||||
1
nssa/program_methods/guest/Cargo.lock
generated
1
nssa/program_methods/guest/Cargo.lock
generated
@ -1574,6 +1574,7 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e"
|
||||
name = "nssa-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"chacha20",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
|
||||
@ -27,11 +27,11 @@ fn main() {
|
||||
let ProgramOutput {
|
||||
pre_states,
|
||||
post_states,
|
||||
chained_call,
|
||||
chained_calls,
|
||||
} = program_output;
|
||||
|
||||
// TODO: implement chained calls for privacy preserving transactions
|
||||
if chained_call.is_some() {
|
||||
if !chained_calls.is_empty() {
|
||||
panic!("Privacy preserving transactions do not support yet chained calls.")
|
||||
}
|
||||
|
||||
|
||||
@ -1,258 +1,24 @@
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, Nullifier,
|
||||
account::Account,
|
||||
encryption::{Ciphertext, EphemeralPublicKey},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Address, PrivacyPreservingTransaction, PublicKey, Signature,
|
||||
error::NssaError,
|
||||
privacy_preserving_transaction::{
|
||||
circuit::Proof,
|
||||
message::{EncryptedAccountData, Message},
|
||||
witness_set::WitnessSet,
|
||||
},
|
||||
PrivacyPreservingTransaction, error::NssaError,
|
||||
privacy_preserving_transaction::message::Message,
|
||||
};
|
||||
|
||||
const MESSAGE_ENCODING_PREFIX_LEN: usize = 32;
|
||||
const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] =
|
||||
b"/NSSA/v0.2/TxMessage/Private/\x00\x00\x00";
|
||||
|
||||
impl EncryptedAccountData {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = self.ciphertext.to_bytes();
|
||||
bytes.extend_from_slice(&self.epk.to_bytes());
|
||||
bytes.push(self.view_tag);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let ciphertext = Ciphertext::from_cursor(cursor)?;
|
||||
let epk = EphemeralPublicKey::from_cursor(cursor)?;
|
||||
|
||||
let mut tag_bytes = [0; 1];
|
||||
cursor.read_exact(&mut tag_bytes)?;
|
||||
let view_tag = tag_bytes[0];
|
||||
|
||||
Ok(Self {
|
||||
ciphertext,
|
||||
epk,
|
||||
view_tag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Message {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
|
||||
|
||||
// Public addresses
|
||||
let public_addresses_len: u32 = self.public_addresses.len() as u32;
|
||||
bytes.extend_from_slice(&public_addresses_len.to_le_bytes());
|
||||
for address in &self.public_addresses {
|
||||
bytes.extend_from_slice(address.value());
|
||||
}
|
||||
// Nonces
|
||||
let nonces_len = self.nonces.len() as u32;
|
||||
bytes.extend(&nonces_len.to_le_bytes());
|
||||
for nonce in &self.nonces {
|
||||
bytes.extend(&nonce.to_le_bytes());
|
||||
}
|
||||
// Public post states
|
||||
let public_post_states_len: u32 = self.public_post_states.len() as u32;
|
||||
bytes.extend_from_slice(&public_post_states_len.to_le_bytes());
|
||||
for account in &self.public_post_states {
|
||||
bytes.extend_from_slice(&account.to_bytes());
|
||||
}
|
||||
|
||||
// Encrypted post states
|
||||
let encrypted_accounts_post_states_len: u32 =
|
||||
self.encrypted_private_post_states.len() as u32;
|
||||
bytes.extend_from_slice(&encrypted_accounts_post_states_len.to_le_bytes());
|
||||
for encrypted_account in &self.encrypted_private_post_states {
|
||||
bytes.extend_from_slice(&encrypted_account.to_bytes());
|
||||
}
|
||||
|
||||
// New commitments
|
||||
let new_commitments_len: u32 = self.new_commitments.len() as u32;
|
||||
bytes.extend_from_slice(&new_commitments_len.to_le_bytes());
|
||||
for commitment in &self.new_commitments {
|
||||
bytes.extend_from_slice(&commitment.to_byte_array());
|
||||
}
|
||||
|
||||
// New nullifiers
|
||||
let new_nullifiers_len: u32 = self.new_nullifiers.len() as u32;
|
||||
bytes.extend_from_slice(&new_nullifiers_len.to_le_bytes());
|
||||
for (nullifier, commitment_set_digest) in &self.new_nullifiers {
|
||||
bytes.extend_from_slice(&nullifier.to_byte_array());
|
||||
bytes.extend_from_slice(commitment_set_digest);
|
||||
}
|
||||
|
||||
bytes
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let prefix = {
|
||||
let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN];
|
||||
cursor.read_exact(&mut this)?;
|
||||
this
|
||||
};
|
||||
if &prefix != MESSAGE_ENCODING_PREFIX {
|
||||
return Err(NssaError::TransactionDeserializationError(
|
||||
"Invalid privacy preserving message prefix".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut len_bytes = [0u8; 4];
|
||||
|
||||
// Public addresses
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let public_addresses_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut public_addresses = Vec::with_capacity(public_addresses_len);
|
||||
for _ in 0..public_addresses_len {
|
||||
let mut value = [0u8; 32];
|
||||
cursor.read_exact(&mut value)?;
|
||||
public_addresses.push(Address::new(value))
|
||||
}
|
||||
|
||||
// Nonces
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let nonces_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut nonces = Vec::with_capacity(nonces_len);
|
||||
for _ in 0..nonces_len {
|
||||
let mut buf = [0u8; 16];
|
||||
cursor.read_exact(&mut buf)?;
|
||||
nonces.push(u128::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
// Public post states
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let public_post_states_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut public_post_states = Vec::with_capacity(public_post_states_len);
|
||||
for _ in 0..public_post_states_len {
|
||||
public_post_states.push(Account::from_cursor(cursor)?);
|
||||
}
|
||||
|
||||
// Encrypted private post states
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let encrypted_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut encrypted_private_post_states = Vec::with_capacity(encrypted_len);
|
||||
for _ in 0..encrypted_len {
|
||||
encrypted_private_post_states.push(EncryptedAccountData::from_cursor(cursor)?);
|
||||
}
|
||||
|
||||
// New commitments
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let new_commitments_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut new_commitments = Vec::with_capacity(new_commitments_len);
|
||||
for _ in 0..new_commitments_len {
|
||||
new_commitments.push(Commitment::from_cursor(cursor)?);
|
||||
}
|
||||
|
||||
// New nullifiers
|
||||
cursor.read_exact(&mut len_bytes)?;
|
||||
let new_nullifiers_len = u32::from_le_bytes(len_bytes) as usize;
|
||||
let mut new_nullifiers = Vec::with_capacity(new_nullifiers_len);
|
||||
for _ in 0..new_nullifiers_len {
|
||||
let nullifier = Nullifier::from_cursor(cursor)?;
|
||||
let mut commitment_set_digest = [0; 32];
|
||||
cursor.read_exact(&mut commitment_set_digest)?;
|
||||
new_nullifiers.push((nullifier, commitment_set_digest));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
public_addresses,
|
||||
nonces,
|
||||
public_post_states,
|
||||
encrypted_private_post_states,
|
||||
new_commitments,
|
||||
new_nullifiers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WitnessSet {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let size = self.signatures_and_public_keys().len() as u32;
|
||||
bytes.extend_from_slice(&size.to_le_bytes());
|
||||
for (signature, public_key) in self.signatures_and_public_keys() {
|
||||
bytes.extend_from_slice(signature.to_bytes());
|
||||
bytes.extend_from_slice(public_key.to_bytes());
|
||||
}
|
||||
bytes.extend_from_slice(&self.proof.to_bytes());
|
||||
bytes
|
||||
}
|
||||
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let num_signatures: u32 = {
|
||||
let mut buf = [0u8; 4];
|
||||
cursor.read_exact(&mut buf)?;
|
||||
u32::from_le_bytes(buf)
|
||||
};
|
||||
let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize);
|
||||
for _i in 0..num_signatures {
|
||||
let signature = Signature::from_cursor(cursor)?;
|
||||
let public_key = PublicKey::from_cursor(cursor)?;
|
||||
signatures_and_public_keys.push((signature, public_key))
|
||||
}
|
||||
let proof = Proof::from_cursor(cursor)?;
|
||||
Ok(Self {
|
||||
signatures_and_public_keys,
|
||||
proof,
|
||||
})
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, NssaError> {
|
||||
Ok(borsh::from_slice(bytes)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivacyPreservingTransaction {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = self.message().to_bytes();
|
||||
bytes.extend_from_slice(&self.witness_set().to_bytes());
|
||||
bytes
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, NssaError> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
Self::from_cursor(&mut cursor)
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let message = Message::from_cursor(cursor)?;
|
||||
let witness_set = WitnessSet::from_cursor(cursor)?;
|
||||
Ok(PrivacyPreservingTransaction::new(message, witness_set))
|
||||
Ok(borsh::from_slice(bytes)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Proof {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let proof_len = self.0.len() as u32;
|
||||
bytes.extend_from_slice(&proof_len.to_le_bytes());
|
||||
bytes.extend_from_slice(&self.0);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let proof_len = u32_from_cursor(cursor) as usize;
|
||||
let mut proof = Vec::with_capacity(proof_len);
|
||||
|
||||
for _ in 0..proof_len {
|
||||
let mut one_byte_buf = [0u8];
|
||||
|
||||
cursor.read_exact(&mut one_byte_buf)?;
|
||||
|
||||
proof.push(one_byte_buf[0]);
|
||||
}
|
||||
Ok(Self(proof))
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@ -1,77 +1,17 @@
|
||||
// TODO: Consider switching to deriving Borsh
|
||||
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use crate::{
|
||||
ProgramDeploymentTransaction, error::NssaError, program_deployment_transaction::Message,
|
||||
};
|
||||
|
||||
const MESSAGE_ENCODING_PREFIX_LEN: usize = 32;
|
||||
const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] =
|
||||
b"/NSSA/v0.2/TxMessage/Program/\x00\x00\x00";
|
||||
|
||||
impl Message {
|
||||
/// Serializes a `Message` into bytes in the following layout:
|
||||
/// PREFIX || bytecode_len (4 bytes LE) || <bytecode>
|
||||
/// Integers are encoded in little-endian byte order, and fields appear in the above order.
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
|
||||
let bytecode_len = self.bytecode.len() as u32;
|
||||
bytes.extend(&bytecode_len.to_le_bytes());
|
||||
bytes.extend(&self.bytecode);
|
||||
bytes
|
||||
}
|
||||
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let prefix = {
|
||||
let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN];
|
||||
cursor.read_exact(&mut this)?;
|
||||
this
|
||||
};
|
||||
if &prefix != MESSAGE_ENCODING_PREFIX {
|
||||
return Err(NssaError::TransactionDeserializationError(
|
||||
"Invalid public message prefix".to_string(),
|
||||
));
|
||||
}
|
||||
let bytecode_len = u32_from_cursor(cursor)?;
|
||||
let mut bytecode = vec![0; bytecode_len as usize];
|
||||
let num_bytes = cursor.read(&mut bytecode)?;
|
||||
if num_bytes != bytecode_len as usize {
|
||||
println!("num bytes: {}", num_bytes);
|
||||
return Err(NssaError::TransactionDeserializationError(
|
||||
"Invalid number of bytes".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(Self { bytecode })
|
||||
}
|
||||
}
|
||||
use crate::{ProgramDeploymentTransaction, error::NssaError};
|
||||
|
||||
impl ProgramDeploymentTransaction {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.message.to_bytes()
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, NssaError> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
Self::from_cursor(&mut cursor)
|
||||
Ok(borsh::from_slice(bytes)?)
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let message = Message::from_cursor(cursor)?;
|
||||
Ok(Self::new(message))
|
||||
}
|
||||
}
|
||||
|
||||
fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<u32, NssaError> {
|
||||
let mut word_buf = [0u8; 4];
|
||||
cursor.read_exact(&mut word_buf)?;
|
||||
Ok(u32::from_le_bytes(word_buf))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use crate::{ProgramDeploymentTransaction, program_deployment_transaction::Message};
|
||||
|
||||
#[test]
|
||||
@ -79,8 +19,7 @@ mod tests {
|
||||
let message = Message::new(vec![0xca, 0xfe, 0xca, 0xfe, 0x01, 0x02, 0x03]);
|
||||
let tx = ProgramDeploymentTransaction::new(message);
|
||||
let bytes = tx.to_bytes();
|
||||
let mut cursor = Cursor::new(bytes.as_ref());
|
||||
let tx_from_cursor = ProgramDeploymentTransaction::from_cursor(&mut cursor).unwrap();
|
||||
assert_eq!(tx, tx_from_cursor);
|
||||
let tx_from_bytes = ProgramDeploymentTransaction::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(tx, tx_from_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,153 +1,17 @@
|
||||
// TODO: Consider switching to deriving Borsh
|
||||
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use nssa_core::program::ProgramId;
|
||||
|
||||
use crate::{
|
||||
Address, PublicKey, PublicTransaction, Signature,
|
||||
error::NssaError,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
|
||||
const MESSAGE_ENCODING_PREFIX_LEN: usize = 32;
|
||||
const MESSAGE_ENCODING_PREFIX: &[u8; MESSAGE_ENCODING_PREFIX_LEN] =
|
||||
b"/NSSA/v0.2/TxMessage/Public/\x00\x00\x00\x00";
|
||||
use crate::{PublicTransaction, error::NssaError, public_transaction::Message};
|
||||
|
||||
impl Message {
|
||||
/// Serializes a `Message` into bytes in the following layout:
|
||||
/// PREFIX || <program_id> (4 bytes LE) * 8 || addresses_len (4 bytes LE) || addresses (32 bytes * N) || nonces_len (4 bytes LE) || nonces (16 bytes LE * M) || instruction_data_len || instruction_data (4 bytes LE * K)
|
||||
/// Integers and words are encoded in little-endian byte order, and fields appear in the above order.
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = MESSAGE_ENCODING_PREFIX.to_vec();
|
||||
// program_id: [u32; 8]
|
||||
for word in &self.program_id {
|
||||
bytes.extend_from_slice(&word.to_le_bytes());
|
||||
}
|
||||
// addresses: Vec<[u8;32]>
|
||||
// serialize length as u32 little endian, then all addresses concatenated
|
||||
let addresses_len = self.addresses.len() as u32;
|
||||
bytes.extend(&addresses_len.to_le_bytes());
|
||||
for addr in &self.addresses {
|
||||
bytes.extend_from_slice(addr.value());
|
||||
}
|
||||
// nonces: Vec<u128>
|
||||
// serialize length as u32 little endian, then all nonces concatenated in LE
|
||||
let nonces_len = self.nonces.len() as u32;
|
||||
bytes.extend(&nonces_len.to_le_bytes());
|
||||
for nonce in &self.nonces {
|
||||
bytes.extend(&nonce.to_le_bytes());
|
||||
}
|
||||
// instruction_data: Vec<u32>
|
||||
// serialize length as u32 little endian, then all addresses concatenated
|
||||
let instr_len = self.instruction_data.len() as u32;
|
||||
bytes.extend(&instr_len.to_le_bytes());
|
||||
for word in &self.instruction_data {
|
||||
bytes.extend(&word.to_le_bytes());
|
||||
}
|
||||
|
||||
bytes
|
||||
}
|
||||
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let prefix = {
|
||||
let mut this = [0u8; MESSAGE_ENCODING_PREFIX_LEN];
|
||||
cursor.read_exact(&mut this)?;
|
||||
this
|
||||
};
|
||||
if &prefix != MESSAGE_ENCODING_PREFIX {
|
||||
return Err(NssaError::TransactionDeserializationError(
|
||||
"Invalid public message prefix".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let program_id: ProgramId = {
|
||||
let mut this = [0u32; 8];
|
||||
for item in &mut this {
|
||||
*item = u32_from_cursor(cursor)?;
|
||||
}
|
||||
this
|
||||
};
|
||||
let addresses_len = u32_from_cursor(cursor)?;
|
||||
let mut addresses = Vec::with_capacity(addresses_len as usize);
|
||||
for _ in 0..addresses_len {
|
||||
let mut value = [0u8; 32];
|
||||
cursor.read_exact(&mut value)?;
|
||||
addresses.push(Address::new(value))
|
||||
}
|
||||
let nonces_len = u32_from_cursor(cursor)?;
|
||||
let mut nonces = Vec::with_capacity(nonces_len as usize);
|
||||
for _ in 0..nonces_len {
|
||||
let mut buf = [0u8; 16];
|
||||
cursor.read_exact(&mut buf)?;
|
||||
nonces.push(u128::from_le_bytes(buf))
|
||||
}
|
||||
let instruction_data_len = u32_from_cursor(cursor)?;
|
||||
let mut instruction_data = Vec::with_capacity(instruction_data_len as usize);
|
||||
for _ in 0..instruction_data_len {
|
||||
let word = u32_from_cursor(cursor)?;
|
||||
instruction_data.push(word)
|
||||
}
|
||||
Ok(Self {
|
||||
program_id,
|
||||
addresses,
|
||||
nonces,
|
||||
instruction_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WitnessSet {
|
||||
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let size = self.signatures_and_public_keys().len() as u32;
|
||||
bytes.extend_from_slice(&size.to_le_bytes());
|
||||
for (signature, public_key) in self.signatures_and_public_keys() {
|
||||
bytes.extend_from_slice(signature.to_bytes());
|
||||
bytes.extend_from_slice(public_key.to_bytes());
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let num_signatures: u32 = {
|
||||
let mut buf = [0u8; 4];
|
||||
cursor.read_exact(&mut buf)?;
|
||||
u32::from_le_bytes(buf)
|
||||
};
|
||||
let mut signatures_and_public_keys = Vec::with_capacity(num_signatures as usize);
|
||||
for _i in 0..num_signatures {
|
||||
let signature = Signature::from_cursor(cursor)?;
|
||||
let public_key = PublicKey::from_cursor(cursor)?;
|
||||
signatures_and_public_keys.push((signature, public_key))
|
||||
}
|
||||
Ok(Self {
|
||||
signatures_and_public_keys,
|
||||
})
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicTransaction {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = self.message().to_bytes();
|
||||
bytes.extend_from_slice(&self.witness_set().to_bytes());
|
||||
bytes
|
||||
borsh::to_vec(&self).expect("Autoderived borsh serialization failure")
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, NssaError> {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
Self::from_cursor(&mut cursor)
|
||||
}
|
||||
|
||||
pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let message = Message::from_cursor(cursor)?;
|
||||
let witness_set = WitnessSet::from_cursor(cursor)?;
|
||||
Ok(PublicTransaction::new(message, witness_set))
|
||||
Ok(borsh::from_slice(bytes)?)
|
||||
}
|
||||
}
|
||||
|
||||
fn u32_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<u32, NssaError> {
|
||||
let mut word_buf = [0u8; 4];
|
||||
cursor.read_exact(&mut word_buf)?;
|
||||
Ok(u32::from_le_bytes(word_buf))
|
||||
}
|
||||
|
||||
@ -54,4 +54,7 @@ pub enum NssaError {
|
||||
|
||||
#[error("Program already exists")]
|
||||
ProgramAlreadyExists,
|
||||
|
||||
#[error("Chain of calls is too long")]
|
||||
MaxChainedCallsDepthExceeded,
|
||||
}
|
||||
|
||||
@ -18,14 +18,11 @@ mod signature;
|
||||
mod state;
|
||||
|
||||
pub use nssa_core::account::{Account, AccountId};
|
||||
pub use nssa_core::address::Address;
|
||||
pub use privacy_preserving_transaction::{
|
||||
PrivacyPreservingTransaction, circuit::execute_and_prove,
|
||||
};
|
||||
pub use program_deployment_transaction::ProgramDeploymentTransaction;
|
||||
pub use program_methods::PRIVACY_PRESERVING_CIRCUIT_ID;
|
||||
pub use public_transaction::PublicTransaction;
|
||||
pub use signature::PrivateKey;
|
||||
pub use signature::PublicKey;
|
||||
pub use signature::Signature;
|
||||
pub use signature::{PrivateKey, PublicKey, Signature};
|
||||
pub use state::V02State;
|
||||
|
||||
@ -43,6 +43,7 @@ impl MerkleTree {
|
||||
(1 << (capacity_depth - tree_depth)) - 1
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of levels required to hold all nodes
|
||||
fn depth(&self) -> usize {
|
||||
self.length.next_power_of_two().trailing_zeros() as usize
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey, NullifierSecretKey, PrivacyPreservingCircuitInput,
|
||||
PrivacyPreservingCircuitOutput, SharedSecretKey,
|
||||
@ -6,12 +7,14 @@ use nssa_core::{
|
||||
};
|
||||
use risc0_zkvm::{ExecutorEnv, InnerReceipt, Receipt, default_prover};
|
||||
|
||||
use crate::{error::NssaError, program::Program};
|
||||
|
||||
use crate::program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID};
|
||||
use crate::{
|
||||
error::NssaError,
|
||||
program::Program,
|
||||
program_methods::{PRIVACY_PRESERVING_CIRCUIT_ELF, PRIVACY_PRESERVING_CIRCUIT_ID},
|
||||
};
|
||||
|
||||
/// Proof of the privacy preserving execution circuit
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Proof(pub(crate) Vec<u8>);
|
||||
|
||||
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
|
||||
@ -95,6 +98,7 @@ mod tests {
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
privacy_preserving_transaction::circuit::execute_and_prove,
|
||||
program::Program,
|
||||
@ -104,8 +108,6 @@ mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() {
|
||||
let recipient_keys = test_private_account_keys_1();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, Nonce},
|
||||
@ -5,11 +6,11 @@ use nssa_core::{
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{Address, error::NssaError};
|
||||
use crate::{AccountId, error::NssaError};
|
||||
|
||||
pub type ViewTag = u8;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct EncryptedAccountData {
|
||||
pub ciphertext: Ciphertext,
|
||||
pub epk: EphemeralPublicKey,
|
||||
@ -42,9 +43,9 @@ impl EncryptedAccountData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Message {
|
||||
pub(crate) public_addresses: Vec<Address>,
|
||||
pub(crate) public_account_ids: Vec<AccountId>,
|
||||
pub(crate) nonces: Vec<Nonce>,
|
||||
pub(crate) public_post_states: Vec<Account>,
|
||||
pub encrypted_private_post_states: Vec<EncryptedAccountData>,
|
||||
@ -54,7 +55,7 @@ pub struct Message {
|
||||
|
||||
impl Message {
|
||||
pub fn try_from_circuit_output(
|
||||
public_addresses: Vec<Address>,
|
||||
public_account_ids: Vec<AccountId>,
|
||||
nonces: Vec<Nonce>,
|
||||
public_keys: Vec<(
|
||||
NullifierPublicKey,
|
||||
@ -78,7 +79,7 @@ impl Message {
|
||||
})
|
||||
.collect();
|
||||
Ok(Self {
|
||||
public_addresses,
|
||||
public_account_ids,
|
||||
nonces,
|
||||
public_post_states: output.public_post_states,
|
||||
encrypted_private_post_states,
|
||||
@ -90,8 +91,6 @@ impl Message {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::io::Cursor;
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, EncryptionScheme, Nullifier, NullifierPublicKey, SharedSecretKey,
|
||||
account::Account,
|
||||
@ -100,7 +99,7 @@ pub mod tests {
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{
|
||||
Address,
|
||||
AccountId,
|
||||
privacy_preserving_transaction::message::{EncryptedAccountData, Message},
|
||||
};
|
||||
|
||||
@ -114,7 +113,7 @@ pub mod tests {
|
||||
let npk1 = NullifierPublicKey::from(&nsk1);
|
||||
let npk2 = NullifierPublicKey::from(&nsk2);
|
||||
|
||||
let public_addresses = vec![Address::new([1; 32])];
|
||||
let public_account_ids = vec![AccountId::new([1; 32])];
|
||||
|
||||
let nonces = vec![1, 2, 3];
|
||||
|
||||
@ -131,7 +130,7 @@ pub mod tests {
|
||||
)];
|
||||
|
||||
Message {
|
||||
public_addresses: public_addresses.clone(),
|
||||
public_account_ids: public_account_ids.clone(),
|
||||
nonces: nonces.clone(),
|
||||
public_post_states: public_post_states.clone(),
|
||||
encrypted_private_post_states: encrypted_private_post_states.clone(),
|
||||
@ -140,17 +139,6 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_serialization_roundtrip() {
|
||||
let message = message_for_tests();
|
||||
|
||||
let bytes = message.to_bytes();
|
||||
let mut cursor = Cursor::new(bytes.as_ref());
|
||||
let message_from_cursor = Message::from_cursor(&mut cursor).unwrap();
|
||||
|
||||
assert_eq!(message, message_from_cursor);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encrypted_account_data_constructor() {
|
||||
let npk = NullifierPublicKey::from(&[1; 32]);
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, Nullifier, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, AccountWithMetadata},
|
||||
};
|
||||
|
||||
use crate::error::NssaError;
|
||||
use crate::privacy_preserving_transaction::circuit::Proof;
|
||||
use crate::privacy_preserving_transaction::message::EncryptedAccountData;
|
||||
use crate::{Address, V02State};
|
||||
use super::{message::Message, witness_set::WitnessSet};
|
||||
use crate::{
|
||||
AccountId, V02State,
|
||||
error::NssaError,
|
||||
privacy_preserving_transaction::{circuit::Proof, message::EncryptedAccountData},
|
||||
};
|
||||
|
||||
use super::message::Message;
|
||||
use super::witness_set::WitnessSet;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct PrivacyPreservingTransaction {
|
||||
pub message: Message,
|
||||
witness_set: WitnessSet,
|
||||
@ -30,7 +30,7 @@ impl PrivacyPreservingTransaction {
|
||||
pub(crate) fn validate_and_produce_public_state_diff(
|
||||
&self,
|
||||
state: &V02State,
|
||||
) -> Result<HashMap<Address, Account>, NssaError> {
|
||||
) -> Result<HashMap<AccountId, Account>, NssaError> {
|
||||
let message = &self.message;
|
||||
let witness_set = &self.witness_set;
|
||||
|
||||
@ -41,10 +41,10 @@ impl PrivacyPreservingTransaction {
|
||||
));
|
||||
}
|
||||
|
||||
// 2. Check there are no duplicate addresses in the public_addresses list.
|
||||
if n_unique(&message.public_addresses) != message.public_addresses.len() {
|
||||
// 2. Check there are no duplicate account_ids in the public_account_ids list.
|
||||
if n_unique(&message.public_account_ids) != message.public_account_ids.len() {
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Duplicate addresses found in message".into(),
|
||||
"Duplicate account_ids found in message".into(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -77,10 +77,10 @@ impl PrivacyPreservingTransaction {
|
||||
));
|
||||
}
|
||||
|
||||
let signer_addresses = self.signer_addresses();
|
||||
let signer_account_ids = self.signer_account_ids();
|
||||
// Check nonces corresponds to the current nonces on the public state.
|
||||
for (address, nonce) in signer_addresses.iter().zip(&message.nonces) {
|
||||
let current_nonce = state.get_account_by_address(address).nonce;
|
||||
for (account_id, nonce) in signer_account_ids.iter().zip(&message.nonces) {
|
||||
let current_nonce = state.get_account_by_id(account_id).nonce;
|
||||
if current_nonce != *nonce {
|
||||
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
||||
}
|
||||
@ -88,13 +88,13 @@ impl PrivacyPreservingTransaction {
|
||||
|
||||
// Build pre_states for proof verification
|
||||
let public_pre_states: Vec<_> = message
|
||||
.public_addresses
|
||||
.public_account_ids
|
||||
.iter()
|
||||
.map(|address| {
|
||||
.map(|account_id| {
|
||||
AccountWithMetadata::new(
|
||||
state.get_account_by_address(address),
|
||||
signer_addresses.contains(address),
|
||||
*address,
|
||||
state.get_account_by_id(account_id),
|
||||
signer_account_ids.contains(account_id),
|
||||
*account_id,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@ -116,7 +116,7 @@ impl PrivacyPreservingTransaction {
|
||||
state.check_nullifiers_are_valid(&message.new_nullifiers)?;
|
||||
|
||||
Ok(message
|
||||
.public_addresses
|
||||
.public_account_ids
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(message.public_post_states.clone())
|
||||
@ -131,11 +131,11 @@ impl PrivacyPreservingTransaction {
|
||||
&self.witness_set
|
||||
}
|
||||
|
||||
pub(crate) fn signer_addresses(&self) -> Vec<Address> {
|
||||
pub(crate) fn signer_account_ids(&self) -> Vec<AccountId> {
|
||||
self.witness_set
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, public_key)| Address::from(public_key))
|
||||
.map(|(_, public_key)| AccountId::from(public_key))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@ -174,17 +174,17 @@ fn n_unique<T: Eq + Hash>(data: &[T]) -> usize {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
Address, PrivacyPreservingTransaction, PrivateKey, PublicKey,
|
||||
AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey,
|
||||
privacy_preserving_transaction::{
|
||||
circuit::Proof, message::tests::message_for_tests, witness_set::WitnessSet,
|
||||
},
|
||||
};
|
||||
|
||||
fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) {
|
||||
fn keys_for_tests() -> (PrivateKey, PrivateKey, AccountId, AccountId) {
|
||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let addr1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = Address::from(&PublicKey::new_from_private_key(&key2));
|
||||
let addr1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||
(key1, key2, addr1, addr2)
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
use crate::{
|
||||
PrivateKey, PublicKey, Signature,
|
||||
privacy_preserving_transaction::{circuit::Proof, message::Message},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct WitnessSet {
|
||||
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
||||
pub(crate) proof: Proof,
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
use crate::program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF};
|
||||
use nssa_core::{
|
||||
account::AccountWithMetadata,
|
||||
program::{InstructionData, ProgramId, ProgramOutput},
|
||||
};
|
||||
|
||||
use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::error::NssaError;
|
||||
use crate::{
|
||||
error::NssaError,
|
||||
program_methods::{AUTHENTICATED_TRANSFER_ELF, PINATA_ELF, TOKEN_ELF},
|
||||
};
|
||||
|
||||
/// Maximum number of cycles for a public execution.
|
||||
/// TODO: Make this variable when fees are implemented
|
||||
@ -107,13 +108,15 @@ impl Program {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::program_methods::{
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF,
|
||||
TOKEN_ID,
|
||||
};
|
||||
use nssa_core::account::{Account, AccountId, AccountWithMetadata};
|
||||
|
||||
use crate::program::Program;
|
||||
use crate::{
|
||||
program::Program,
|
||||
program_methods::{
|
||||
AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID,
|
||||
TOKEN_ELF, TOKEN_ID,
|
||||
},
|
||||
};
|
||||
|
||||
impl Program {
|
||||
/// A program that changes the nonce of an account
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Message {
|
||||
pub(crate) bytecode: Vec<u8>,
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
use crate::{
|
||||
V02State, error::NssaError, program::Program, program_deployment_transaction::message::Message,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ProgramDeploymentTransaction {
|
||||
pub(crate) message: Message,
|
||||
}
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
account::Nonce,
|
||||
program::{InstructionData, ProgramId},
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{Address, error::NssaError, program::Program};
|
||||
use crate::{AccountId, error::NssaError, program::Program};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Message {
|
||||
pub(crate) program_id: ProgramId,
|
||||
pub(crate) addresses: Vec<Address>,
|
||||
pub(crate) account_ids: Vec<AccountId>,
|
||||
pub(crate) nonces: Vec<Nonce>,
|
||||
pub(crate) instruction_data: InstructionData,
|
||||
}
|
||||
@ -17,14 +18,14 @@ pub struct Message {
|
||||
impl Message {
|
||||
pub fn try_new<T: Serialize>(
|
||||
program_id: ProgramId,
|
||||
addresses: Vec<Address>,
|
||||
account_ids: Vec<AccountId>,
|
||||
nonces: Vec<Nonce>,
|
||||
instruction: T,
|
||||
) -> Result<Self, NssaError> {
|
||||
let instruction_data = Program::serialize_instruction(instruction)?;
|
||||
Ok(Self {
|
||||
program_id,
|
||||
addresses,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction_data,
|
||||
})
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::{
|
||||
account::{Account, AccountWithMetadata},
|
||||
address::Address,
|
||||
program::{DEFAULT_PROGRAM_ID, validate_execution},
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
program::{ChainedCall, DEFAULT_PROGRAM_ID, validate_execution},
|
||||
};
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
@ -11,14 +11,14 @@ use crate::{
|
||||
V02State,
|
||||
error::NssaError,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
state::MAX_NUMBER_CHAINED_CALLS,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct PublicTransaction {
|
||||
message: Message,
|
||||
witness_set: WitnessSet,
|
||||
}
|
||||
const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
||||
|
||||
impl PublicTransaction {
|
||||
pub fn new(message: Message, witness_set: WitnessSet) -> Self {
|
||||
@ -36,11 +36,11 @@ impl PublicTransaction {
|
||||
&self.witness_set
|
||||
}
|
||||
|
||||
pub(crate) fn signer_addresses(&self) -> Vec<Address> {
|
||||
pub(crate) fn signer_account_ids(&self) -> Vec<AccountId> {
|
||||
self.witness_set
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, public_key)| Address::from(public_key))
|
||||
.map(|(_, public_key)| AccountId::from(public_key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -54,14 +54,14 @@ impl PublicTransaction {
|
||||
pub(crate) fn validate_and_produce_public_state_diff(
|
||||
&self,
|
||||
state: &V02State,
|
||||
) -> Result<HashMap<Address, Account>, NssaError> {
|
||||
) -> Result<HashMap<AccountId, Account>, NssaError> {
|
||||
let message = self.message();
|
||||
let witness_set = self.witness_set();
|
||||
|
||||
// All addresses must be different
|
||||
if message.addresses.iter().collect::<HashSet<_>>().len() != message.addresses.len() {
|
||||
// All account_ids must be different
|
||||
if message.account_ids.iter().collect::<HashSet<_>>().len() != message.account_ids.len() {
|
||||
return Err(NssaError::InvalidInput(
|
||||
"Duplicate addresses found in message".into(),
|
||||
"Duplicate account_ids found in message".into(),
|
||||
));
|
||||
}
|
||||
|
||||
@ -79,46 +79,68 @@ impl PublicTransaction {
|
||||
));
|
||||
}
|
||||
|
||||
let signer_addresses = self.signer_addresses();
|
||||
let signer_account_ids = self.signer_account_ids();
|
||||
// Check nonces corresponds to the current nonces on the public state.
|
||||
for (address, nonce) in signer_addresses.iter().zip(&message.nonces) {
|
||||
let current_nonce = state.get_account_by_address(address).nonce;
|
||||
for (account_id, nonce) in signer_account_ids.iter().zip(&message.nonces) {
|
||||
let current_nonce = state.get_account_by_id(account_id).nonce;
|
||||
if current_nonce != *nonce {
|
||||
return Err(NssaError::InvalidInput("Nonce mismatch".into()));
|
||||
}
|
||||
}
|
||||
|
||||
// Build pre_states for execution
|
||||
let mut input_pre_states: Vec<_> = message
|
||||
.addresses
|
||||
let input_pre_states: Vec<_> = message
|
||||
.account_ids
|
||||
.iter()
|
||||
.map(|address| {
|
||||
.map(|account_id| {
|
||||
AccountWithMetadata::new(
|
||||
state.get_account_by_address(address),
|
||||
signer_addresses.contains(address),
|
||||
*address,
|
||||
state.get_account_by_id(account_id),
|
||||
signer_account_ids.contains(account_id),
|
||||
*account_id,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut state_diff: HashMap<Address, Account> = HashMap::new();
|
||||
let mut state_diff: HashMap<AccountId, Account> = HashMap::new();
|
||||
|
||||
let mut program_id = message.program_id;
|
||||
let mut instruction_data = message.instruction_data.clone();
|
||||
let initial_call = ChainedCall {
|
||||
program_id: message.program_id,
|
||||
instruction_data: message.instruction_data.clone(),
|
||||
pre_states: input_pre_states,
|
||||
};
|
||||
|
||||
let mut chained_calls = VecDeque::from_iter([initial_call]);
|
||||
let mut chain_calls_counter = 0;
|
||||
|
||||
while let Some(chained_call) = chained_calls.pop_front() {
|
||||
if chain_calls_counter > MAX_NUMBER_CHAINED_CALLS {
|
||||
return Err(NssaError::MaxChainedCallsDepthExceeded);
|
||||
}
|
||||
|
||||
for _i in 0..MAX_NUMBER_CHAINED_CALLS {
|
||||
// Check the `program_id` corresponds to a deployed program
|
||||
let Some(program) = state.programs().get(&program_id) else {
|
||||
let Some(program) = state.programs().get(&chained_call.program_id) else {
|
||||
return Err(NssaError::InvalidInput("Unknown program".into()));
|
||||
};
|
||||
|
||||
let mut program_output = program.execute(&input_pre_states, &instruction_data)?;
|
||||
let mut program_output =
|
||||
program.execute(&chained_call.pre_states, &chained_call.instruction_data)?;
|
||||
|
||||
// This check is equivalent to checking that the program output pre_states coinicide
|
||||
// with the values in the public state or with any modifications to those values
|
||||
// during the chain of calls.
|
||||
if input_pre_states != program_output.pre_states {
|
||||
return Err(NssaError::InvalidProgramBehavior);
|
||||
for pre in &program_output.pre_states {
|
||||
let account_id = pre.account_id;
|
||||
// Check that the program output pre_states coinicide with the values in the public
|
||||
// state or with any modifications to those values during the chain of calls.
|
||||
let expected_pre = state_diff
|
||||
.get(&account_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| state.get_account_by_id(&account_id));
|
||||
if pre.account != expected_pre {
|
||||
return Err(NssaError::InvalidProgramBehavior);
|
||||
}
|
||||
|
||||
// Check that authorization flags are consistent with the provided ones
|
||||
if pre.is_authorized && !signer_account_ids.contains(&account_id) {
|
||||
return Err(NssaError::InvalidProgramBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify execution corresponds to a well-behaved program.
|
||||
@ -126,7 +148,7 @@ impl PublicTransaction {
|
||||
if !validate_execution(
|
||||
&program_output.pre_states,
|
||||
&program_output.post_states,
|
||||
program_id,
|
||||
chained_call.program_id,
|
||||
) {
|
||||
return Err(NssaError::InvalidProgramBehavior);
|
||||
}
|
||||
@ -134,7 +156,7 @@ impl PublicTransaction {
|
||||
// The invoked program claims the accounts with default program id.
|
||||
for post in program_output.post_states.iter_mut() {
|
||||
if post.program_owner == DEFAULT_PROGRAM_ID {
|
||||
post.program_owner = program_id;
|
||||
post.program_owner = chained_call.program_id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,37 +169,11 @@ impl PublicTransaction {
|
||||
state_diff.insert(pre.account_id, post.clone());
|
||||
}
|
||||
|
||||
if let Some(next_chained_call) = program_output.chained_call {
|
||||
program_id = next_chained_call.program_id;
|
||||
instruction_data = next_chained_call.instruction_data;
|
||||
for new_call in program_output.chained_calls.into_iter().rev() {
|
||||
chained_calls.push_front(new_call);
|
||||
}
|
||||
|
||||
// Build post states with metadata for next call
|
||||
let mut post_states_with_metadata = Vec::new();
|
||||
for (pre, post) in program_output
|
||||
.pre_states
|
||||
.iter()
|
||||
.zip(program_output.post_states)
|
||||
{
|
||||
let mut post_with_metadata = pre.clone();
|
||||
post_with_metadata.account = post.clone();
|
||||
post_states_with_metadata.push(post_with_metadata);
|
||||
}
|
||||
|
||||
input_pre_states = next_chained_call
|
||||
.account_indices
|
||||
.iter()
|
||||
.map(|&i| {
|
||||
post_states_with_metadata
|
||||
.get(i)
|
||||
.ok_or_else(|| {
|
||||
NssaError::InvalidInput("Invalid account indices".into())
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
.collect::<Result<Vec<_>, NssaError>>()?;
|
||||
} else {
|
||||
break;
|
||||
};
|
||||
chain_calls_counter += 1;
|
||||
}
|
||||
|
||||
Ok(state_diff)
|
||||
@ -189,17 +185,17 @@ pub mod tests {
|
||||
use sha2::{Digest, digest::FixedOutput};
|
||||
|
||||
use crate::{
|
||||
Address, PrivateKey, PublicKey, PublicTransaction, Signature, V02State,
|
||||
AccountId, PrivateKey, PublicKey, PublicTransaction, Signature, V02State,
|
||||
error::NssaError,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
|
||||
fn keys_for_tests() -> (PrivateKey, PrivateKey, Address, Address) {
|
||||
fn keys_for_tests() -> (PrivateKey, PrivateKey, AccountId, AccountId) {
|
||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let addr1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = Address::from(&PublicKey::new_from_private_key(&key2));
|
||||
let addr1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||
(key1, key2, addr1, addr2)
|
||||
}
|
||||
|
||||
@ -248,20 +244,20 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signer_addresses() {
|
||||
fn test_signer_account_ids() {
|
||||
let tx = transaction_for_tests();
|
||||
let expected_signer_addresses = vec![
|
||||
Address::new([
|
||||
let expected_signer_account_ids = vec![
|
||||
AccountId::new([
|
||||
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9,
|
||||
115, 84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
||||
]),
|
||||
Address::new([
|
||||
AccountId::new([
|
||||
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57,
|
||||
141, 98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
||||
]),
|
||||
];
|
||||
let signer_addresses = tx.signer_addresses();
|
||||
assert_eq!(signer_addresses, expected_signer_addresses);
|
||||
let signer_account_ids = tx.signer_account_ids();
|
||||
assert_eq!(signer_account_ids, expected_signer_account_ids);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -286,7 +282,7 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_list_cant_have_duplicates() {
|
||||
fn test_account_id_list_cant_have_duplicates() {
|
||||
let (key1, _, addr1, _) = keys_for_tests();
|
||||
let state = state_for_tests();
|
||||
let nonces = vec![0, 0];
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
|
||||
use crate::{PrivateKey, PublicKey, Signature, public_transaction::Message};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct WitnessSet {
|
||||
pub(crate) signatures_and_public_keys: Vec<(Signature, PublicKey)>,
|
||||
}
|
||||
@ -39,9 +41,8 @@ impl WitnessSet {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Address;
|
||||
|
||||
use super::*;
|
||||
use crate::AccountId;
|
||||
|
||||
#[test]
|
||||
fn test_for_message_constructor() {
|
||||
@ -49,8 +50,8 @@ mod tests {
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let pubkey1 = PublicKey::new_from_private_key(&key1);
|
||||
let pubkey2 = PublicKey::new_from_private_key(&key2);
|
||||
let addr1 = Address::from(&pubkey1);
|
||||
let addr2 = Address::from(&pubkey2);
|
||||
let addr1 = AccountId::from(&pubkey1);
|
||||
let addr2 = AccountId::from(&pubkey2);
|
||||
let nonces = vec![1, 2];
|
||||
let instruction = vec![1, 2, 3, 4];
|
||||
let message = Message::try_new([0; 8], vec![addr1, addr2], nonces, instruction).unwrap();
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use crate::{PublicKey, Signature, error::NssaError};
|
||||
|
||||
impl PublicKey {
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let mut value = [0u8; 32];
|
||||
cursor.read_exact(&mut value)?;
|
||||
Self::try_new(value)
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> &[u8] {
|
||||
self.value()
|
||||
}
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
pub(crate) fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Self, NssaError> {
|
||||
let mut value = [0u8; 64];
|
||||
cursor.read_exact(&mut value)?;
|
||||
Ok(Self { value })
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
mod encoding;
|
||||
mod private_key;
|
||||
mod public_key;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
pub use private_key::PrivateKey;
|
||||
pub use public_key::PublicKey;
|
||||
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Signature {
|
||||
value: [u8; 64],
|
||||
}
|
||||
|
||||
@ -1,13 +1,27 @@
|
||||
use nssa_core::address::Address;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use nssa_core::account::AccountId;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::{PrivateKey, error::NssaError};
|
||||
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, Serialize, Deserialize)]
|
||||
pub struct PublicKey([u8; 32]);
|
||||
|
||||
impl BorshDeserialize for PublicKey {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut buf = [0u8; 32];
|
||||
reader.read_exact(&mut buf)?;
|
||||
|
||||
Self::try_new(buf).map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
"Invalid public key: not a valid point",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
pub fn new_from_private_key(key: &PrivateKey) -> Self {
|
||||
let value = {
|
||||
@ -20,7 +34,7 @@ impl PublicKey {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
pub(super) fn try_new(value: [u8; 32]) -> Result<Self, NssaError> {
|
||||
pub fn try_new(value: [u8; 32]) -> Result<Self, NssaError> {
|
||||
// Check point is valid
|
||||
let _ = secp256k1::XOnlyPublicKey::from_byte_array(value)
|
||||
.map_err(|_| NssaError::InvalidPublicKey)?;
|
||||
@ -32,7 +46,7 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PublicKey> for Address {
|
||||
impl From<&PublicKey> for AccountId {
|
||||
fn from(key: &PublicKey) -> Self {
|
||||
const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.2/AccountId/Public/\x00\x00\x00\x00";
|
||||
|
||||
@ -94,4 +108,14 @@ mod test {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_correct_ser_deser_roundtrip() {
|
||||
let pub_key = PublicKey::try_new([42; 32]).unwrap();
|
||||
|
||||
let pub_key_borsh_ser = borsh::to_vec(&pub_key).unwrap();
|
||||
let pub_key_new: PublicKey = borsh::from_slice(&pub_key_borsh_ser).unwrap();
|
||||
|
||||
assert_eq!(pub_key, pub_key_new);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
account::{Account, AccountId},
|
||||
program::ProgramId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::NssaError, merkle_tree::MerkleTree,
|
||||
privacy_preserving_transaction::PrivacyPreservingTransaction, program::Program,
|
||||
program_deployment_transaction::ProgramDeploymentTransaction,
|
||||
public_transaction::PublicTransaction,
|
||||
};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
account::Account, address::Address, program::ProgramId,
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub const MAX_NUMBER_CHAINED_CALLS: usize = 10;
|
||||
|
||||
pub(crate) struct CommitmentSet {
|
||||
merkle_tree: MerkleTree,
|
||||
@ -58,27 +63,27 @@ impl CommitmentSet {
|
||||
type NullifierSet = HashSet<Nullifier>;
|
||||
|
||||
pub struct V02State {
|
||||
public_state: HashMap<Address, Account>,
|
||||
public_state: HashMap<AccountId, Account>,
|
||||
private_state: (CommitmentSet, NullifierSet),
|
||||
programs: HashMap<ProgramId, Program>,
|
||||
}
|
||||
|
||||
impl V02State {
|
||||
pub fn new_with_genesis_accounts(
|
||||
initial_data: &[(Address, u128)],
|
||||
initial_data: &[(AccountId, u128)],
|
||||
initial_commitments: &[nssa_core::Commitment],
|
||||
) -> Self {
|
||||
let authenticated_transfer_program = Program::authenticated_transfer_program();
|
||||
let public_state = initial_data
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|(address, balance)| {
|
||||
.map(|(account_id, balance)| {
|
||||
let account = Account {
|
||||
balance,
|
||||
program_owner: authenticated_transfer_program.id(),
|
||||
..Account::default()
|
||||
};
|
||||
(address, account)
|
||||
(account_id, account)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -108,14 +113,14 @@ impl V02State {
|
||||
) -> Result<(), NssaError> {
|
||||
let state_diff = tx.validate_and_produce_public_state_diff(self)?;
|
||||
|
||||
for (address, post) in state_diff.into_iter() {
|
||||
let current_account = self.get_account_by_address_mut(address);
|
||||
for (account_id, post) in state_diff.into_iter() {
|
||||
let current_account = self.get_account_by_id_mut(account_id);
|
||||
|
||||
*current_account = post;
|
||||
}
|
||||
|
||||
for address in tx.signer_addresses() {
|
||||
let current_account = self.get_account_by_address_mut(address);
|
||||
for account_id in tx.signer_account_ids() {
|
||||
let current_account = self.get_account_by_id_mut(account_id);
|
||||
current_account.nonce += 1;
|
||||
}
|
||||
|
||||
@ -144,8 +149,8 @@ impl V02State {
|
||||
self.private_state.1.extend(new_nullifiers);
|
||||
|
||||
// 4. Update public accounts
|
||||
for (address, post) in public_state_diff.into_iter() {
|
||||
let current_account = self.get_account_by_address_mut(address);
|
||||
for (account_id, post) in public_state_diff.into_iter() {
|
||||
let current_account = self.get_account_by_id_mut(account_id);
|
||||
*current_account = post;
|
||||
}
|
||||
|
||||
@ -161,13 +166,13 @@ impl V02State {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_account_by_address_mut(&mut self, address: Address) -> &mut Account {
|
||||
self.public_state.entry(address).or_default()
|
||||
fn get_account_by_id_mut(&mut self, account_id: AccountId) -> &mut Account {
|
||||
self.public_state.entry(account_id).or_default()
|
||||
}
|
||||
|
||||
pub fn get_account_by_address(&self, address: &Address) -> Account {
|
||||
pub fn get_account_by_id(&self, account_id: &AccountId) -> Account {
|
||||
self.public_state
|
||||
.get(address)
|
||||
.get(account_id)
|
||||
.cloned()
|
||||
.unwrap_or(Account::default())
|
||||
}
|
||||
@ -220,11 +225,11 @@ impl V02State {
|
||||
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
impl V02State {
|
||||
pub fn add_pinata_program(&mut self, address: Address) {
|
||||
pub fn add_pinata_program(&mut self, account_id: AccountId) {
|
||||
self.insert_program(Program::pinata());
|
||||
|
||||
self.public_state.insert(
|
||||
address,
|
||||
account_id,
|
||||
Account {
|
||||
program_owner: Program::pinata().id(),
|
||||
balance: 1500,
|
||||
@ -241,8 +246,15 @@ pub mod tests {
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
|
||||
program::ProgramId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Address, PublicKey, PublicTransaction, V02State,
|
||||
PublicKey, PublicTransaction, V02State,
|
||||
error::NssaError,
|
||||
execute_and_prove,
|
||||
privacy_preserving_transaction::{
|
||||
@ -251,27 +263,21 @@ pub mod tests {
|
||||
program::Program,
|
||||
public_transaction,
|
||||
signature::PrivateKey,
|
||||
};
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey,
|
||||
account::{Account, AccountId, AccountWithMetadata, Nonce},
|
||||
encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar},
|
||||
program::ProgramId,
|
||||
state::MAX_NUMBER_CHAINED_CALLS,
|
||||
};
|
||||
|
||||
fn transfer_transaction(
|
||||
from: Address,
|
||||
from: AccountId,
|
||||
from_key: PrivateKey,
|
||||
nonce: u128,
|
||||
to: Address,
|
||||
to: AccountId,
|
||||
balance: u128,
|
||||
) -> PublicTransaction {
|
||||
let addresses = vec![from, to];
|
||||
let account_ids = vec![from, to];
|
||||
let nonces = vec![nonce];
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, addresses, nonces, balance).unwrap();
|
||||
public_transaction::Message::try_new(program_id, account_ids, nonces, balance).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
PublicTransaction::new(message, witness_set)
|
||||
}
|
||||
@ -280,8 +286,8 @@ pub mod tests {
|
||||
fn test_new_with_genesis() {
|
||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let addr1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = Address::from(&PublicKey::new_from_private_key(&key2));
|
||||
let addr1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
||||
let addr2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||
let initial_data = [(addr1, 100u128), (addr2, 151u128)];
|
||||
let authenticated_transfers_program = Program::authenticated_transfer_program();
|
||||
let expected_public_state = {
|
||||
@ -333,25 +339,25 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_account_by_address_non_default_account() {
|
||||
fn test_get_account_by_account_id_non_default_account() {
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let addr = Address::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(addr, 100u128)];
|
||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(account_id, 100u128)];
|
||||
let state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
||||
let expected_account = state.public_state.get(&addr).unwrap();
|
||||
let expected_account = state.public_state.get(&account_id).unwrap();
|
||||
|
||||
let account = state.get_account_by_address(&addr);
|
||||
let account = state.get_account_by_id(&account_id);
|
||||
|
||||
assert_eq!(&account, expected_account);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_account_by_address_default_account() {
|
||||
let addr2 = Address::new([0; 32]);
|
||||
fn test_get_account_by_account_id_default_account() {
|
||||
let addr2 = AccountId::new([0; 32]);
|
||||
let state = V02State::new_with_genesis_accounts(&[], &[]);
|
||||
let expected_account = Account::default();
|
||||
|
||||
let account = state.get_account_by_address(&addr2);
|
||||
let account = state.get_account_by_id(&addr2);
|
||||
|
||||
assert_eq!(account, expected_account);
|
||||
}
|
||||
@ -368,96 +374,96 @@ pub mod tests {
|
||||
#[test]
|
||||
fn transition_from_authenticated_transfer_program_invocation_default_account_destination() {
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(address, 100)];
|
||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(account_id, 100)];
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
||||
let from = address;
|
||||
let to = Address::new([2; 32]);
|
||||
assert_eq!(state.get_account_by_address(&to), Account::default());
|
||||
let from = account_id;
|
||||
let to = AccountId::new([2; 32]);
|
||||
assert_eq!(state.get_account_by_id(&to), Account::default());
|
||||
let balance_to_move = 5;
|
||||
|
||||
let tx = transfer_transaction(from, key, 0, to, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_address(&from).balance, 95);
|
||||
assert_eq!(state.get_account_by_address(&to).balance, 5);
|
||||
assert_eq!(state.get_account_by_address(&from).nonce, 1);
|
||||
assert_eq!(state.get_account_by_address(&to).nonce, 0);
|
||||
assert_eq!(state.get_account_by_id(&from).balance, 95);
|
||||
assert_eq!(state.get_account_by_id(&to).balance, 5);
|
||||
assert_eq!(state.get_account_by_id(&from).nonce, 1);
|
||||
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_from_authenticated_transfer_program_invocation_insuficient_balance() {
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(address, 100)];
|
||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_data = [(account_id, 100)];
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
||||
let from = address;
|
||||
let from = account_id;
|
||||
let from_key = key;
|
||||
let to = Address::new([2; 32]);
|
||||
let to = AccountId::new([2; 32]);
|
||||
let balance_to_move = 101;
|
||||
assert!(state.get_account_by_address(&from).balance < balance_to_move);
|
||||
assert!(state.get_account_by_id(&from).balance < balance_to_move);
|
||||
|
||||
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
|
||||
let result = state.transition_from_public_transaction(&tx);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::ProgramExecutionFailed(_))));
|
||||
assert_eq!(state.get_account_by_address(&from).balance, 100);
|
||||
assert_eq!(state.get_account_by_address(&to).balance, 0);
|
||||
assert_eq!(state.get_account_by_address(&from).nonce, 0);
|
||||
assert_eq!(state.get_account_by_address(&to).nonce, 0);
|
||||
assert_eq!(state.get_account_by_id(&from).balance, 100);
|
||||
assert_eq!(state.get_account_by_id(&to).balance, 0);
|
||||
assert_eq!(state.get_account_by_id(&from).nonce, 0);
|
||||
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_from_authenticated_transfer_program_invocation_non_default_account_destination() {
|
||||
let key1 = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||
let address2 = Address::from(&PublicKey::new_from_private_key(&key2));
|
||||
let initial_data = [(address1, 100), (address2, 200)];
|
||||
let account_id1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
||||
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||
let initial_data = [(account_id1, 100), (account_id2, 200)];
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
||||
let from = address2;
|
||||
let from = account_id2;
|
||||
let from_key = key2;
|
||||
let to = address1;
|
||||
assert_ne!(state.get_account_by_address(&to), Account::default());
|
||||
let to = account_id1;
|
||||
assert_ne!(state.get_account_by_id(&to), Account::default());
|
||||
let balance_to_move = 8;
|
||||
|
||||
let tx = transfer_transaction(from, from_key, 0, to, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_address(&from).balance, 192);
|
||||
assert_eq!(state.get_account_by_address(&to).balance, 108);
|
||||
assert_eq!(state.get_account_by_address(&from).nonce, 1);
|
||||
assert_eq!(state.get_account_by_address(&to).nonce, 0);
|
||||
assert_eq!(state.get_account_by_id(&from).balance, 192);
|
||||
assert_eq!(state.get_account_by_id(&to).balance, 108);
|
||||
assert_eq!(state.get_account_by_id(&from).nonce, 1);
|
||||
assert_eq!(state.get_account_by_id(&to).nonce, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_from_sequence_of_authenticated_transfer_program_invocations() {
|
||||
let key1 = PrivateKey::try_new([8; 32]).unwrap();
|
||||
let address1 = Address::from(&PublicKey::new_from_private_key(&key1));
|
||||
let account_id1 = AccountId::from(&PublicKey::new_from_private_key(&key1));
|
||||
let key2 = PrivateKey::try_new([2; 32]).unwrap();
|
||||
let address2 = Address::from(&PublicKey::new_from_private_key(&key2));
|
||||
let initial_data = [(address1, 100)];
|
||||
let account_id2 = AccountId::from(&PublicKey::new_from_private_key(&key2));
|
||||
let initial_data = [(account_id1, 100)];
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[]);
|
||||
let address3 = Address::new([3; 32]);
|
||||
let account_id3 = AccountId::new([3; 32]);
|
||||
let balance_to_move = 5;
|
||||
|
||||
let tx = transfer_transaction(address1, key1, 0, address2, balance_to_move);
|
||||
let tx = transfer_transaction(account_id1, key1, 0, account_id2, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
let balance_to_move = 3;
|
||||
let tx = transfer_transaction(address2, key2, 0, address3, balance_to_move);
|
||||
let tx = transfer_transaction(account_id2, key2, 0, account_id3, balance_to_move);
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
|
||||
assert_eq!(state.get_account_by_address(&address1).balance, 95);
|
||||
assert_eq!(state.get_account_by_address(&address2).balance, 2);
|
||||
assert_eq!(state.get_account_by_address(&address3).balance, 3);
|
||||
assert_eq!(state.get_account_by_address(&address1).nonce, 1);
|
||||
assert_eq!(state.get_account_by_address(&address2).nonce, 1);
|
||||
assert_eq!(state.get_account_by_address(&address3).nonce, 0);
|
||||
assert_eq!(state.get_account_by_id(&account_id1).balance, 95);
|
||||
assert_eq!(state.get_account_by_id(&account_id2).balance, 2);
|
||||
assert_eq!(state.get_account_by_id(&account_id3).balance, 3);
|
||||
assert_eq!(state.get_account_by_id(&account_id1).nonce, 1);
|
||||
assert_eq!(state.get_account_by_id(&account_id2).nonce, 1);
|
||||
assert_eq!(state.get_account_by_id(&account_id3).nonce, 0);
|
||||
}
|
||||
|
||||
impl V02State {
|
||||
pub fn force_insert_account(&mut self, address: Address, account: Account) {
|
||||
self.public_state.insert(address, account);
|
||||
pub fn force_insert_account(&mut self, account_id: AccountId, account: Account) {
|
||||
self.public_state.insert(account_id, account);
|
||||
}
|
||||
|
||||
/// Include test programs in the builtin programs map
|
||||
@ -488,15 +494,15 @@ pub mod tests {
|
||||
..Account::default()
|
||||
};
|
||||
self.force_insert_account(
|
||||
Address::new([255; 32]),
|
||||
AccountId::new([255; 32]),
|
||||
account_with_default_values_except_balance,
|
||||
);
|
||||
self.force_insert_account(
|
||||
Address::new([254; 32]),
|
||||
AccountId::new([254; 32]),
|
||||
account_with_default_values_except_nonce,
|
||||
);
|
||||
self.force_insert_account(
|
||||
Address::new([253; 32]),
|
||||
AccountId::new([253; 32]),
|
||||
account_with_default_values_except_data,
|
||||
);
|
||||
self
|
||||
@ -508,7 +514,7 @@ pub mod tests {
|
||||
balance: 100,
|
||||
..Default::default()
|
||||
};
|
||||
self.force_insert_account(Address::new([252; 32]), account);
|
||||
self.force_insert_account(AccountId::new([252; 32]), account);
|
||||
self
|
||||
}
|
||||
|
||||
@ -521,13 +527,13 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_program_should_fail_if_modifies_nonces() {
|
||||
let initial_data = [(Address::new([1; 32]), 100)];
|
||||
let initial_data = [(AccountId::new([1; 32]), 100)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let addresses = vec![Address::new([1; 32])];
|
||||
let account_ids = vec![AccountId::new([1; 32])];
|
||||
let program_id = Program::nonce_changer_program().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -538,13 +544,13 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_program_should_fail_if_output_accounts_exceed_inputs() {
|
||||
let initial_data = [(Address::new([1; 32]), 100)];
|
||||
let initial_data = [(AccountId::new([1; 32]), 100)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let addresses = vec![Address::new([1; 32])];
|
||||
let account_ids = vec![AccountId::new([1; 32])];
|
||||
let program_id = Program::extra_output_program().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -555,13 +561,13 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_program_should_fail_with_missing_output_accounts() {
|
||||
let initial_data = [(Address::new([1; 32]), 100)];
|
||||
let initial_data = [(AccountId::new([1; 32]), 100)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let addresses = vec![Address::new([1; 32]), Address::new([2; 32])];
|
||||
let account_ids = vec![AccountId::new([1; 32]), AccountId::new([2; 32])];
|
||||
let program_id = Program::missing_output_program().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, addresses, vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, account_ids, vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -572,19 +578,20 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() {
|
||||
let initial_data = [(Address::new([1; 32]), 0)];
|
||||
let initial_data = [(AccountId::new([1; 32]), 0)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let address = Address::new([1; 32]);
|
||||
let account = state.get_account_by_address(&address);
|
||||
// Assert the target account only differs from the default account in the program owner field
|
||||
let account_id = AccountId::new([1; 32]);
|
||||
let account = state.get_account_by_id(&account_id);
|
||||
// Assert the target account only differs from the default account in the program owner
|
||||
// field
|
||||
assert_ne!(account.program_owner, Account::default().program_owner);
|
||||
assert_eq!(account.balance, Account::default().balance);
|
||||
assert_eq!(account.nonce, Account::default().nonce);
|
||||
assert_eq!(account.data, Account::default().data);
|
||||
let program_id = Program::program_owner_changer().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -599,8 +606,8 @@ pub mod tests {
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
||||
.with_test_programs()
|
||||
.with_non_default_accounts_but_default_program_owners();
|
||||
let address = Address::new([255; 32]);
|
||||
let account = state.get_account_by_address(&address);
|
||||
let account_id = AccountId::new([255; 32]);
|
||||
let account = state.get_account_by_id(&account_id);
|
||||
// Assert the target account only differs from the default account in balance field
|
||||
assert_eq!(account.program_owner, Account::default().program_owner);
|
||||
assert_ne!(account.balance, Account::default().balance);
|
||||
@ -608,7 +615,7 @@ pub mod tests {
|
||||
assert_eq!(account.data, Account::default().data);
|
||||
let program_id = Program::program_owner_changer().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -623,8 +630,8 @@ pub mod tests {
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
||||
.with_test_programs()
|
||||
.with_non_default_accounts_but_default_program_owners();
|
||||
let address = Address::new([254; 32]);
|
||||
let account = state.get_account_by_address(&address);
|
||||
let account_id = AccountId::new([254; 32]);
|
||||
let account = state.get_account_by_id(&account_id);
|
||||
// Assert the target account only differs from the default account in nonce field
|
||||
assert_eq!(account.program_owner, Account::default().program_owner);
|
||||
assert_eq!(account.balance, Account::default().balance);
|
||||
@ -632,7 +639,7 @@ pub mod tests {
|
||||
assert_eq!(account.data, Account::default().data);
|
||||
let program_id = Program::program_owner_changer().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -647,8 +654,8 @@ pub mod tests {
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
||||
.with_test_programs()
|
||||
.with_non_default_accounts_but_default_program_owners();
|
||||
let address = Address::new([253; 32]);
|
||||
let account = state.get_account_by_address(&address);
|
||||
let account_id = AccountId::new([253; 32]);
|
||||
let account = state.get_account_by_id(&account_id);
|
||||
// Assert the target account only differs from the default account in data field
|
||||
assert_eq!(account.program_owner, Account::default().program_owner);
|
||||
assert_eq!(account.balance, Account::default().balance);
|
||||
@ -656,7 +663,7 @@ pub mod tests {
|
||||
assert_ne!(account.data, Account::default().data);
|
||||
let program_id = Program::program_owner_changer().id();
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -667,20 +674,20 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_program_should_fail_if_transfers_balance_from_non_owned_account() {
|
||||
let initial_data = [(Address::new([1; 32]), 100)];
|
||||
let initial_data = [(AccountId::new([1; 32]), 100)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let sender_address = Address::new([1; 32]);
|
||||
let receiver_address = Address::new([2; 32]);
|
||||
let sender_account_id = AccountId::new([1; 32]);
|
||||
let receiver_account_id = AccountId::new([2; 32]);
|
||||
let balance_to_move: u128 = 1;
|
||||
let program_id = Program::simple_balance_transfer().id();
|
||||
assert_ne!(
|
||||
state.get_account_by_address(&sender_address).program_owner,
|
||||
state.get_account_by_id(&sender_account_id).program_owner,
|
||||
program_id
|
||||
);
|
||||
let message = public_transaction::Message::try_new(
|
||||
program_id,
|
||||
vec![sender_address, receiver_address],
|
||||
vec![sender_account_id, receiver_account_id],
|
||||
vec![],
|
||||
balance_to_move,
|
||||
)
|
||||
@ -699,16 +706,16 @@ pub mod tests {
|
||||
let mut state = V02State::new_with_genesis_accounts(&initial_data, &[])
|
||||
.with_test_programs()
|
||||
.with_non_default_accounts_but_default_program_owners();
|
||||
let address = Address::new([255; 32]);
|
||||
let account_id = AccountId::new([255; 32]);
|
||||
let program_id = Program::data_changer().id();
|
||||
|
||||
assert_ne!(state.get_account_by_address(&address), Account::default());
|
||||
assert_ne!(state.get_account_by_id(&account_id), Account::default());
|
||||
assert_ne!(
|
||||
state.get_account_by_address(&address).program_owner,
|
||||
state.get_account_by_id(&account_id).program_owner,
|
||||
program_id
|
||||
);
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -722,11 +729,11 @@ pub mod tests {
|
||||
let initial_data = [];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let address = Address::new([1; 32]);
|
||||
let account_id = AccountId::new([1; 32]);
|
||||
let program_id = Program::minter().id();
|
||||
|
||||
let message =
|
||||
public_transaction::Message::try_new(program_id, vec![address], vec![], ()).unwrap();
|
||||
public_transaction::Message::try_new(program_id, vec![account_id], vec![], ()).unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
@ -742,17 +749,17 @@ pub mod tests {
|
||||
.with_test_programs()
|
||||
.with_account_owned_by_burner_program();
|
||||
let program_id = Program::burner().id();
|
||||
let address = Address::new([252; 32]);
|
||||
let account_id = AccountId::new([252; 32]);
|
||||
assert_eq!(
|
||||
state.get_account_by_address(&address).program_owner,
|
||||
state.get_account_by_id(&account_id).program_owner,
|
||||
program_id
|
||||
);
|
||||
let balance_to_burn: u128 = 1;
|
||||
assert!(state.get_account_by_address(&address).balance > balance_to_burn);
|
||||
assert!(state.get_account_by_id(&account_id).balance > balance_to_burn);
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
program_id,
|
||||
vec![address],
|
||||
vec![account_id],
|
||||
vec![],
|
||||
balance_to_burn,
|
||||
)
|
||||
@ -769,8 +776,8 @@ pub mod tests {
|
||||
}
|
||||
|
||||
impl TestPublicKeys {
|
||||
pub fn address(&self) -> Address {
|
||||
Address::from(&PublicKey::new_from_private_key(&self.signing_key))
|
||||
pub fn account_id(&self) -> AccountId {
|
||||
AccountId::from(&PublicKey::new_from_private_key(&self.signing_key))
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,9 +823,9 @@ pub mod tests {
|
||||
state: &V02State,
|
||||
) -> PrivacyPreservingTransaction {
|
||||
let sender = AccountWithMetadata::new(
|
||||
state.get_account_by_address(&sender_keys.address()),
|
||||
state.get_account_by_id(&sender_keys.account_id()),
|
||||
true,
|
||||
sender_keys.address(),
|
||||
sender_keys.account_id(),
|
||||
);
|
||||
|
||||
let sender_nonce = sender.account.nonce;
|
||||
@ -841,7 +848,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![sender_keys.address()],
|
||||
vec![sender_keys.account_id()],
|
||||
vec![sender_nonce],
|
||||
vec![(recipient_keys.npk(), recipient_keys.ivk(), epk)],
|
||||
output,
|
||||
@ -911,7 +918,7 @@ pub mod tests {
|
||||
fn deshielded_balance_transfer_for_tests(
|
||||
sender_keys: &TestPrivateKeys,
|
||||
sender_private_account: &Account,
|
||||
recipient_address: &Address,
|
||||
recipient_account_id: &AccountId,
|
||||
balance_to_move: u128,
|
||||
new_nonce: Nonce,
|
||||
state: &V02State,
|
||||
@ -921,9 +928,9 @@ pub mod tests {
|
||||
let sender_pre =
|
||||
AccountWithMetadata::new(sender_private_account.clone(), true, &sender_keys.npk());
|
||||
let recipient_pre = AccountWithMetadata::new(
|
||||
state.get_account_by_address(recipient_address),
|
||||
state.get_account_by_id(recipient_account_id),
|
||||
false,
|
||||
*recipient_address,
|
||||
*recipient_account_id,
|
||||
);
|
||||
|
||||
let esk = [3; 32];
|
||||
@ -945,7 +952,7 @@ pub mod tests {
|
||||
.unwrap();
|
||||
|
||||
let message = Message::try_from_circuit_output(
|
||||
vec![*recipient_address],
|
||||
vec![*recipient_account_id],
|
||||
vec![],
|
||||
vec![(sender_keys.npk(), sender_keys.ivk(), epk)],
|
||||
output,
|
||||
@ -962,7 +969,8 @@ pub mod tests {
|
||||
let sender_keys = test_public_account_keys_1();
|
||||
let recipient_keys = test_private_account_keys_1();
|
||||
|
||||
let mut state = V02State::new_with_genesis_accounts(&[(sender_keys.address(), 200)], &[]);
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&[(sender_keys.account_id(), 200)], &[]);
|
||||
|
||||
let balance_to_move = 37;
|
||||
|
||||
@ -974,7 +982,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let expected_sender_post = {
|
||||
let mut this = state.get_account_by_address(&sender_keys.address());
|
||||
let mut this = state.get_account_by_id(&sender_keys.account_id());
|
||||
this.balance -= balance_to_move;
|
||||
this.nonce += 1;
|
||||
this
|
||||
@ -987,12 +995,12 @@ pub mod tests {
|
||||
.transition_from_privacy_preserving_transaction(&tx)
|
||||
.unwrap();
|
||||
|
||||
let sender_post = state.get_account_by_address(&sender_keys.address());
|
||||
let sender_post = state.get_account_by_id(&sender_keys.account_id());
|
||||
assert_eq!(sender_post, expected_sender_post);
|
||||
assert!(state.private_state.0.contains(&expected_new_commitment));
|
||||
|
||||
assert_eq!(
|
||||
state.get_account_by_address(&sender_keys.address()).balance,
|
||||
state.get_account_by_id(&sender_keys.account_id()).balance,
|
||||
200 - balance_to_move
|
||||
);
|
||||
}
|
||||
@ -1075,7 +1083,7 @@ pub mod tests {
|
||||
let recipient_keys = test_public_account_keys_1();
|
||||
let recipient_initial_balance = 400;
|
||||
let mut state = V02State::new_with_genesis_accounts(
|
||||
&[(recipient_keys.address(), recipient_initial_balance)],
|
||||
&[(recipient_keys.account_id(), recipient_initial_balance)],
|
||||
&[],
|
||||
)
|
||||
.with_private_account(&sender_keys, &sender_private_account);
|
||||
@ -1083,7 +1091,7 @@ pub mod tests {
|
||||
let balance_to_move = 37;
|
||||
|
||||
let expected_recipient_post = {
|
||||
let mut this = state.get_account_by_address(&recipient_keys.address());
|
||||
let mut this = state.get_account_by_id(&recipient_keys.account_id());
|
||||
this.balance += balance_to_move;
|
||||
this
|
||||
};
|
||||
@ -1091,7 +1099,7 @@ pub mod tests {
|
||||
let tx = deshielded_balance_transfer_for_tests(
|
||||
&sender_keys,
|
||||
&sender_private_account,
|
||||
&recipient_keys.address(),
|
||||
&recipient_keys.account_id(),
|
||||
balance_to_move,
|
||||
0xcafecafe,
|
||||
&state,
|
||||
@ -1119,14 +1127,14 @@ pub mod tests {
|
||||
.transition_from_privacy_preserving_transaction(&tx)
|
||||
.unwrap();
|
||||
|
||||
let recipient_post = state.get_account_by_address(&recipient_keys.address());
|
||||
let recipient_post = state.get_account_by_id(&recipient_keys.account_id());
|
||||
assert_eq!(recipient_post, expected_recipient_post);
|
||||
assert!(state.private_state.0.contains(&sender_pre_commitment));
|
||||
assert!(state.private_state.0.contains(&expected_new_commitment));
|
||||
assert!(state.private_state.1.contains(&expected_new_nullifier));
|
||||
assert_eq!(
|
||||
state
|
||||
.get_account_by_address(&recipient_keys.address())
|
||||
.get_account_by_id(&recipient_keys.account_id())
|
||||
.balance,
|
||||
recipient_initial_balance + balance_to_move
|
||||
);
|
||||
@ -2046,18 +2054,18 @@ pub mod tests {
|
||||
fn test_claiming_mechanism() {
|
||||
let program = Program::authenticated_transfer_program();
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||
let account_id = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let initial_balance = 100;
|
||||
let initial_data = [(address, initial_balance)];
|
||||
let initial_data = [(account_id, initial_balance)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let from = address;
|
||||
let from = account_id;
|
||||
let from_key = key;
|
||||
let to = Address::new([2; 32]);
|
||||
let to = AccountId::new([2; 32]);
|
||||
let amount: u128 = 37;
|
||||
|
||||
// Check the recipient is an uninitialized account
|
||||
assert_eq!(state.get_account_by_address(&to), Account::default());
|
||||
assert_eq!(state.get_account_by_id(&to), Account::default());
|
||||
|
||||
let expected_recipient_post = Account {
|
||||
program_owner: program.id(),
|
||||
@ -2073,36 +2081,36 @@ pub mod tests {
|
||||
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
|
||||
let recipient_post = state.get_account_by_address(&to);
|
||||
let recipient_post = state.get_account_by_id(&to);
|
||||
|
||||
assert_eq!(recipient_post, expected_recipient_post);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chained_call() {
|
||||
fn test_chained_call_succeeds() {
|
||||
let program = Program::chain_caller();
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let address = Address::from(&PublicKey::new_from_private_key(&key));
|
||||
let from = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let to = AccountId::new([2; 32]);
|
||||
let initial_balance = 100;
|
||||
let initial_data = [(address, initial_balance)];
|
||||
let initial_data = [(from, initial_balance), (to, 0)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let from = address;
|
||||
let from_key = key;
|
||||
let to = Address::new([2; 32]);
|
||||
let amount: u128 = 37;
|
||||
let instruction: (u128, ProgramId) =
|
||||
(amount, Program::authenticated_transfer_program().id());
|
||||
let amount: u128 = 0;
|
||||
let instruction: (u128, ProgramId, u32) =
|
||||
(amount, Program::authenticated_transfer_program().id(), 2);
|
||||
|
||||
let expected_to_post = Account {
|
||||
program_owner: Program::chain_caller().id(),
|
||||
balance: amount,
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
balance: amount * 2, // The `chain_caller` chains the program twice
|
||||
..Account::default()
|
||||
};
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
vec![to, from], //The chain_caller program permutes the account order in the chain call
|
||||
vec![to, from], // The chain_caller program permutes the account order in the chain
|
||||
// call
|
||||
vec![0],
|
||||
instruction,
|
||||
)
|
||||
@ -2112,9 +2120,46 @@ pub mod tests {
|
||||
|
||||
state.transition_from_public_transaction(&tx).unwrap();
|
||||
|
||||
let from_post = state.get_account_by_address(&from);
|
||||
let to_post = state.get_account_by_address(&to);
|
||||
assert_eq!(from_post.balance, initial_balance - amount);
|
||||
let from_post = state.get_account_by_id(&from);
|
||||
let to_post = state.get_account_by_id(&to);
|
||||
// The `chain_caller` program calls the program twice
|
||||
assert_eq!(from_post.balance, initial_balance - 2 * amount);
|
||||
assert_eq!(to_post, expected_to_post);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_execution_fails_if_chained_calls_exceeds_depth() {
|
||||
let program = Program::chain_caller();
|
||||
let key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
let from = AccountId::from(&PublicKey::new_from_private_key(&key));
|
||||
let to = AccountId::new([2; 32]);
|
||||
let initial_balance = 100;
|
||||
let initial_data = [(from, initial_balance), (to, 0)];
|
||||
let mut state =
|
||||
V02State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs();
|
||||
let from_key = key;
|
||||
let amount: u128 = 0;
|
||||
let instruction: (u128, ProgramId, u32) = (
|
||||
amount,
|
||||
Program::authenticated_transfer_program().id(),
|
||||
MAX_NUMBER_CHAINED_CALLS as u32 + 1,
|
||||
);
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
program.id(),
|
||||
vec![to, from], // The chain_caller program permutes the account order in the chain
|
||||
// call
|
||||
vec![0],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = public_transaction::WitnessSet::for_message(&message, &[&from_key]);
|
||||
let tx = PublicTransaction::new(message, witness_set);
|
||||
|
||||
let result = state.transition_from_public_transaction(&tx);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(NssaError::MaxChainedCallsDepthExceeded)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
1
nssa/test_program_methods/guest/Cargo.lock
generated
1
nssa/test_program_methods/guest/Cargo.lock
generated
@ -1579,6 +1579,7 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e"
|
||||
name = "nssa-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"borsh",
|
||||
"chacha20",
|
||||
"risc0-zkvm",
|
||||
"serde",
|
||||
|
||||
@ -3,14 +3,14 @@ use nssa_core::program::{
|
||||
};
|
||||
use risc0_zkvm::serde::to_vec;
|
||||
|
||||
type Instruction = (u128, ProgramId);
|
||||
type Instruction = (u128, ProgramId, u32);
|
||||
|
||||
/// A program that calls another program.
|
||||
/// A program that calls another program `num_chain_calls` times.
|
||||
/// It permutes the order of the input accounts on the subsequent call
|
||||
fn main() {
|
||||
let ProgramInput {
|
||||
pre_states,
|
||||
instruction: (balance, program_id),
|
||||
instruction: (balance, program_id, num_chain_calls),
|
||||
} = read_nssa_inputs::<Instruction>();
|
||||
|
||||
let [sender_pre, receiver_pre] = match pre_states.try_into() {
|
||||
@ -20,10 +20,19 @@ fn main() {
|
||||
|
||||
let instruction_data = to_vec(&balance).unwrap();
|
||||
|
||||
let chained_call = Some(ChainedCall {
|
||||
let mut chained_call = vec![
|
||||
ChainedCall {
|
||||
program_id,
|
||||
instruction_data: instruction_data.clone(),
|
||||
pre_states: vec![receiver_pre.clone(), sender_pre.clone()], // <- Account order permutation here
|
||||
};
|
||||
num_chain_calls as usize - 1
|
||||
];
|
||||
|
||||
chained_call.push(ChainedCall {
|
||||
program_id,
|
||||
instruction_data,
|
||||
account_indices: vec![1, 0], // <- Account order permutation here
|
||||
pre_states: vec![receiver_pre.clone(), sender_pre.clone()], // <- Account order permutation here
|
||||
});
|
||||
|
||||
write_nssa_outputs_with_chained_call(
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
channel = "1.91.1"
|
||||
profile = "default"
|
||||
|
||||
11
rustfmt.toml
11
rustfmt.toml
@ -1 +1,12 @@
|
||||
edition = "2024"
|
||||
|
||||
newline_style = "Unix"
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
group_imports = "StdExternalCrate"
|
||||
imports_granularity = "Crate"
|
||||
normalize_comments = true
|
||||
reorder_impl_items = true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
format_code_in_doc_comments = true
|
||||
|
||||
@ -28,3 +28,7 @@ path = "../nssa"
|
||||
[features]
|
||||
default = []
|
||||
testnet = []
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
futures.workspace = true
|
||||
|
||||
@ -4,16 +4,16 @@ use anyhow::Result;
|
||||
use common::{HashType, block::Block, transaction::EncodedTransaction};
|
||||
use storage::RocksDBIO;
|
||||
|
||||
pub struct SequecerBlockStore {
|
||||
pub struct SequencerBlockStore {
|
||||
dbio: RocksDBIO,
|
||||
// TODO: Consider adding the hashmap to the database for faster recovery.
|
||||
pub tx_hash_to_block_map: HashMap<HashType, u64>,
|
||||
pub genesis_id: u64,
|
||||
pub signing_key: nssa::PrivateKey,
|
||||
tx_hash_to_block_map: HashMap<HashType, u64>,
|
||||
genesis_id: u64,
|
||||
signing_key: nssa::PrivateKey,
|
||||
}
|
||||
|
||||
impl SequecerBlockStore {
|
||||
///Starting database at the start of new chain.
|
||||
impl SequencerBlockStore {
|
||||
/// Starting database at the start of new chain.
|
||||
/// Creates files if necessary.
|
||||
///
|
||||
/// ATTENTION: Will overwrite genesis block.
|
||||
@ -40,9 +40,9 @@ impl SequecerBlockStore {
|
||||
})
|
||||
}
|
||||
|
||||
///Reopening existing database
|
||||
/// Reopening existing database
|
||||
pub fn open_db_restart(location: &Path, signing_key: nssa::PrivateKey) -> Result<Self> {
|
||||
SequecerBlockStore::open_db_with_genesis(location, None, signing_key)
|
||||
SequencerBlockStore::open_db_with_genesis(location, None, signing_key)
|
||||
}
|
||||
|
||||
pub fn get_block_at_id(&self, id: u64) -> Result<Block> {
|
||||
@ -69,6 +69,18 @@ impl SequecerBlockStore {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, tx: &EncodedTransaction, block_id: u64) {
|
||||
self.tx_hash_to_block_map.insert(tx.hash(), block_id);
|
||||
}
|
||||
|
||||
pub fn genesis_id(&self) -> u64 {
|
||||
self.genesis_id
|
||||
}
|
||||
|
||||
pub fn signing_key(&self) -> &nssa::PrivateKey {
|
||||
&self.signing_key
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap<HashType, u64> {
|
||||
@ -82,11 +94,11 @@ pub(crate) fn block_to_transactions_map(block: &Block) -> HashMap<HashType, u64>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use common::{block::HashableBlockData, test_utils::sequencer_sign_key_for_testing};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_get_transaction_by_hash() {
|
||||
let temp_dir = tempdir().unwrap();
|
||||
@ -104,7 +116,7 @@ mod tests {
|
||||
let genesis_block = genesis_block_hashable_data.into_block(&signing_key);
|
||||
// Start an empty node store
|
||||
let mut node_store =
|
||||
SequecerBlockStore::open_db_with_genesis(path, Some(genesis_block), signing_key)
|
||||
SequencerBlockStore::open_db_with_genesis(path, Some(genesis_block), signing_key)
|
||||
.unwrap();
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
|
||||
@ -1,43 +1,45 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
///Helperstruct for account serialization
|
||||
/// Helperstruct for account serialization
|
||||
pub struct AccountInitialData {
|
||||
///Hex encoded `AccountAddress`
|
||||
pub addr: String,
|
||||
/// Hex encoded account id
|
||||
pub account_id: String,
|
||||
pub balance: u128,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
///Helperstruct to initialize commitments
|
||||
/// Helperstruct to initialize commitments
|
||||
pub struct CommitmentsInitialData {
|
||||
pub npk: nssa_core::NullifierPublicKey,
|
||||
pub account: nssa_core::account::Account,
|
||||
}
|
||||
|
||||
// TODO: Provide default values
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct SequencerConfig {
|
||||
///Home dir of sequencer storage
|
||||
/// Home dir of sequencer storage
|
||||
pub home: PathBuf,
|
||||
///Override rust log (env var logging level)
|
||||
/// Override rust log (env var logging level)
|
||||
pub override_rust_log: Option<String>,
|
||||
///Genesis id
|
||||
/// Genesis id
|
||||
pub genesis_id: u64,
|
||||
///If `True`, then adds random sequence of bytes to genesis block
|
||||
/// If `True`, then adds random sequence of bytes to genesis block
|
||||
pub is_genesis_random: bool,
|
||||
///Maximum number of transactions in block
|
||||
/// Maximum number of transactions in block
|
||||
pub max_num_tx_in_block: usize,
|
||||
///Mempool maximum size
|
||||
/// Mempool maximum size
|
||||
pub mempool_max_size: usize,
|
||||
///Interval in which blocks produced
|
||||
/// Interval in which blocks produced
|
||||
pub block_create_timeout_millis: u64,
|
||||
///Port to listen
|
||||
/// Port to listen
|
||||
pub port: u16,
|
||||
///List of initial accounts data
|
||||
/// List of initial accounts data
|
||||
pub initial_accounts: Vec<AccountInitialData>,
|
||||
///List of initial commitments
|
||||
/// List of initial commitments
|
||||
pub initial_commitments: Vec<CommitmentsInitialData>,
|
||||
///Sequencer own signing key
|
||||
/// Sequencer own signing key
|
||||
pub signing_key: [u8; 32],
|
||||
}
|
||||
|
||||
@ -10,25 +10,24 @@ use common::{
|
||||
};
|
||||
use config::SequencerConfig;
|
||||
use log::warn;
|
||||
use mempool::MemPool;
|
||||
use mempool::{MemPool, MemPoolHandle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::block_store::SequecerBlockStore;
|
||||
use crate::block_store::SequencerBlockStore;
|
||||
|
||||
pub mod block_store;
|
||||
pub mod config;
|
||||
|
||||
pub struct SequencerCore {
|
||||
pub state: nssa::V02State,
|
||||
pub block_store: SequecerBlockStore,
|
||||
pub mempool: MemPool<EncodedTransaction>,
|
||||
pub sequencer_config: SequencerConfig,
|
||||
pub chain_height: u64,
|
||||
state: nssa::V02State,
|
||||
block_store: SequencerBlockStore,
|
||||
mempool: MemPool<EncodedTransaction>,
|
||||
sequencer_config: SequencerConfig,
|
||||
chain_height: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum TransactionMalformationError {
|
||||
MempoolFullForRound,
|
||||
InvalidSignature,
|
||||
FailedToDecode { tx: HashType },
|
||||
}
|
||||
@ -42,7 +41,8 @@ impl Display for TransactionMalformationError {
|
||||
impl std::error::Error for TransactionMalformationError {}
|
||||
|
||||
impl SequencerCore {
|
||||
pub fn start_from_config(config: SequencerConfig) -> Self {
|
||||
/// Start Sequencer from configuration and construct transaction sender
|
||||
pub fn start_from_config(config: SequencerConfig) -> (Self, MemPoolHandle<EncodedTransaction>) {
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: config.genesis_id,
|
||||
transactions: vec![],
|
||||
@ -53,9 +53,9 @@ impl SequencerCore {
|
||||
let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap();
|
||||
let genesis_block = hashable_data.into_block(&signing_key);
|
||||
|
||||
//Sequencer should panic if unable to open db,
|
||||
//as fixing this issue may require actions non-native to program scope
|
||||
let block_store = SequecerBlockStore::open_db_with_genesis(
|
||||
// Sequencer should panic if unable to open db,
|
||||
// as fixing this issue may require actions non-native to program scope
|
||||
let block_store = SequencerBlockStore::open_db_with_genesis(
|
||||
&config.home.join("rocksdb"),
|
||||
Some(genesis_block),
|
||||
signing_key,
|
||||
@ -75,10 +75,10 @@ impl SequencerCore {
|
||||
initial_commitments.push(comm);
|
||||
}
|
||||
|
||||
let init_accs: Vec<(nssa::Address, u128)> = config
|
||||
let init_accs: Vec<(nssa::AccountId, u128)> = config
|
||||
.initial_accounts
|
||||
.iter()
|
||||
.map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance))
|
||||
.map(|acc_data| (acc_data.account_id.parse().unwrap(), acc_data.balance))
|
||||
.collect();
|
||||
|
||||
let mut state = nssa::V02State::new_with_genesis_accounts(&init_accs, &initial_commitments);
|
||||
@ -86,21 +86,23 @@ impl SequencerCore {
|
||||
#[cfg(feature = "testnet")]
|
||||
state.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
||||
|
||||
let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size);
|
||||
let mut this = Self {
|
||||
state,
|
||||
block_store,
|
||||
mempool: MemPool::default(),
|
||||
mempool,
|
||||
chain_height: config.genesis_id,
|
||||
sequencer_config: config,
|
||||
};
|
||||
|
||||
this.sync_state_with_stored_blocks();
|
||||
|
||||
this
|
||||
(this, mempool_handle)
|
||||
}
|
||||
|
||||
/// If there are stored blocks ahead of the current height, this method will load and process all transaction
|
||||
/// in them in the order they are stored. The NSSA state will be updated accordingly.
|
||||
/// If there are stored blocks ahead of the current height, this method will load and process
|
||||
/// all transaction in them in the order they are stored. The NSSA state will be updated
|
||||
/// accordingly.
|
||||
fn sync_state_with_stored_blocks(&mut self) {
|
||||
let mut next_block_id = self.sequencer_config.genesis_id + 1;
|
||||
while let Ok(block) = self.block_store.get_block_at_id(next_block_id) {
|
||||
@ -110,108 +112,50 @@ impl SequencerCore {
|
||||
self.execute_check_transaction_on_state(transaction)
|
||||
.unwrap();
|
||||
// Update the tx hash to block id map.
|
||||
self.block_store
|
||||
.tx_hash_to_block_map
|
||||
.insert(encoded_transaction.hash(), next_block_id);
|
||||
self.block_store.insert(&encoded_transaction, next_block_id);
|
||||
}
|
||||
self.chain_height = next_block_id;
|
||||
next_block_id += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transaction_pre_check(
|
||||
&mut self,
|
||||
tx: NSSATransaction,
|
||||
) -> Result<NSSATransaction, TransactionMalformationError> {
|
||||
// Stateless checks here
|
||||
match tx {
|
||||
NSSATransaction::Public(tx) => {
|
||||
if tx.witness_set().is_valid_for(tx.message()) {
|
||||
Ok(NSSATransaction::Public(tx))
|
||||
} else {
|
||||
Err(TransactionMalformationError::InvalidSignature)
|
||||
}
|
||||
}
|
||||
NSSATransaction::PrivacyPreserving(tx) => {
|
||||
if tx.witness_set().signatures_are_valid_for(tx.message()) {
|
||||
Ok(NSSATransaction::PrivacyPreserving(tx))
|
||||
} else {
|
||||
Err(TransactionMalformationError::InvalidSignature)
|
||||
}
|
||||
}
|
||||
NSSATransaction::ProgramDeployment(tx) => Ok(NSSATransaction::ProgramDeployment(tx)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_tx_into_mempool_pre_check(
|
||||
&mut self,
|
||||
transaction: EncodedTransaction,
|
||||
) -> Result<(), TransactionMalformationError> {
|
||||
let transaction = NSSATransaction::try_from(&transaction).map_err(|_| {
|
||||
TransactionMalformationError::FailedToDecode {
|
||||
tx: transaction.hash(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let mempool_size = self.mempool.len();
|
||||
if mempool_size >= self.sequencer_config.mempool_max_size {
|
||||
return Err(TransactionMalformationError::MempoolFullForRound);
|
||||
}
|
||||
|
||||
let authenticated_tx = self
|
||||
.transaction_pre_check(transaction)
|
||||
.inspect_err(|err| warn!("Error at pre_check {err:#?}"))?;
|
||||
|
||||
self.mempool.push_item(authenticated_tx.into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_check_transaction_on_state(
|
||||
&mut self,
|
||||
tx: NSSATransaction,
|
||||
) -> Result<NSSATransaction, nssa::error::NssaError> {
|
||||
match &tx {
|
||||
NSSATransaction::Public(tx) => {
|
||||
self.state
|
||||
.transition_from_public_transaction(tx)
|
||||
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
|
||||
}
|
||||
NSSATransaction::PrivacyPreserving(tx) => {
|
||||
self.state
|
||||
.transition_from_privacy_preserving_transaction(tx)
|
||||
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
|
||||
}
|
||||
NSSATransaction::ProgramDeployment(tx) => {
|
||||
self.state
|
||||
.transition_from_program_deployment_transaction(tx)
|
||||
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
|
||||
}
|
||||
NSSATransaction::Public(tx) => self.state.transition_from_public_transaction(tx),
|
||||
NSSATransaction::PrivacyPreserving(tx) => self
|
||||
.state
|
||||
.transition_from_privacy_preserving_transaction(tx),
|
||||
NSSATransaction::ProgramDeployment(tx) => self
|
||||
.state
|
||||
.transition_from_program_deployment_transaction(tx),
|
||||
}
|
||||
.inspect_err(|err| warn!("Error at transition {err:#?}"))?;
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
///Produces new block from transactions in mempool
|
||||
/// Produces new block from transactions in mempool
|
||||
pub fn produce_new_block_with_mempool_transactions(&mut self) -> Result<u64> {
|
||||
let now = Instant::now();
|
||||
let new_block_height = self.chain_height + 1;
|
||||
|
||||
let mut num_valid_transactions_in_block = 0;
|
||||
let mut valid_transactions = vec![];
|
||||
|
||||
while let Some(tx) = self.mempool.pop_last() {
|
||||
while let Some(tx) = self.mempool.pop() {
|
||||
let nssa_transaction = NSSATransaction::try_from(&tx)
|
||||
.map_err(|_| TransactionMalformationError::FailedToDecode { tx: tx.hash() })?;
|
||||
|
||||
if let Ok(valid_tx) = self.execute_check_transaction_on_state(nssa_transaction) {
|
||||
valid_transactions.push(valid_tx.into());
|
||||
|
||||
num_valid_transactions_in_block += 1;
|
||||
|
||||
if num_valid_transactions_in_block >= self.sequencer_config.max_num_tx_in_block {
|
||||
if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Probably need to handle unsuccessful transaction execution?
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,12 +176,22 @@ impl SequencerCore {
|
||||
timestamp: curr_time,
|
||||
};
|
||||
|
||||
let block = hashable_data.into_block(&self.block_store.signing_key);
|
||||
let block = hashable_data.into_block(self.block_store.signing_key());
|
||||
|
||||
self.block_store.put_block_at_id(block)?;
|
||||
|
||||
self.chain_height = new_block_height;
|
||||
|
||||
// TODO: Consider switching to `tracing` crate to have more structured and consistent logs
|
||||
// e.g.
|
||||
//
|
||||
// ```
|
||||
// info!(
|
||||
// num_txs = num_txs_in_block,
|
||||
// time = now.elapsed(),
|
||||
// "Created block"
|
||||
// );
|
||||
// ```
|
||||
log::info!(
|
||||
"Created block with {} transactions in {} seconds",
|
||||
num_txs_in_block,
|
||||
@ -246,17 +200,58 @@ impl SequencerCore {
|
||||
|
||||
Ok(self.chain_height)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &nssa::V02State {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub fn block_store(&self) -> &SequencerBlockStore {
|
||||
&self.block_store
|
||||
}
|
||||
|
||||
pub fn chain_height(&self) -> u64 {
|
||||
self.chain_height
|
||||
}
|
||||
|
||||
pub fn sequencer_config(&self) -> &SequencerConfig {
|
||||
&self.sequencer_config
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Introduce type-safe wrapper around checked transaction, e.g. AuthenticatedTransaction
|
||||
pub fn transaction_pre_check(
|
||||
tx: NSSATransaction,
|
||||
) -> Result<NSSATransaction, TransactionMalformationError> {
|
||||
// Stateless checks here
|
||||
match tx {
|
||||
NSSATransaction::Public(tx) => {
|
||||
if tx.witness_set().is_valid_for(tx.message()) {
|
||||
Ok(NSSATransaction::Public(tx))
|
||||
} else {
|
||||
Err(TransactionMalformationError::InvalidSignature)
|
||||
}
|
||||
}
|
||||
NSSATransaction::PrivacyPreserving(tx) => {
|
||||
if tx.witness_set().signatures_are_valid_for(tx.message()) {
|
||||
Ok(NSSATransaction::PrivacyPreserving(tx))
|
||||
} else {
|
||||
Err(TransactionMalformationError::InvalidSignature)
|
||||
}
|
||||
}
|
||||
NSSATransaction::ProgramDeployment(tx) => Ok(NSSATransaction::ProgramDeployment(tx)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::pin::pin;
|
||||
|
||||
use base58::{FromBase58, ToBase58};
|
||||
use common::test_utils::sequencer_sign_key_for_testing;
|
||||
use nssa::PrivateKey;
|
||||
|
||||
use crate::config::AccountInitialData;
|
||||
|
||||
use super::*;
|
||||
use crate::config::AccountInitialData;
|
||||
|
||||
fn parse_unwrap_tx_body_into_nssa_tx(tx_body: EncodedTransaction) -> NSSATransaction {
|
||||
NSSATransaction::try_from(&tx_body)
|
||||
@ -286,23 +281,23 @@ mod tests {
|
||||
}
|
||||
|
||||
fn setup_sequencer_config() -> SequencerConfig {
|
||||
let acc1_addr: Vec<u8> = vec![
|
||||
let acc1_account_id: Vec<u8> = vec![
|
||||
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
||||
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
||||
];
|
||||
|
||||
let acc2_addr: Vec<u8> = vec![
|
||||
let acc2_account_id: Vec<u8> = vec![
|
||||
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
||||
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
||||
];
|
||||
|
||||
let initial_acc1 = AccountInitialData {
|
||||
addr: acc1_addr.to_base58(),
|
||||
account_id: acc1_account_id.to_base58(),
|
||||
balance: 10000,
|
||||
};
|
||||
|
||||
let initial_acc2 = AccountInitialData {
|
||||
addr: acc2_addr.to_base58(),
|
||||
account_id: acc2_account_id.to_base58(),
|
||||
balance: 20000,
|
||||
};
|
||||
|
||||
@ -319,33 +314,44 @@ mod tests {
|
||||
nssa::PrivateKey::try_new([2; 32]).unwrap()
|
||||
}
|
||||
|
||||
fn common_setup(sequencer: &mut SequencerCore) {
|
||||
async fn common_setup() -> (SequencerCore, MemPoolHandle<EncodedTransaction>) {
|
||||
let config = setup_sequencer_config();
|
||||
common_setup_with_config(config).await
|
||||
}
|
||||
|
||||
async fn common_setup_with_config(
|
||||
config: SequencerConfig,
|
||||
) -> (SequencerCore, MemPoolHandle<EncodedTransaction>) {
|
||||
let (mut sequencer, mempool_handle) = SequencerCore::start_from_config(config);
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
sequencer.mempool.push_item(tx);
|
||||
mempool_handle.push(tx).await.unwrap();
|
||||
|
||||
sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
|
||||
(sequencer, mempool_handle)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start_from_config() {
|
||||
let config = setup_sequencer_config();
|
||||
let sequencer = SequencerCore::start_from_config(config.clone());
|
||||
let (sequencer, _mempool_handle) = SequencerCore::start_from_config(config.clone());
|
||||
|
||||
assert_eq!(sequencer.chain_height, config.genesis_id);
|
||||
assert_eq!(sequencer.sequencer_config.max_num_tx_in_block, 10);
|
||||
assert_eq!(sequencer.sequencer_config.port, 8080);
|
||||
|
||||
let acc1_addr = config.initial_accounts[0]
|
||||
.addr
|
||||
let acc1_account_id = config.initial_accounts[0]
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2_addr = config.initial_accounts[1]
|
||||
.addr
|
||||
let acc2_account_id = config.initial_accounts[1]
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -354,11 +360,11 @@ mod tests {
|
||||
|
||||
let balance_acc_1 = sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc1_addr))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc1_account_id))
|
||||
.balance;
|
||||
let balance_acc_2 = sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc2_addr))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc2_account_id))
|
||||
.balance;
|
||||
|
||||
assert_eq!(10000, balance_acc_1);
|
||||
@ -367,40 +373,40 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_start_different_intial_accounts_balances() {
|
||||
let acc1_addr: Vec<u8> = vec![
|
||||
let acc1_account_id: Vec<u8> = vec![
|
||||
27, 132, 197, 86, 123, 18, 100, 64, 153, 93, 62, 213, 170, 186, 5, 101, 215, 30, 24,
|
||||
52, 96, 72, 25, 255, 156, 23, 245, 233, 213, 221, 7, 143,
|
||||
];
|
||||
|
||||
let acc2_addr: Vec<u8> = vec![
|
||||
let acc2_account_id: Vec<u8> = vec![
|
||||
77, 75, 108, 209, 54, 16, 50, 202, 155, 210, 174, 185, 217, 0, 170, 77, 69, 217, 234,
|
||||
216, 10, 201, 66, 51, 116, 196, 81, 167, 37, 77, 7, 102,
|
||||
];
|
||||
|
||||
let initial_acc1 = AccountInitialData {
|
||||
addr: acc1_addr.to_base58(),
|
||||
account_id: acc1_account_id.to_base58(),
|
||||
balance: 10000,
|
||||
};
|
||||
|
||||
let initial_acc2 = AccountInitialData {
|
||||
addr: acc2_addr.to_base58(),
|
||||
account_id: acc2_account_id.to_base58(),
|
||||
balance: 20000,
|
||||
};
|
||||
|
||||
let initial_accounts = vec![initial_acc1, initial_acc2];
|
||||
|
||||
let config = setup_sequencer_config_variable_initial_accounts(initial_accounts);
|
||||
let sequencer = SequencerCore::start_from_config(config.clone());
|
||||
let (sequencer, _mempool_handle) = SequencerCore::start_from_config(config.clone());
|
||||
|
||||
let acc1_addr = config.initial_accounts[0]
|
||||
.addr
|
||||
let acc1_account_id = config.initial_accounts[0]
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2_addr = config.initial_accounts[1]
|
||||
.addr
|
||||
let acc2_account_id = config.initial_accounts[1]
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -411,47 +417,39 @@ mod tests {
|
||||
10000,
|
||||
sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc1_addr))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc1_account_id))
|
||||
.balance
|
||||
);
|
||||
assert_eq!(
|
||||
20000,
|
||||
sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc2_addr))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc2_account_id))
|
||||
.balance
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_pre_check_pass() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
let result = transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_pre_check_native_transfer_valid() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_valid() {
|
||||
let (sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -463,27 +461,24 @@ mod tests {
|
||||
let tx = common::test_utils::create_transaction_native_token_transfer(
|
||||
acc1, 0, acc2, 10, sign_key1,
|
||||
);
|
||||
let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
let result = transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_pre_check_native_transfer_other_signature() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_other_signature() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -497,9 +492,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Signature is valid, stateless check pass
|
||||
let tx = sequencer
|
||||
.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx))
|
||||
.unwrap();
|
||||
let tx = transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx)).unwrap();
|
||||
|
||||
// Signature is not from sender. Execution fails
|
||||
let result = sequencer.execute_check_transaction_on_state(tx);
|
||||
@ -510,22 +503,19 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_pre_check_native_transfer_sent_too_much() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_transaction_pre_check_native_transfer_sent_too_much() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -538,9 +528,9 @@ mod tests {
|
||||
acc1, 0, acc2, 10000000, sign_key1,
|
||||
);
|
||||
|
||||
let result = sequencer.transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
let result = transaction_pre_check(parse_unwrap_tx_body_into_nssa_tx(tx));
|
||||
|
||||
//Passed pre-check
|
||||
// Passed pre-check
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = sequencer.execute_check_transaction_on_state(result.unwrap());
|
||||
@ -552,22 +542,19 @@ mod tests {
|
||||
assert!(is_failed_at_balance_mismatch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_execute_native_transfer() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_transaction_execute_native_transfer() {
|
||||
let (mut sequencer, _mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -586,84 +573,70 @@ mod tests {
|
||||
|
||||
let bal_from = sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc1))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc1))
|
||||
.balance;
|
||||
let bal_to = sequencer
|
||||
.state
|
||||
.get_account_by_address(&nssa::Address::new(acc2))
|
||||
.get_account_by_id(&nssa::AccountId::new(acc2))
|
||||
.balance;
|
||||
|
||||
assert_eq!(bal_from, 9900);
|
||||
assert_eq!(bal_to, 20100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_tx_into_mempool_fails_mempool_full() {
|
||||
#[tokio::test]
|
||||
async fn test_push_tx_into_mempool_blocks_until_mempool_is_full() {
|
||||
let config = SequencerConfig {
|
||||
mempool_max_size: 1,
|
||||
..setup_sequencer_config()
|
||||
};
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
let (mut sequencer, mempool_handle) = common_setup_with_config(config).await;
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
|
||||
// Fill the mempool
|
||||
sequencer.mempool.push_item(tx.clone());
|
||||
mempool_handle.push(tx.clone()).await.unwrap();
|
||||
|
||||
let result = sequencer.push_tx_into_mempool_pre_check(tx);
|
||||
// Check that pushing another transaction will block
|
||||
let mut push_fut = pin!(mempool_handle.push(tx.clone()));
|
||||
let poll = futures::poll!(push_fut.as_mut());
|
||||
assert!(poll.is_pending());
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(TransactionMalformationError::MempoolFullForRound)
|
||||
));
|
||||
// Empty the mempool by producing a block
|
||||
sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
|
||||
// Resolve the pending push
|
||||
assert!(push_fut.await.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_tx_into_mempool_pre_check() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
|
||||
let result = sequencer.push_tx_into_mempool_pre_check(tx);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(sequencer.mempool.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_produce_new_block_with_mempool_transactions() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
#[tokio::test]
|
||||
async fn test_produce_new_block_with_mempool_transactions() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
let genesis_height = sequencer.chain_height;
|
||||
|
||||
let tx = common::test_utils::produce_dummy_empty_transaction();
|
||||
sequencer.mempool.push_item(tx);
|
||||
mempool_handle.push(tx).await.unwrap();
|
||||
|
||||
let block_id = sequencer.produce_new_block_with_mempool_transactions();
|
||||
assert!(block_id.is_ok());
|
||||
assert_eq!(block_id.unwrap(), genesis_height + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replay_transactions_are_rejected_in_the_same_block() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_replay_transactions_are_rejected_in_the_same_block() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -679,8 +652,8 @@ mod tests {
|
||||
let tx_original = tx.clone();
|
||||
let tx_replay = tx.clone();
|
||||
// Pushing two copies of the same tx to the mempool
|
||||
sequencer.mempool.push_item(tx_original);
|
||||
sequencer.mempool.push_item(tx_replay);
|
||||
mempool_handle.push(tx_original).await.unwrap();
|
||||
mempool_handle.push(tx_replay).await.unwrap();
|
||||
|
||||
// Create block
|
||||
let current_height = sequencer
|
||||
@ -695,22 +668,19 @@ mod tests {
|
||||
assert_eq!(block.body.transactions, vec![tx.clone()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replay_transactions_are_rejected_in_different_blocks() {
|
||||
let config = setup_sequencer_config();
|
||||
let mut sequencer = SequencerCore::start_from_config(config);
|
||||
|
||||
common_setup(&mut sequencer);
|
||||
#[tokio::test]
|
||||
async fn test_replay_transactions_are_rejected_in_different_blocks() {
|
||||
let (mut sequencer, mempool_handle) = common_setup().await;
|
||||
|
||||
let acc1 = sequencer.sequencer_config.initial_accounts[0]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let acc2 = sequencer.sequencer_config.initial_accounts[1]
|
||||
.addr
|
||||
.account_id
|
||||
.clone()
|
||||
.from_base58()
|
||||
.unwrap()
|
||||
@ -724,7 +694,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// The transaction should be included the first time
|
||||
sequencer.mempool.push_item(tx.clone());
|
||||
mempool_handle.push(tx.clone()).await.unwrap();
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
@ -735,7 +705,7 @@ mod tests {
|
||||
assert_eq!(block.body.transactions, vec![tx.clone()]);
|
||||
|
||||
// Add same transaction should fail
|
||||
sequencer.mempool.push_item(tx);
|
||||
mempool_handle.push(tx.clone()).await.unwrap();
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
@ -746,29 +716,31 @@ mod tests {
|
||||
assert!(block.body.transactions.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_restart_from_storage() {
|
||||
#[tokio::test]
|
||||
async fn test_restart_from_storage() {
|
||||
let config = setup_sequencer_config();
|
||||
let acc1_addr: nssa::Address = config.initial_accounts[0].addr.parse().unwrap();
|
||||
let acc2_addr: nssa::Address = config.initial_accounts[1].addr.parse().unwrap();
|
||||
let acc1_account_id: nssa::AccountId =
|
||||
config.initial_accounts[0].account_id.parse().unwrap();
|
||||
let acc2_account_id: nssa::AccountId =
|
||||
config.initial_accounts[1].account_id.parse().unwrap();
|
||||
let balance_to_move = 13;
|
||||
|
||||
// In the following code block a transaction will be processed that moves `balance_to_move`
|
||||
// from `acc_1` to `acc_2`. The block created with that transaction will be kept stored in
|
||||
// the temporary directory for the block storage of this test.
|
||||
{
|
||||
let mut sequencer = SequencerCore::start_from_config(config.clone());
|
||||
let (mut sequencer, mempool_handle) = SequencerCore::start_from_config(config.clone());
|
||||
let signing_key = PrivateKey::try_new([1; 32]).unwrap();
|
||||
|
||||
let tx = common::test_utils::create_transaction_native_token_transfer(
|
||||
*acc1_addr.value(),
|
||||
*acc1_account_id.value(),
|
||||
0,
|
||||
*acc2_addr.value(),
|
||||
*acc2_account_id.value(),
|
||||
balance_to_move,
|
||||
signing_key,
|
||||
);
|
||||
|
||||
sequencer.mempool.push_item(tx.clone());
|
||||
mempool_handle.push(tx.clone()).await.unwrap();
|
||||
let current_height = sequencer
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
.unwrap();
|
||||
@ -781,9 +753,9 @@ mod tests {
|
||||
|
||||
// Instantiating a new sequencer from the same config. This should load the existing block
|
||||
// with the above transaction and update the state to reflect that.
|
||||
let sequencer = SequencerCore::start_from_config(config.clone());
|
||||
let balance_acc_1 = sequencer.state.get_account_by_address(&acc1_addr).balance;
|
||||
let balance_acc_2 = sequencer.state.get_account_by_address(&acc2_addr).balance;
|
||||
let (sequencer, _mempool_handle) = SequencerCore::start_from_config(config.clone());
|
||||
let balance_acc_1 = sequencer.state.get_account_by_id(&acc1_account_id).balance;
|
||||
let balance_acc_2 = sequencer.state.get_account_by_id(&acc2_account_id).balance;
|
||||
|
||||
// Balances should be consistent with the stored block
|
||||
assert_eq!(
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use block_store::SequecerBlockStore;
|
||||
use common::block::HashableBlockData;
|
||||
use nssa::{self, Address};
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use crate::config::AccountInitialData;
|
||||
|
||||
pub mod block_store;
|
||||
|
||||
pub struct SequecerChainStore {
|
||||
pub state: nssa::V02State,
|
||||
pub block_store: SequecerBlockStore,
|
||||
}
|
||||
|
||||
impl SequecerChainStore {
|
||||
pub fn new_with_genesis(
|
||||
home_dir: &Path,
|
||||
genesis_id: u64,
|
||||
is_genesis_random: bool,
|
||||
initial_accounts: &[AccountInitialData],
|
||||
initial_commitments: &[nssa_core::Commitment],
|
||||
signing_key: nssa::PrivateKey,
|
||||
) -> Self {
|
||||
let init_accs: Vec<(Address, u128)> = initial_accounts
|
||||
.iter()
|
||||
.map(|acc_data| (acc_data.addr.parse().unwrap(), acc_data.balance))
|
||||
.collect();
|
||||
|
||||
#[cfg(not(feature = "testnet"))]
|
||||
let state = nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments);
|
||||
|
||||
#[cfg(feature = "testnet")]
|
||||
let state = {
|
||||
use common::PINATA_BASE58;
|
||||
|
||||
let mut this =
|
||||
nssa::V02State::new_with_genesis_accounts(&init_accs, initial_commitments);
|
||||
this.add_pinata_program(PINATA_BASE58.parse().unwrap());
|
||||
this
|
||||
};
|
||||
|
||||
let mut data = [0; 32];
|
||||
let mut prev_block_hash = [0; 32];
|
||||
|
||||
if is_genesis_random {
|
||||
OsRng.fill_bytes(&mut data);
|
||||
OsRng.fill_bytes(&mut prev_block_hash);
|
||||
}
|
||||
|
||||
let curr_time = chrono::Utc::now().timestamp_millis() as u64;
|
||||
|
||||
let hashable_data = HashableBlockData {
|
||||
block_id: genesis_id,
|
||||
transactions: vec![],
|
||||
prev_block_hash,
|
||||
timestamp: curr_time,
|
||||
};
|
||||
|
||||
let genesis_block = hashable_data.into_block(&signing_key);
|
||||
|
||||
//Sequencer should panic if unable to open db,
|
||||
//as fixing this issue may require actions non-native to program scope
|
||||
let block_store = SequecerBlockStore::open_db_with_genesis(
|
||||
&home_dir.join("rocksdb"),
|
||||
Some(genesis_block),
|
||||
signing_key,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Self { state, block_store }
|
||||
}
|
||||
}
|
||||
@ -19,6 +19,8 @@ actix-web.workspace = true
|
||||
tokio.workspace = true
|
||||
borsh.workspace = true
|
||||
|
||||
# TODO: Move to workspace
|
||||
|
||||
[dependencies.sequencer_core]
|
||||
path = "../sequencer_core"
|
||||
|
||||
@ -27,3 +29,6 @@ path = "../common"
|
||||
|
||||
[dependencies.nssa]
|
||||
path = "../nssa"
|
||||
|
||||
[dependencies.mempool]
|
||||
path = "../mempool"
|
||||
|
||||
@ -4,23 +4,23 @@ pub mod types;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::rpc_primitives::{
|
||||
RpcPollingConfig,
|
||||
errors::{RpcError, RpcErrorKind},
|
||||
use common::{
|
||||
rpc_primitives::errors::{RpcError, RpcErrorKind},
|
||||
transaction::EncodedTransaction,
|
||||
};
|
||||
use mempool::MemPoolHandle;
|
||||
pub use net_utils::*;
|
||||
use sequencer_core::SequencerCore;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
pub use net_utils::*;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use self::types::err_rpc::RpcErr;
|
||||
|
||||
//ToDo: Add necessary fields
|
||||
// ToDo: Add necessary fields
|
||||
pub struct JsonHandler {
|
||||
pub polling_config: RpcPollingConfig,
|
||||
pub sequencer_state: Arc<Mutex<SequencerCore>>,
|
||||
sequencer_state: Arc<Mutex<SequencerCore>>,
|
||||
mempool_handle: MemPoolHandle<EncodedTransaction>,
|
||||
}
|
||||
|
||||
fn respond<T: Serialize>(val: T) -> Result<Value, RpcErr> {
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::{io, sync::Arc};
|
||||
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{App, Error as HttpError, HttpResponse, HttpServer, http, middleware, web};
|
||||
use futures::Future;
|
||||
use futures::FutureExt;
|
||||
use common::{
|
||||
rpc_primitives::{RpcConfig, message::Message},
|
||||
transaction::EncodedTransaction,
|
||||
};
|
||||
use futures::{Future, FutureExt};
|
||||
use log::info;
|
||||
|
||||
use common::rpc_primitives::RpcConfig;
|
||||
use common::rpc_primitives::message::Message;
|
||||
use mempool::MemPoolHandle;
|
||||
use sequencer_core::SequencerCore;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -46,17 +46,17 @@ fn get_cors(cors_allowed_origins: &[String]) -> Cors {
|
||||
pub fn new_http_server(
|
||||
config: RpcConfig,
|
||||
seuquencer_core: Arc<Mutex<SequencerCore>>,
|
||||
mempool_handle: MemPoolHandle<EncodedTransaction>,
|
||||
) -> io::Result<actix_web::dev::Server> {
|
||||
let RpcConfig {
|
||||
addr,
|
||||
cors_allowed_origins,
|
||||
polling_config,
|
||||
limits_config,
|
||||
} = config;
|
||||
info!(target:NETWORK, "Starting http server at {addr}");
|
||||
let handler = web::Data::new(JsonHandler {
|
||||
polling_config,
|
||||
sequencer_state: seuquencer_core.clone(),
|
||||
mempool_handle,
|
||||
});
|
||||
|
||||
// HTTP server
|
||||
|
||||
@ -3,10 +3,6 @@ use std::collections::HashMap;
|
||||
use actix_web::Error as HttpError;
|
||||
use base58::FromBase58;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use nssa::{self, program::Program};
|
||||
use sequencer_core::config::AccountInitialData;
|
||||
use serde_json::Value;
|
||||
|
||||
use common::{
|
||||
HashType,
|
||||
block::HashableBlockData,
|
||||
@ -17,19 +13,20 @@ use common::{
|
||||
requests::{
|
||||
GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountRequest,
|
||||
GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse,
|
||||
GetInitialTestnetAccountsRequest, GetProgramIdsRequest, GetProgramIdsResponse,
|
||||
GetProofForCommitmentRequest, GetProofForCommitmentResponse,
|
||||
GetTransactionByHashRequest, GetTransactionByHashResponse,
|
||||
GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse,
|
||||
GetInitialTestnetAccountsRequest, GetLastBlockRequest, GetLastBlockResponse,
|
||||
GetProgramIdsRequest, GetProgramIdsResponse, GetProofForCommitmentRequest,
|
||||
GetProofForCommitmentResponse, GetTransactionByHashRequest,
|
||||
GetTransactionByHashResponse, HelloRequest, HelloResponse, SendTxRequest,
|
||||
SendTxResponse,
|
||||
},
|
||||
},
|
||||
transaction::EncodedTransaction,
|
||||
};
|
||||
|
||||
use common::rpc_primitives::requests::{
|
||||
GetBlockDataRequest, GetBlockDataResponse, GetGenesisIdRequest, GetGenesisIdResponse,
|
||||
GetLastBlockRequest, GetLastBlockResponse, HelloRequest, HelloResponse, SendTxRequest,
|
||||
SendTxResponse,
|
||||
transaction::{EncodedTransaction, NSSATransaction},
|
||||
};
|
||||
use log::warn;
|
||||
use nssa::{self, program::Program};
|
||||
use sequencer_core::{TransactionMalformationError, config::AccountInitialData};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::{JsonHandler, respond, types::err_rpc::RpcErr};
|
||||
|
||||
@ -67,16 +64,16 @@ impl JsonHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Example of request processing
|
||||
#[allow(clippy::unused_async)]
|
||||
///Example of request processing
|
||||
async fn process_temp_hello(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let _hello_request = HelloRequest::parse(Some(request.params))?;
|
||||
|
||||
let helperstruct = HelloResponse {
|
||||
let response = HelloResponse {
|
||||
greeting: HELLO_FROM_SEQUENCER.to_string(),
|
||||
};
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
async fn process_send_tx(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -84,18 +81,25 @@ impl JsonHandler {
|
||||
let tx = borsh::from_slice::<EncodedTransaction>(&send_tx_req.transaction).unwrap();
|
||||
let tx_hash = hex::encode(tx.hash());
|
||||
|
||||
{
|
||||
let mut state = self.sequencer_state.lock().await;
|
||||
let transaction = NSSATransaction::try_from(&tx)
|
||||
.map_err(|_| TransactionMalformationError::FailedToDecode { tx: tx.hash() })?;
|
||||
|
||||
state.push_tx_into_mempool_pre_check(tx)?;
|
||||
}
|
||||
let authenticated_tx = sequencer_core::transaction_pre_check(transaction)
|
||||
.inspect_err(|err| warn!("Error at pre_check {err:#?}"))?;
|
||||
|
||||
let helperstruct = SendTxResponse {
|
||||
// TODO: Do we need a timeout here? It will be usable if we have too many transactions to
|
||||
// process
|
||||
self.mempool_handle
|
||||
.push(authenticated_tx.into())
|
||||
.await
|
||||
.expect("Mempool is closed, this is a bug");
|
||||
|
||||
let response = SendTxResponse {
|
||||
status: TRANSACTION_SUBMITTED.to_string(),
|
||||
tx_hash,
|
||||
};
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
async fn process_get_block_data(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -104,14 +108,16 @@ impl JsonHandler {
|
||||
let block = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.block_store.get_block_at_id(get_block_req.block_id)?
|
||||
state
|
||||
.block_store()
|
||||
.get_block_at_id(get_block_req.block_id)?
|
||||
};
|
||||
|
||||
let helperstruct = GetBlockDataResponse {
|
||||
let response = GetBlockDataResponse {
|
||||
block: borsh::to_vec(&HashableBlockData::from(block)).unwrap(),
|
||||
};
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
async fn process_get_genesis(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -120,12 +126,12 @@ impl JsonHandler {
|
||||
let genesis_id = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.block_store.genesis_id
|
||||
state.block_store().genesis_id()
|
||||
};
|
||||
|
||||
let helperstruct = GetGenesisIdResponse { genesis_id };
|
||||
let response = GetGenesisIdResponse { genesis_id };
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
async fn process_get_last_block(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -134,12 +140,12 @@ impl JsonHandler {
|
||||
let last_block = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.chain_height
|
||||
state.chain_height()
|
||||
};
|
||||
|
||||
let helperstruct = GetLastBlockResponse { last_block };
|
||||
let response = GetLastBlockResponse { last_block };
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
/// Returns the initial accounts for testnet
|
||||
@ -151,83 +157,83 @@ impl JsonHandler {
|
||||
let initial_accounts: Vec<AccountInitialData> = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.sequencer_config.initial_accounts.clone()
|
||||
state.sequencer_config().initial_accounts.clone()
|
||||
};
|
||||
|
||||
respond(initial_accounts)
|
||||
}
|
||||
|
||||
/// Returns the balance of the account at the given address.
|
||||
/// The address must be a valid hex string of the correct length.
|
||||
/// Returns the balance of the account at the given account_id.
|
||||
/// The account_id must be a valid hex string of the correct length.
|
||||
async fn process_get_account_balance(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_account_req = GetAccountBalanceRequest::parse(Some(request.params))?;
|
||||
let address_bytes = get_account_req
|
||||
.address
|
||||
let account_id_bytes = get_account_req
|
||||
.account_id
|
||||
.from_base58()
|
||||
.map_err(|_| RpcError::invalid_params("invalid base58".to_string()))?;
|
||||
let address = nssa::Address::new(
|
||||
address_bytes
|
||||
let account_id = nssa::AccountId::new(
|
||||
account_id_bytes
|
||||
.try_into()
|
||||
.map_err(|_| RpcError::invalid_params("invalid length".to_string()))?,
|
||||
);
|
||||
|
||||
let balance = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
let account = state.state.get_account_by_address(&address);
|
||||
let account = state.state().get_account_by_id(&account_id);
|
||||
account.balance
|
||||
};
|
||||
|
||||
let helperstruct = GetAccountBalanceResponse { balance };
|
||||
let response = GetAccountBalanceResponse { balance };
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
/// Returns the nonces of the accounts at the given addresses.
|
||||
/// Each address must be a valid hex string of the correct length.
|
||||
/// Returns the nonces of the accounts at the given account_ids.
|
||||
/// Each account_id must be a valid hex string of the correct length.
|
||||
async fn process_get_accounts_nonces(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_account_nonces_req = GetAccountsNoncesRequest::parse(Some(request.params))?;
|
||||
let mut addresses = vec![];
|
||||
for address_raw in get_account_nonces_req.addresses {
|
||||
let address = address_raw
|
||||
.parse::<nssa::Address>()
|
||||
let mut account_ids = vec![];
|
||||
for account_id_raw in get_account_nonces_req.account_ids {
|
||||
let account_id = account_id_raw
|
||||
.parse::<nssa::AccountId>()
|
||||
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
|
||||
|
||||
addresses.push(address);
|
||||
account_ids.push(account_id);
|
||||
}
|
||||
|
||||
let nonces = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
addresses
|
||||
account_ids
|
||||
.into_iter()
|
||||
.map(|addr| state.state.get_account_by_address(&addr).nonce)
|
||||
.map(|account_id| state.state().get_account_by_id(&account_id).nonce)
|
||||
.collect()
|
||||
};
|
||||
|
||||
let helperstruct = GetAccountsNoncesResponse { nonces };
|
||||
let response = GetAccountsNoncesResponse { nonces };
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
/// Returns account struct for given address.
|
||||
/// Address must be a valid hex string of the correct length.
|
||||
/// Returns account struct for given account_id.
|
||||
/// AccountId must be a valid hex string of the correct length.
|
||||
async fn process_get_account(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
let get_account_nonces_req = GetAccountRequest::parse(Some(request.params))?;
|
||||
|
||||
let address = get_account_nonces_req
|
||||
.address
|
||||
.parse::<nssa::Address>()
|
||||
let account_id = get_account_nonces_req
|
||||
.account_id
|
||||
.parse::<nssa::AccountId>()
|
||||
.map_err(|e| RpcError::invalid_params(e.to_string()))?;
|
||||
|
||||
let account = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
|
||||
state.state.get_account_by_address(&address)
|
||||
state.state().get_account_by_id(&account_id)
|
||||
};
|
||||
|
||||
let helperstruct = GetAccountResponse { account };
|
||||
let response = GetAccountResponse { account };
|
||||
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
/// Returns the transaction corresponding to the given hash, if it exists in the blockchain.
|
||||
@ -243,15 +249,15 @@ impl JsonHandler {
|
||||
let transaction = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
state
|
||||
.block_store
|
||||
.block_store()
|
||||
.get_transaction_by_hash(hash)
|
||||
.map(|tx| borsh::to_vec(&tx).unwrap())
|
||||
};
|
||||
let base64_encoded = transaction.map(|tx| general_purpose::STANDARD.encode(tx));
|
||||
let helperstruct = GetTransactionByHashResponse {
|
||||
let response = GetTransactionByHashResponse {
|
||||
transaction: base64_encoded,
|
||||
};
|
||||
respond(helperstruct)
|
||||
respond(response)
|
||||
}
|
||||
|
||||
/// Returns the commitment proof, corresponding to commitment
|
||||
@ -261,11 +267,11 @@ impl JsonHandler {
|
||||
let membership_proof = {
|
||||
let state = self.sequencer_state.lock().await;
|
||||
state
|
||||
.state
|
||||
.state()
|
||||
.get_proof_for_commitment(&get_proof_req.commitment)
|
||||
};
|
||||
let helperstruct = GetProofForCommitmentResponse { membership_proof };
|
||||
respond(helperstruct)
|
||||
let response = GetProofForCommitmentResponse { membership_proof };
|
||||
respond(response)
|
||||
}
|
||||
|
||||
async fn process_get_program_ids(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -282,8 +288,8 @@ impl JsonHandler {
|
||||
"privacy_preserving_circuit".to_string(),
|
||||
nssa::PRIVACY_PRESERVING_CIRCUIT_ID,
|
||||
);
|
||||
let helperstruct = GetProgramIdsResponse { program_ids };
|
||||
respond(helperstruct)
|
||||
let response = GetProgramIdsResponse { program_ids };
|
||||
respond(response)
|
||||
}
|
||||
|
||||
pub async fn process_request_internal(&self, request: Request) -> Result<Value, RpcErr> {
|
||||
@ -309,14 +315,9 @@ impl JsonHandler {
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{JsonHandler, rpc_handler};
|
||||
use base58::ToBase58;
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use common::{
|
||||
rpc_primitives::RpcPollingConfig, test_utils::sequencer_sign_key_for_testing,
|
||||
transaction::EncodedTransaction,
|
||||
};
|
||||
|
||||
use common::{test_utils::sequencer_sign_key_for_testing, transaction::EncodedTransaction};
|
||||
use sequencer_core::{
|
||||
SequencerCore,
|
||||
config::{AccountInitialData, SequencerConfig},
|
||||
@ -325,26 +326,28 @@ mod tests {
|
||||
use tempfile::tempdir;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{JsonHandler, rpc_handler};
|
||||
|
||||
fn sequencer_config_for_tests() -> SequencerConfig {
|
||||
let tempdir = tempdir().unwrap();
|
||||
let home = tempdir.path().to_path_buf();
|
||||
let acc1_addr: Vec<u8> = vec![
|
||||
let acc1_id: Vec<u8> = vec![
|
||||
208, 122, 210, 232, 75, 39, 250, 0, 194, 98, 240, 161, 238, 160, 255, 53, 202, 9, 115,
|
||||
84, 126, 106, 16, 111, 114, 241, 147, 194, 220, 131, 139, 68,
|
||||
];
|
||||
|
||||
let acc2_addr: Vec<u8> = vec![
|
||||
let acc2_id: Vec<u8> = vec![
|
||||
231, 174, 119, 197, 239, 26, 5, 153, 147, 68, 175, 73, 159, 199, 138, 23, 5, 57, 141,
|
||||
98, 237, 6, 207, 46, 20, 121, 246, 222, 248, 154, 57, 188,
|
||||
];
|
||||
|
||||
let initial_acc1 = AccountInitialData {
|
||||
addr: acc1_addr.to_base58(),
|
||||
account_id: acc1_id.to_base58(),
|
||||
balance: 10000,
|
||||
};
|
||||
|
||||
let initial_acc2 = AccountInitialData {
|
||||
addr: acc2_addr.to_base58(),
|
||||
account_id: acc2_id.to_base58(),
|
||||
balance: 20000,
|
||||
};
|
||||
|
||||
@ -365,10 +368,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn components_for_tests() -> (JsonHandler, Vec<AccountInitialData>, EncodedTransaction) {
|
||||
async fn components_for_tests() -> (JsonHandler, Vec<AccountInitialData>, EncodedTransaction) {
|
||||
let config = sequencer_config_for_tests();
|
||||
let mut sequencer_core = SequencerCore::start_from_config(config);
|
||||
let initial_accounts = sequencer_core.sequencer_config.initial_accounts.clone();
|
||||
let (mut sequencer_core, mempool_handle) = SequencerCore::start_from_config(config);
|
||||
let initial_accounts = sequencer_core.sequencer_config().initial_accounts.clone();
|
||||
|
||||
let signing_key = nssa::PrivateKey::try_new([1; 32]).unwrap();
|
||||
let balance_to_move = 10;
|
||||
@ -383,9 +386,10 @@ mod tests {
|
||||
signing_key,
|
||||
);
|
||||
|
||||
sequencer_core
|
||||
.push_tx_into_mempool_pre_check(tx.clone())
|
||||
.unwrap();
|
||||
mempool_handle
|
||||
.push(tx.clone())
|
||||
.await
|
||||
.expect("Mempool is closed, this is a bug");
|
||||
|
||||
sequencer_core
|
||||
.produce_new_block_with_mempool_transactions()
|
||||
@ -395,8 +399,8 @@ mod tests {
|
||||
|
||||
(
|
||||
JsonHandler {
|
||||
polling_config: RpcPollingConfig::default(),
|
||||
sequencer_state: sequencer_core,
|
||||
mempool_handle,
|
||||
},
|
||||
initial_accounts,
|
||||
tx,
|
||||
@ -426,11 +430,11 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_balance_for_non_existent_account() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account_balance",
|
||||
"params": { "address": "11".repeat(16) },
|
||||
"params": { "account_id": "11".repeat(16) },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -448,11 +452,11 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_balance_for_invalid_base58() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account_balance",
|
||||
"params": { "address": "not_a_valid_base58" },
|
||||
"params": { "account_id": "not_a_valid_base58" },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -471,11 +475,11 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_balance_for_invalid_length() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account_balance",
|
||||
"params": { "address": "cafecafe" },
|
||||
"params": { "account_id": "cafecafe" },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -494,14 +498,14 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_balance_for_existing_account() {
|
||||
let (json_handler, initial_accounts, _) = components_for_tests();
|
||||
let (json_handler, initial_accounts, _) = components_for_tests().await;
|
||||
|
||||
let acc1_addr = initial_accounts[0].addr.clone();
|
||||
let acc1_id = initial_accounts[0].account_id.clone();
|
||||
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account_balance",
|
||||
"params": { "address": acc1_addr },
|
||||
"params": { "account_id": acc1_id },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -519,11 +523,11 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_accounts_nonces_for_non_existent_account() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_accounts_nonces",
|
||||
"params": { "addresses": ["11".repeat(16)] },
|
||||
"params": { "account_ids": ["11".repeat(16)] },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -541,15 +545,15 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_accounts_nonces_for_existent_account() {
|
||||
let (json_handler, initial_accounts, _) = components_for_tests();
|
||||
let (json_handler, initial_accounts, _) = components_for_tests().await;
|
||||
|
||||
let acc_1_addr = initial_accounts[0].addr.clone();
|
||||
let acc_2_addr = initial_accounts[1].addr.clone();
|
||||
let acc1_id = initial_accounts[0].account_id.clone();
|
||||
let acc2_id = initial_accounts[1].account_id.clone();
|
||||
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_accounts_nonces",
|
||||
"params": { "addresses": [acc_1_addr, acc_2_addr] },
|
||||
"params": { "account_ids": [acc1_id, acc2_id] },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -567,11 +571,11 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_account_data_for_non_existent_account() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_account",
|
||||
"params": { "address": "11".repeat(16) },
|
||||
"params": { "account_id": "11".repeat(16) },
|
||||
"id": 1
|
||||
});
|
||||
let expected_response = serde_json::json!({
|
||||
@ -594,7 +598,7 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_transaction_by_hash_for_non_existent_hash() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_transaction_by_hash",
|
||||
@ -616,7 +620,7 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_transaction_by_hash_for_invalid_hex() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_transaction_by_hash",
|
||||
@ -640,7 +644,7 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_transaction_by_hash_for_invalid_length() {
|
||||
let (json_handler, _, _) = components_for_tests();
|
||||
let (json_handler, _, _) = components_for_tests().await;
|
||||
let request = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "get_transaction_by_hash",
|
||||
@ -664,7 +668,7 @@ mod tests {
|
||||
|
||||
#[actix_web::test]
|
||||
async fn test_get_transaction_by_hash_for_existing_transaction() {
|
||||
let (json_handler, _, tx) = components_for_tests();
|
||||
let (json_handler, _, tx) = components_for_tests().await;
|
||||
let tx_hash_hex = hex::encode(tx.hash());
|
||||
let expected_base64_encoded = general_purpose::STANDARD.encode(borsh::to_vec(&tx).unwrap());
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use log::debug;
|
||||
|
||||
use common::rpc_primitives::errors::{RpcError, RpcParseError};
|
||||
use log::debug;
|
||||
use sequencer_core::TransactionMalformationError;
|
||||
|
||||
pub struct RpcErr(pub RpcError);
|
||||
|
||||
@ -9,11 +9,11 @@
|
||||
"port": 3040,
|
||||
"initial_accounts": [
|
||||
{
|
||||
"addr": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"balance": 10000
|
||||
},
|
||||
{
|
||||
"addr": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"balance": 20000
|
||||
}
|
||||
],
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{fs::File, io::BufReader, path::PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use sequencer_core::config::SequencerConfig;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
||||
pub fn from_file(config_home: PathBuf) -> Result<SequencerConfig> {
|
||||
let file = File::open(config_home)?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
@ -26,13 +26,17 @@ pub async fn startup_sequencer(
|
||||
let block_timeout = app_config.block_create_timeout_millis;
|
||||
let port = app_config.port;
|
||||
|
||||
let sequencer_core = SequencerCore::start_from_config(app_config);
|
||||
let (sequencer_core, mempool_handle) = SequencerCore::start_from_config(app_config);
|
||||
|
||||
info!("Sequencer core set up");
|
||||
|
||||
let seq_core_wrapped = Arc::new(Mutex::new(sequencer_core));
|
||||
|
||||
let http_server = new_http_server(RpcConfig::with_port(port), seq_core_wrapped.clone())?;
|
||||
let http_server = new_http_server(
|
||||
RpcConfig::with_port(port),
|
||||
Arc::clone(&seq_core_wrapped),
|
||||
mempool_handle,
|
||||
)?;
|
||||
info!("HTTP server started");
|
||||
let http_server_handle = http_server.handle();
|
||||
tokio::spawn(http_server);
|
||||
@ -76,7 +80,7 @@ pub async fn main_runner() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
//ToDo: Add restart on failures
|
||||
// ToDo: Add restart on failures
|
||||
let (_, main_loop_handle) = startup_sequencer(app_config).await?;
|
||||
|
||||
main_loop_handle.await??;
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use sequencer_runner::main_runner;
|
||||
|
||||
pub const NUM_THREADS: usize = 4;
|
||||
|
||||
// TODO: Why it requires config as a directory and not as a file?
|
||||
fn main() -> Result<()> {
|
||||
actix::System::with_tokio_rt(|| {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
|
||||
@ -8,33 +8,33 @@ use rocksdb::{
|
||||
|
||||
pub mod error;
|
||||
|
||||
///Maximal size of stored blocks in base
|
||||
/// Maximal size of stored blocks in base
|
||||
///
|
||||
///Used to control db size
|
||||
/// Used to control db size
|
||||
///
|
||||
///Currently effectively unbounded.
|
||||
/// Currently effectively unbounded.
|
||||
pub const BUFF_SIZE_ROCKSDB: usize = usize::MAX;
|
||||
|
||||
///Size of stored blocks cache in memory
|
||||
/// Size of stored blocks cache in memory
|
||||
///
|
||||
///Keeping small to not run out of memory
|
||||
/// Keeping small to not run out of memory
|
||||
pub const CACHE_SIZE: usize = 1000;
|
||||
|
||||
///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";
|
||||
///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
|
||||
pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db";
|
||||
///Key base for storing metainformation which describe if first block has been set
|
||||
/// Key base for storing metainformation which describe if first block has been set
|
||||
pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set";
|
||||
|
||||
///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";
|
||||
|
||||
///Name of block column family
|
||||
/// Name of block column family
|
||||
pub const CF_BLOCK_NAME: &str = "cf_block";
|
||||
///Name of meta column family
|
||||
/// Name of meta column family
|
||||
pub const CF_META_NAME: &str = "cf_meta";
|
||||
///Name of snapshot column family
|
||||
/// Name of snapshot column family
|
||||
pub const CF_SNAPSHOT_NAME: &str = "cf_snapshot";
|
||||
|
||||
pub type DbResult<T> = Result<T, DbError>;
|
||||
@ -47,7 +47,7 @@ impl RocksDBIO {
|
||||
pub fn open_or_create(path: &Path, start_block: Option<Block>) -> DbResult<Self> {
|
||||
let mut cf_opts = Options::default();
|
||||
cf_opts.set_max_write_buffer_number(16);
|
||||
//ToDo: Add more column families for different data
|
||||
// ToDo: Add more column families for different data
|
||||
let cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone());
|
||||
let cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone());
|
||||
let cfsnapshot = ColumnFamilyDescriptor::new(CF_SNAPSHOT_NAME, cf_opts.clone());
|
||||
@ -62,7 +62,7 @@ impl RocksDBIO {
|
||||
);
|
||||
|
||||
let dbio = Self {
|
||||
//There is no point in handling this from runner code
|
||||
// There is no point in handling this from runner code
|
||||
db: db.unwrap(),
|
||||
};
|
||||
|
||||
@ -86,7 +86,7 @@ impl RocksDBIO {
|
||||
pub fn destroy(path: &Path) -> DbResult<()> {
|
||||
let mut cf_opts = Options::default();
|
||||
cf_opts.set_max_write_buffer_number(16);
|
||||
//ToDo: Add more column families for different data
|
||||
// ToDo: Add more column families for different data
|
||||
let _cfb = ColumnFamilyDescriptor::new(CF_BLOCK_NAME, cf_opts.clone());
|
||||
let _cfmeta = ColumnFamilyDescriptor::new(CF_META_NAME, cf_opts.clone());
|
||||
let _cfsnapshot = ColumnFamilyDescriptor::new(CF_SNAPSHOT_NAME, cf_opts.clone());
|
||||
|
||||
@ -19,6 +19,7 @@ borsh.workspace = true
|
||||
base58.workspace = true
|
||||
hex = "0.4.3"
|
||||
rand.workspace = true
|
||||
itertools = "0.14.0"
|
||||
|
||||
[dependencies.key_protocol]
|
||||
path = "../key_protocol"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, hash_map::Entry};
|
||||
|
||||
use anyhow::Result;
|
||||
use key_protocol::{
|
||||
@ -36,7 +36,7 @@ impl WalletChainStore {
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.unwrap();
|
||||
.expect("Malformed persistent account data, must have public root");
|
||||
|
||||
let private_root = persistent_accounts
|
||||
.iter()
|
||||
@ -45,7 +45,7 @@ impl WalletChainStore {
|
||||
_ => false,
|
||||
})
|
||||
.cloned()
|
||||
.unwrap();
|
||||
.expect("Malformed persistent account data, must have private root");
|
||||
|
||||
let mut public_tree = KeyTreePublic::new_from_root(match public_root {
|
||||
PersistentAccountData::Public(data) => data.data,
|
||||
@ -59,18 +59,18 @@ impl WalletChainStore {
|
||||
for pers_acc_data in persistent_accounts {
|
||||
match pers_acc_data {
|
||||
PersistentAccountData::Public(data) => {
|
||||
public_tree.insert(data.address, data.chain_index, data.data);
|
||||
public_tree.insert(data.account_id, data.chain_index, data.data);
|
||||
}
|
||||
PersistentAccountData::Private(data) => {
|
||||
private_tree.insert(data.address, data.chain_index, data.data);
|
||||
private_tree.insert(data.account_id, data.chain_index, data.data);
|
||||
}
|
||||
PersistentAccountData::Preconfigured(acc_data) => match acc_data {
|
||||
InitialAccountData::Public(data) => {
|
||||
public_init_acc_map.insert(data.address.parse()?, data.pub_sign_key);
|
||||
public_init_acc_map.insert(data.account_id.parse()?, data.pub_sign_key);
|
||||
}
|
||||
InitialAccountData::Private(data) => {
|
||||
private_init_acc_map
|
||||
.insert(data.address.parse()?, (data.key_chain, data.account));
|
||||
.insert(data.account_id.parse()?, (data.key_chain, data.account));
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -94,7 +94,7 @@ impl WalletChainStore {
|
||||
for init_acc_data in config.initial_accounts.clone() {
|
||||
match init_acc_data {
|
||||
InitialAccountData::Public(data) => {
|
||||
public_init_acc_map.insert(data.address.parse()?, data.pub_sign_key);
|
||||
public_init_acc_map.insert(data.account_id.parse()?, data.pub_sign_key);
|
||||
}
|
||||
InitialAccountData::Private(data) => {
|
||||
let mut account = data.account;
|
||||
@ -102,7 +102,8 @@ impl WalletChainStore {
|
||||
// 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));
|
||||
private_init_acc_map
|
||||
.insert(data.account_id.parse()?, (data.key_chain, account));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,25 +124,22 @@ impl WalletChainStore {
|
||||
|
||||
pub fn insert_private_account_data(
|
||||
&mut self,
|
||||
addr: nssa::Address,
|
||||
account_id: nssa::AccountId,
|
||||
account: nssa_core::account::Account,
|
||||
) {
|
||||
println!("inserting at address {}, this account {:?}", addr, account);
|
||||
println!("inserting at address {account_id}, this account {account:?}");
|
||||
|
||||
if self
|
||||
let entry = self
|
||||
.user_data
|
||||
.default_user_private_accounts
|
||||
.contains_key(&addr)
|
||||
{
|
||||
self.user_data
|
||||
.default_user_private_accounts
|
||||
.entry(addr)
|
||||
.and_modify(|data| data.1 = account);
|
||||
} else {
|
||||
.entry(account_id)
|
||||
.and_modify(|data| data.1 = account.clone());
|
||||
|
||||
if matches!(entry, Entry::Vacant(_)) {
|
||||
self.user_data
|
||||
.private_key_tree
|
||||
.addr_map
|
||||
.get(&addr)
|
||||
.account_id_map
|
||||
.get(&account_id)
|
||||
.map(|chain_index| {
|
||||
self.user_data
|
||||
.private_key_tree
|
||||
@ -159,17 +157,16 @@ mod tests {
|
||||
keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic, traits::KeyNode,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::config::{
|
||||
InitialAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn create_initial_accounts() -> Vec<InitialAccountData> {
|
||||
let initial_acc1 = serde_json::from_str(
|
||||
r#"{
|
||||
"Public": {
|
||||
"address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"pub_sign_key": [
|
||||
16,
|
||||
162,
|
||||
@ -212,7 +209,7 @@ mod tests {
|
||||
let initial_acc2 = serde_json::from_str(
|
||||
r#"{
|
||||
"Public": {
|
||||
"address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"pub_sign_key": [
|
||||
113,
|
||||
121,
|
||||
@ -270,27 +267,21 @@ mod tests {
|
||||
}
|
||||
|
||||
fn create_sample_persistent_accounts() -> Vec<PersistentAccountData> {
|
||||
let mut accs = vec![];
|
||||
|
||||
let public_data = ChildKeysPublic::root([42; 64]);
|
||||
|
||||
accs.push(PersistentAccountData::Public(PersistentAccountDataPublic {
|
||||
address: public_data.address(),
|
||||
chain_index: ChainIndex::root(),
|
||||
data: public_data,
|
||||
}));
|
||||
|
||||
let private_data = ChildKeysPrivate::root([47; 64]);
|
||||
|
||||
accs.push(PersistentAccountData::Private(
|
||||
PersistentAccountDataPrivate {
|
||||
address: private_data.address(),
|
||||
vec![
|
||||
PersistentAccountData::Public(PersistentAccountDataPublic {
|
||||
account_id: public_data.account_id(),
|
||||
chain_index: ChainIndex::root(),
|
||||
data: public_data,
|
||||
}),
|
||||
PersistentAccountData::Private(PersistentAccountDataPrivate {
|
||||
account_id: private_data.account_id(),
|
||||
chain_index: ChainIndex::root(),
|
||||
data: private_data,
|
||||
},
|
||||
));
|
||||
|
||||
accs
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
use anyhow::Result;
|
||||
use base58::ToBase58;
|
||||
use clap::Subcommand;
|
||||
use itertools::Itertools as _;
|
||||
use key_protocol::key_management::key_tree::chain_index::ChainIndex;
|
||||
use nssa::{Account, Address, program::Program};
|
||||
use nssa::{Account, AccountId, program::Program};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
SubcommandReturnValue, WalletCore,
|
||||
cli::WalletSubcommand,
|
||||
helperfunctions::{AddressPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix},
|
||||
helperfunctions::{AccountPrivacyKind, HumanReadableAccount, parse_addr_with_privacy_prefix},
|
||||
parse_block_range,
|
||||
};
|
||||
|
||||
@ -28,7 +29,7 @@ struct TokenDefinition {
|
||||
struct TokenHolding {
|
||||
#[allow(unused)]
|
||||
account_type: u8,
|
||||
definition_id: Address,
|
||||
definition_id: AccountId,
|
||||
balance: u128,
|
||||
}
|
||||
|
||||
@ -56,7 +57,7 @@ impl TokenHolding {
|
||||
None
|
||||
} else {
|
||||
let account_type = data[0];
|
||||
let definition_id = Address::new(data[1..33].try_into().unwrap());
|
||||
let definition_id = AccountId::new(data[1..33].try_into().unwrap());
|
||||
let balance = u128::from_le_bytes(data[33..].try_into().unwrap());
|
||||
Some(Self {
|
||||
definition_id,
|
||||
@ -67,36 +68,41 @@ impl TokenHolding {
|
||||
}
|
||||
}
|
||||
|
||||
///Represents generic chain CLI subcommand
|
||||
/// Represents generic chain CLI subcommand
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum AccountSubcommand {
|
||||
///Get account data
|
||||
/// Get account data
|
||||
Get {
|
||||
///Flag to get raw account data
|
||||
/// Flag to get raw account data
|
||||
#[arg(short, long)]
|
||||
raw: bool,
|
||||
///Valid 32 byte base58 string with privacy prefix
|
||||
/// Valid 32 byte base58 string with privacy prefix
|
||||
#[arg(short, long)]
|
||||
addr: String,
|
||||
account_id: String,
|
||||
},
|
||||
///Produce new public or private account
|
||||
/// Produce new public or private account
|
||||
#[command(subcommand)]
|
||||
New(NewSubcommand),
|
||||
///Sync private accounts
|
||||
/// Sync private accounts
|
||||
SyncPrivate {},
|
||||
/// List all accounts owned by the wallet
|
||||
#[command(visible_alias = "ls")]
|
||||
List {},
|
||||
}
|
||||
|
||||
///Represents generic register CLI subcommand
|
||||
/// Represents generic register CLI subcommand
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum NewSubcommand {
|
||||
///Register new public account
|
||||
/// Register new public account
|
||||
Public {
|
||||
#[arg(long)]
|
||||
/// Chain index of a parent node
|
||||
cci: ChainIndex,
|
||||
},
|
||||
///Register new private account
|
||||
/// Register new private account
|
||||
Private {
|
||||
#[arg(long)]
|
||||
/// Chain index of a parent node
|
||||
cci: ChainIndex,
|
||||
},
|
||||
}
|
||||
@ -108,28 +114,28 @@ impl WalletSubcommand for NewSubcommand {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
NewSubcommand::Public { cci } => {
|
||||
let addr = wallet_core.create_new_account_public(cci);
|
||||
let account_id = wallet_core.create_new_account_public(cci);
|
||||
|
||||
println!("Generated new account with addr Public/{addr}");
|
||||
println!("Generated new account with account_id Public/{account_id}");
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::RegisterAccount { addr })
|
||||
Ok(SubcommandReturnValue::RegisterAccount { account_id })
|
||||
}
|
||||
NewSubcommand::Private { cci } => {
|
||||
let addr = wallet_core.create_new_account_private(cci);
|
||||
let account_id = wallet_core.create_new_account_private(cci);
|
||||
|
||||
let (key, _) = wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.get_private_account(&addr)
|
||||
.get_private_account(&account_id)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Generated new account with addr Private/{}",
|
||||
addr.to_bytes().to_base58()
|
||||
"Generated new account with account_id Private/{}",
|
||||
account_id.to_bytes().to_base58()
|
||||
);
|
||||
println!("With npk {}", hex::encode(key.nullifer_public_key.0));
|
||||
println!(
|
||||
@ -141,7 +147,7 @@ impl WalletSubcommand for NewSubcommand {
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
|
||||
Ok(SubcommandReturnValue::RegisterAccount { addr })
|
||||
Ok(SubcommandReturnValue::RegisterAccount { account_id })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,15 +206,17 @@ impl WalletSubcommand for AccountSubcommand {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
AccountSubcommand::Get { raw, addr } => {
|
||||
let (addr, addr_kind) = parse_addr_with_privacy_prefix(&addr)?;
|
||||
AccountSubcommand::Get { raw, account_id } => {
|
||||
let (account_id, addr_kind) = parse_addr_with_privacy_prefix(&account_id)?;
|
||||
|
||||
let addr = addr.parse()?;
|
||||
let account_id = account_id.parse()?;
|
||||
|
||||
let account = match addr_kind {
|
||||
AddressPrivacyKind::Public => wallet_core.get_account_public(addr).await?,
|
||||
AddressPrivacyKind::Private => wallet_core
|
||||
.get_account_private(&addr)
|
||||
AccountPrivacyKind::Public => {
|
||||
wallet_core.get_account_public(account_id).await?
|
||||
}
|
||||
AccountPrivacyKind::Private => wallet_core
|
||||
.get_account_private(&account_id)
|
||||
.ok_or(anyhow::anyhow!("Private account not found in storage"))?,
|
||||
};
|
||||
|
||||
@ -250,7 +258,9 @@ impl WalletSubcommand for AccountSubcommand {
|
||||
|
||||
serde_json::to_string(&acc_view)?
|
||||
} else {
|
||||
anyhow::bail!("Invalid data for account {addr:#?} with token program");
|
||||
anyhow::bail!(
|
||||
"Invalid data for account {account_id:#?} with token program"
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
@ -274,13 +284,19 @@ impl WalletSubcommand for AccountSubcommand {
|
||||
.await?
|
||||
.last_block;
|
||||
|
||||
if !wallet_core
|
||||
if wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.addr_map
|
||||
.account_id_map
|
||||
.is_empty()
|
||||
{
|
||||
wallet_core.last_synced_block = curr_last_block;
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent data at {path:#?}");
|
||||
} else {
|
||||
parse_block_range(
|
||||
last_synced_block + 1,
|
||||
curr_last_block,
|
||||
@ -288,16 +304,41 @@ impl WalletSubcommand for AccountSubcommand {
|
||||
wallet_core,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
wallet_core.last_synced_block = curr_last_block;
|
||||
|
||||
let path = wallet_core.store_persistent_data().await?;
|
||||
|
||||
println!("Stored persistent data at {path:#?}");
|
||||
}
|
||||
|
||||
Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block))
|
||||
}
|
||||
AccountSubcommand::List {} => {
|
||||
let user_data = &wallet_core.storage.user_data;
|
||||
let accounts = user_data
|
||||
.default_pub_account_signing_keys
|
||||
.keys()
|
||||
.map(|id| format!("Preconfigured Public/{id}"))
|
||||
.chain(
|
||||
user_data
|
||||
.default_user_private_accounts
|
||||
.keys()
|
||||
.map(|id| format!("Preconfigured Private/{id}")),
|
||||
)
|
||||
.chain(
|
||||
user_data
|
||||
.public_key_tree
|
||||
.account_id_map
|
||||
.iter()
|
||||
.map(|(id, chain_index)| format!("{chain_index} Public/{id}")),
|
||||
)
|
||||
.chain(
|
||||
user_data
|
||||
.private_key_tree
|
||||
.account_id_map
|
||||
.iter()
|
||||
.map(|(id, chain_index)| format!("{chain_index} Private/{id}")),
|
||||
)
|
||||
.format(",\n");
|
||||
|
||||
println!("{accounts}");
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,19 +3,19 @@ use clap::Subcommand;
|
||||
|
||||
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
||||
|
||||
///Represents generic chain CLI subcommand
|
||||
/// Represents generic chain CLI subcommand
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum ChainSubcommand {
|
||||
///Get current block id from sequencer
|
||||
/// Get current block id from sequencer
|
||||
CurrentBlockId {},
|
||||
///Get block at id from sequencer
|
||||
/// Get block at id from sequencer
|
||||
Block {
|
||||
#[arg(short, long)]
|
||||
id: u64,
|
||||
},
|
||||
///Get transaction at hash from sequencer
|
||||
/// Get transaction at hash from sequencer
|
||||
Transaction {
|
||||
///hash - valid 32 byte hex string
|
||||
/// hash - valid 32 byte hex string
|
||||
#[arg(short, long)]
|
||||
hash: String,
|
||||
},
|
||||
|
||||
@ -3,7 +3,7 @@ use clap::Subcommand;
|
||||
|
||||
use crate::{SubcommandReturnValue, WalletCore, cli::WalletSubcommand};
|
||||
|
||||
///Represents generic config CLI subcommand
|
||||
/// Represents generic config CLI subcommand
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum ConfigSubcommand {
|
||||
/// Command to explicitly setup config and storage
|
||||
@ -14,6 +14,8 @@ pub enum ConfigSubcommand {
|
||||
Get { key: String },
|
||||
/// Setter of config fields
|
||||
Set { key: String, value: String },
|
||||
/// Prints description of corresponding field
|
||||
Description { key: String },
|
||||
}
|
||||
|
||||
impl WalletSubcommand for ConfigSubcommand {
|
||||
@ -108,6 +110,40 @@ impl WalletSubcommand for ConfigSubcommand {
|
||||
|
||||
println!("Stored changed config at {path:#?}");
|
||||
}
|
||||
ConfigSubcommand::Description { key } => match key.as_str() {
|
||||
"override_rust_log" => {
|
||||
println!("Value of variable RUST_LOG to override, affects logging");
|
||||
}
|
||||
"sequencer_addr" => {
|
||||
println!("HTTP V4 account_id of sequencer");
|
||||
}
|
||||
"seq_poll_timeout_millis" => {
|
||||
println!(
|
||||
"Sequencer client retry variable: how much time to wait between retries in milliseconds(can be zero)"
|
||||
);
|
||||
}
|
||||
"seq_poll_max_blocks" => {
|
||||
println!(
|
||||
"Sequencer client polling variable: max number of blocks to poll in parallel"
|
||||
);
|
||||
}
|
||||
"seq_poll_max_retries" => {
|
||||
println!(
|
||||
"Sequencer client retry variable: MAX number of retries before failing(can be zero)"
|
||||
);
|
||||
}
|
||||
"seq_poll_retry_delay_millis" => {
|
||||
println!(
|
||||
"Sequencer client polling variable: how much time to wait in milliseconds between polling retries(can be zero)"
|
||||
);
|
||||
}
|
||||
"initial_accounts" => {
|
||||
println!("List of initial accounts' keys(both public and private)");
|
||||
}
|
||||
_ => {
|
||||
println!("Unknown field");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
|
||||
@ -1,42 +1,43 @@
|
||||
use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
|
||||
use crate::{
|
||||
SubcommandReturnValue, WalletCore,
|
||||
cli::WalletSubcommand,
|
||||
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
};
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||
/// Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum AuthTransferSubcommand {
|
||||
///Initialize account under authenticated transfer program
|
||||
/// Initialize account under authenticated transfer program
|
||||
Init {
|
||||
///addr - valid 32 byte base58 string with privacy prefix
|
||||
/// account_id - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
addr: String,
|
||||
account_id: String,
|
||||
},
|
||||
///Send native tokens from one account to another with variable privacy
|
||||
/// Send native tokens from one account to another with variable privacy
|
||||
///
|
||||
///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns.
|
||||
/// If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive
|
||||
/// patterns.
|
||||
///
|
||||
///First is used for owned accounts, second otherwise.
|
||||
/// First is used for owned accounts, second otherwise.
|
||||
Send {
|
||||
///from - valid 32 byte base58 string with privacy prefix
|
||||
/// from - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte base58 string with privacy prefix
|
||||
/// to - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
to: Option<String>,
|
||||
///to_npk - valid 32 byte hex string
|
||||
/// to_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to_npk: Option<String>,
|
||||
///to_ipk - valid 33 byte hex string
|
||||
/// to_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
to_ipk: Option<String>,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
@ -48,15 +49,15 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
AuthTransferSubcommand::Init { addr } => {
|
||||
let (addr, addr_privacy) = parse_addr_with_privacy_prefix(&addr)?;
|
||||
AuthTransferSubcommand::Init { account_id } => {
|
||||
let (account_id, addr_privacy) = parse_addr_with_privacy_prefix(&account_id)?;
|
||||
|
||||
match addr_privacy {
|
||||
AddressPrivacyKind::Public => {
|
||||
let addr = addr.parse()?;
|
||||
AccountPrivacyKind::Public => {
|
||||
let account_id = account_id.parse()?;
|
||||
|
||||
let res = wallet_core
|
||||
.register_account_under_authenticated_transfers_programs(addr)
|
||||
.register_account_under_authenticated_transfers_programs(account_id)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send is {res:#?}");
|
||||
@ -70,11 +71,13 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
|
||||
println!("Stored persistent accounts at {path:#?}");
|
||||
}
|
||||
AddressPrivacyKind::Private => {
|
||||
let addr = addr.parse()?;
|
||||
AccountPrivacyKind::Private => {
|
||||
let account_id = account_id.parse()?;
|
||||
|
||||
let (res, [secret]) = wallet_core
|
||||
.register_account_under_authenticated_transfers_programs_private(addr)
|
||||
.register_account_under_authenticated_transfers_programs_private(
|
||||
account_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Results of tx send is {res:#?}");
|
||||
@ -85,7 +88,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret, addr)];
|
||||
let acc_decode_data = vec![(secret, account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -111,12 +114,12 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
let underlying_subcommand = match (to, to_npk, to_ipk) {
|
||||
(None, None, None) => {
|
||||
anyhow::bail!(
|
||||
"Provide either account address of receiver or their public keys"
|
||||
"Provide either account account_id of receiver or their public keys"
|
||||
);
|
||||
}
|
||||
(Some(_), Some(_), Some(_)) => {
|
||||
anyhow::bail!(
|
||||
"Provide only one variant: either account address of receiver or their public keys"
|
||||
"Provide only one variant: either account account_id of receiver or their public keys"
|
||||
);
|
||||
}
|
||||
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||
@ -127,10 +130,10 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?;
|
||||
|
||||
match (from_privacy, to_privacy) {
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
NativeTokenTransferProgramSubcommand::Public { from, to, amount }
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
NativeTokenTransferProgramSubcommand::Private(
|
||||
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned {
|
||||
from,
|
||||
@ -139,14 +142,14 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Public) => {
|
||||
NativeTokenTransferProgramSubcommand::Deshielded {
|
||||
from,
|
||||
to,
|
||||
amount,
|
||||
}
|
||||
}
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Private) => {
|
||||
NativeTokenTransferProgramSubcommand::Shielded(
|
||||
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned {
|
||||
from,
|
||||
@ -161,7 +164,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||
|
||||
match from_privacy {
|
||||
AddressPrivacyKind::Private => {
|
||||
AccountPrivacyKind::Private => {
|
||||
NativeTokenTransferProgramSubcommand::Private(
|
||||
NativeTokenTransferProgramSubcommandPrivate::PrivateForeign {
|
||||
from,
|
||||
@ -171,7 +174,7 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
},
|
||||
)
|
||||
}
|
||||
AddressPrivacyKind::Public => {
|
||||
AccountPrivacyKind::Public => {
|
||||
NativeTokenTransferProgramSubcommand::Shielded(
|
||||
NativeTokenTransferProgramSubcommandShielded::ShieldedForeign {
|
||||
from,
|
||||
@ -191,112 +194,114 @@ impl WalletSubcommand for AuthTransferSubcommand {
|
||||
}
|
||||
}
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||
/// Represents generic CLI subcommand for a wallet working with native token transfer program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum NativeTokenTransferProgramSubcommand {
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Public operation
|
||||
Public {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte hex string
|
||||
/// to - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
///Private execution
|
||||
/// Private execution
|
||||
#[command(subcommand)]
|
||||
Private(NativeTokenTransferProgramSubcommandPrivate),
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Deshielded operation
|
||||
Deshielded {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte hex string
|
||||
/// to - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
///Shielded execution
|
||||
/// Shielded execution
|
||||
#[command(subcommand)]
|
||||
Shielded(NativeTokenTransferProgramSubcommandShielded),
|
||||
}
|
||||
|
||||
///Represents generic shielded CLI subcommand for a wallet working with native token transfer program
|
||||
/// Represents generic shielded CLI subcommand for a wallet working with native token transfer
|
||||
/// program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum NativeTokenTransferProgramSubcommandShielded {
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Shielded operation
|
||||
ShieldedOwned {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte hex string
|
||||
/// to - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Shielded operation
|
||||
ShieldedForeign {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to_npk - valid 32 byte hex string
|
||||
/// to_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to_npk: String,
|
||||
///to_ipk - valid 33 byte hex string
|
||||
/// to_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
to_ipk: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
}
|
||||
|
||||
///Represents generic private CLI subcommand for a wallet working with native token transfer program
|
||||
/// Represents generic private CLI subcommand for a wallet working with native token transfer
|
||||
/// program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum NativeTokenTransferProgramSubcommandPrivate {
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Private operation
|
||||
PrivateOwned {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte hex string
|
||||
/// to - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
///Send native token transfer from `from` to `to` for `amount`
|
||||
/// Send native token transfer from `from` to `to` for `amount`
|
||||
///
|
||||
/// Private operation
|
||||
PrivateForeign {
|
||||
///from - valid 32 byte hex string
|
||||
/// from - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to_npk - valid 32 byte hex string
|
||||
/// to_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to_npk: String,
|
||||
///to_ipk - valid 33 byte hex string
|
||||
/// to_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
to_ipk: String,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
@ -309,8 +314,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
NativeTokenTransferProgramSubcommandPrivate::PrivateOwned { from, to, amount } => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let to: Address = to.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
let to_initialization = wallet_core.check_private_account_initialized(&to).await?;
|
||||
|
||||
@ -356,7 +361,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate {
|
||||
to_ipk,
|
||||
amount,
|
||||
} => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to_npk_res = hex::decode(to_npk)?;
|
||||
let mut to_npk = [0; 32];
|
||||
to_npk.copy_from_slice(&to_npk_res);
|
||||
@ -405,8 +410,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
NativeTokenTransferProgramSubcommandShielded::ShieldedOwned { from, to, amount } => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let to: Address = to.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
let to_initialization = wallet_core.check_private_account_initialized(&to).await?;
|
||||
|
||||
@ -450,7 +455,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded {
|
||||
to_ipk,
|
||||
amount,
|
||||
} => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
|
||||
let to_npk_res = hex::decode(to_npk)?;
|
||||
let mut to_npk = [0; 32];
|
||||
@ -494,8 +499,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
||||
shielded_subcommand.handle_subcommand(wallet_core).await
|
||||
}
|
||||
NativeTokenTransferProgramSubcommand::Deshielded { from, to, amount } => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let to: Address = to.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
let (res, [secret]) = wallet_core
|
||||
.send_deshielded_native_token_transfer(from, to, amount)
|
||||
@ -524,8 +529,8 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand {
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
NativeTokenTransferProgramSubcommand::Public { from, to, amount } => {
|
||||
let from: Address = from.parse().unwrap();
|
||||
let to: Address = to.parse().unwrap();
|
||||
let from: AccountId = from.parse().unwrap();
|
||||
let to: AccountId = to.parse().unwrap();
|
||||
|
||||
let res = wallet_core
|
||||
.send_public_native_token_transfer(from, to, amount)
|
||||
|
||||
@ -6,18 +6,18 @@ use log::info;
|
||||
use crate::{
|
||||
SubcommandReturnValue, WalletCore,
|
||||
cli::WalletSubcommand,
|
||||
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
};
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with pinata program
|
||||
/// Represents generic CLI subcommand for a wallet working with pinata program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum PinataProgramAgnosticSubcommand {
|
||||
///Claim pinata
|
||||
/// Claim pinata
|
||||
Claim {
|
||||
///to_addr - valid 32 byte base58 string with privacy prefix
|
||||
/// to_account_id - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
to_addr: String,
|
||||
///solution - solution to pinata challenge
|
||||
to_account_id: String,
|
||||
/// solution - solution to pinata challenge
|
||||
#[arg(long)]
|
||||
solution: u128,
|
||||
},
|
||||
@ -29,21 +29,25 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand {
|
||||
wallet_core: &mut WalletCore,
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
let underlying_subcommand = match self {
|
||||
PinataProgramAgnosticSubcommand::Claim { to_addr, solution } => {
|
||||
let (to_addr, to_addr_privacy) = parse_addr_with_privacy_prefix(&to_addr)?;
|
||||
PinataProgramAgnosticSubcommand::Claim {
|
||||
to_account_id,
|
||||
solution,
|
||||
} => {
|
||||
let (to_account_id, to_addr_privacy) =
|
||||
parse_addr_with_privacy_prefix(&to_account_id)?;
|
||||
|
||||
match to_addr_privacy {
|
||||
AddressPrivacyKind::Public => {
|
||||
AccountPrivacyKind::Public => {
|
||||
PinataProgramSubcommand::Public(PinataProgramSubcommandPublic::Claim {
|
||||
pinata_addr: PINATA_BASE58.to_string(),
|
||||
winner_addr: to_addr,
|
||||
pinata_account_id: PINATA_BASE58.to_string(),
|
||||
winner_account_id: to_account_id,
|
||||
solution,
|
||||
})
|
||||
}
|
||||
AddressPrivacyKind::Private => PinataProgramSubcommand::Private(
|
||||
AccountPrivacyKind::Private => PinataProgramSubcommand::Private(
|
||||
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
|
||||
pinata_addr: PINATA_BASE58.to_string(),
|
||||
winner_addr: to_addr,
|
||||
pinata_account_id: PINATA_BASE58.to_string(),
|
||||
winner_account_id: to_account_id,
|
||||
solution,
|
||||
},
|
||||
),
|
||||
@ -55,48 +59,48 @@ impl WalletSubcommand for PinataProgramAgnosticSubcommand {
|
||||
}
|
||||
}
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with pinata program
|
||||
/// Represents generic CLI subcommand for a wallet working with pinata program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum PinataProgramSubcommand {
|
||||
///Public execution
|
||||
/// Public execution
|
||||
#[command(subcommand)]
|
||||
Public(PinataProgramSubcommandPublic),
|
||||
///Private execution
|
||||
/// Private execution
|
||||
#[command(subcommand)]
|
||||
Private(PinataProgramSubcommandPrivate),
|
||||
}
|
||||
|
||||
///Represents generic public CLI subcommand for a wallet working with pinata program
|
||||
/// Represents generic public CLI subcommand for a wallet working with pinata program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum PinataProgramSubcommandPublic {
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
// Claim piñata prize
|
||||
Claim {
|
||||
///pinata_addr - valid 32 byte hex string
|
||||
/// pinata_account_id - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
pinata_addr: String,
|
||||
///winner_addr - valid 32 byte hex string
|
||||
pinata_account_id: String,
|
||||
/// winner_account_id - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
winner_addr: String,
|
||||
///solution - solution to pinata challenge
|
||||
winner_account_id: String,
|
||||
/// solution - solution to pinata challenge
|
||||
#[arg(long)]
|
||||
solution: u128,
|
||||
},
|
||||
}
|
||||
|
||||
///Represents generic private CLI subcommand for a wallet working with pinata program
|
||||
/// Represents generic private CLI subcommand for a wallet working with pinata program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum PinataProgramSubcommandPrivate {
|
||||
// TODO: Testnet only. Refactor to prevent compilation on mainnet.
|
||||
// Claim piñata prize
|
||||
ClaimPrivateOwned {
|
||||
///pinata_addr - valid 32 byte hex string
|
||||
/// pinata_account_id - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
pinata_addr: String,
|
||||
///winner_addr - valid 32 byte hex string
|
||||
pinata_account_id: String,
|
||||
/// winner_account_id - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
winner_addr: String,
|
||||
///solution - solution to pinata challenge
|
||||
winner_account_id: String,
|
||||
/// solution - solution to pinata challenge
|
||||
#[arg(long)]
|
||||
solution: u128,
|
||||
},
|
||||
@ -109,14 +113,14 @@ impl WalletSubcommand for PinataProgramSubcommandPublic {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
PinataProgramSubcommandPublic::Claim {
|
||||
pinata_addr,
|
||||
winner_addr,
|
||||
pinata_account_id,
|
||||
winner_account_id,
|
||||
solution,
|
||||
} => {
|
||||
let res = wallet_core
|
||||
.claim_pinata(
|
||||
pinata_addr.parse().unwrap(),
|
||||
winner_addr.parse().unwrap(),
|
||||
pinata_account_id.parse().unwrap(),
|
||||
winner_account_id.parse().unwrap(),
|
||||
solution,
|
||||
)
|
||||
.await?;
|
||||
@ -135,22 +139,22 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
PinataProgramSubcommandPrivate::ClaimPrivateOwned {
|
||||
pinata_addr,
|
||||
winner_addr,
|
||||
pinata_account_id,
|
||||
winner_account_id,
|
||||
solution,
|
||||
} => {
|
||||
let pinata_addr = pinata_addr.parse().unwrap();
|
||||
let winner_addr = winner_addr.parse().unwrap();
|
||||
let pinata_account_id = pinata_account_id.parse().unwrap();
|
||||
let winner_account_id = winner_account_id.parse().unwrap();
|
||||
|
||||
let winner_initialization = wallet_core
|
||||
.check_private_account_initialized(&winner_addr)
|
||||
.check_private_account_initialized(&winner_account_id)
|
||||
.await?;
|
||||
|
||||
let (res, [secret_winner]) = if let Some(winner_proof) = winner_initialization {
|
||||
wallet_core
|
||||
.claim_pinata_private_owned_account_already_initialized(
|
||||
pinata_addr,
|
||||
winner_addr,
|
||||
pinata_account_id,
|
||||
winner_account_id,
|
||||
solution,
|
||||
winner_proof,
|
||||
)
|
||||
@ -158,8 +162,8 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
|
||||
} else {
|
||||
wallet_core
|
||||
.claim_pinata_private_owned_account_not_initialized(
|
||||
pinata_addr,
|
||||
winner_addr,
|
||||
pinata_account_id,
|
||||
winner_account_id,
|
||||
solution,
|
||||
)
|
||||
.await?
|
||||
@ -173,7 +177,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_winner, winner_addr)];
|
||||
let acc_decode_data = vec![(secret_winner, winner_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
|
||||
@ -1,51 +1,52 @@
|
||||
use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use common::transaction::NSSATransaction;
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
|
||||
use crate::{
|
||||
SubcommandReturnValue, WalletCore,
|
||||
cli::WalletSubcommand,
|
||||
helperfunctions::{AddressPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
helperfunctions::{AccountPrivacyKind, parse_addr_with_privacy_prefix},
|
||||
};
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with token program
|
||||
/// Represents generic CLI subcommand for a wallet working with token program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramAgnosticSubcommand {
|
||||
///Produce a new token
|
||||
/// Produce a new token
|
||||
///
|
||||
///Currently the only supported privacy options is for public definition
|
||||
/// Currently the only supported privacy options is for public definition
|
||||
New {
|
||||
///definition_addr - valid 32 byte base58 string with privacy prefix
|
||||
/// definition_account_id - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
definition_addr: String,
|
||||
///supply_addr - valid 32 byte base58 string with privacy prefix
|
||||
definition_account_id: String,
|
||||
/// supply_account_id - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
supply_addr: String,
|
||||
supply_account_id: String,
|
||||
#[arg(short, long)]
|
||||
name: String,
|
||||
#[arg(short, long)]
|
||||
total_supply: u128,
|
||||
},
|
||||
///Send tokens from one account to another with variable privacy
|
||||
/// Send tokens from one account to another with variable privacy
|
||||
///
|
||||
///If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive patterns.
|
||||
/// If receiver is private, then `to` and (`to_npk` , `to_ipk`) is a mutually exclusive
|
||||
/// patterns.
|
||||
///
|
||||
///First is used for owned accounts, second otherwise.
|
||||
/// First is used for owned accounts, second otherwise.
|
||||
Send {
|
||||
///from - valid 32 byte base58 string with privacy prefix
|
||||
/// from - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
from: String,
|
||||
///to - valid 32 byte base58 string with privacy prefix
|
||||
/// to - valid 32 byte base58 string with privacy prefix
|
||||
#[arg(long)]
|
||||
to: Option<String>,
|
||||
///to_npk - valid 32 byte hex string
|
||||
/// to_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
to_npk: Option<String>,
|
||||
///to_ipk - valid 33 byte hex string
|
||||
/// to_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
to_ipk: Option<String>,
|
||||
///amount - amount of balance to move
|
||||
/// amount - amount of balance to move
|
||||
#[arg(long)]
|
||||
amount: u128,
|
||||
},
|
||||
@ -58,43 +59,45 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
TokenProgramAgnosticSubcommand::New {
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name,
|
||||
total_supply,
|
||||
} => {
|
||||
let (definition_addr, definition_addr_privacy) =
|
||||
parse_addr_with_privacy_prefix(&definition_addr)?;
|
||||
let (supply_addr, supply_addr_privacy) =
|
||||
parse_addr_with_privacy_prefix(&supply_addr)?;
|
||||
let (definition_account_id, definition_addr_privacy) =
|
||||
parse_addr_with_privacy_prefix(&definition_account_id)?;
|
||||
let (supply_account_id, supply_addr_privacy) =
|
||||
parse_addr_with_privacy_prefix(&supply_account_id)?;
|
||||
|
||||
let underlying_subcommand = match (definition_addr_privacy, supply_addr_privacy) {
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::CreateNewToken {
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name,
|
||||
total_supply,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned {
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name,
|
||||
total_supply,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||
//ToDo: maybe implement this one. It is not immediately clear why definition should be private.
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
// ToDo: maybe implement this one. It is not immediately clear why
|
||||
// definition should be private.
|
||||
anyhow::bail!("Unavailable privacy pairing")
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||
//ToDo: Probably valid. If definition is not public, but supply is it is very suspicious.
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Public) => {
|
||||
// ToDo: Probably valid. If definition is not public, but supply is it is
|
||||
// very suspicious.
|
||||
anyhow::bail!("Unavailable privacy pairing")
|
||||
}
|
||||
};
|
||||
@ -111,12 +114,12 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
let underlying_subcommand = match (to, to_npk, to_ipk) {
|
||||
(None, None, None) => {
|
||||
anyhow::bail!(
|
||||
"Provide either account address of receiver or their public keys"
|
||||
"Provide either account account_id of receiver or their public keys"
|
||||
);
|
||||
}
|
||||
(Some(_), Some(_), Some(_)) => {
|
||||
anyhow::bail!(
|
||||
"Provide only one variant: either account address of receiver or their public keys"
|
||||
"Provide only one variant: either account account_id of receiver or their public keys"
|
||||
);
|
||||
}
|
||||
(_, Some(_), None) | (_, None, Some(_)) => {
|
||||
@ -127,38 +130,38 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
let (to, to_privacy) = parse_addr_with_privacy_prefix(&to)?;
|
||||
|
||||
match (from_privacy, to_privacy) {
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Public) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Public(
|
||||
TokenProgramSubcommandPublic::TransferToken {
|
||||
sender_addr: from,
|
||||
recipient_addr: to,
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
balance_to_move: amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Private) => {
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::TransferTokenPrivateOwned {
|
||||
sender_addr: from,
|
||||
recipient_addr: to,
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
balance_to_move: amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Private, AddressPrivacyKind::Public) => {
|
||||
(AccountPrivacyKind::Private, AccountPrivacyKind::Public) => {
|
||||
TokenProgramSubcommand::Deshielded(
|
||||
TokenProgramSubcommandDeshielded::TransferTokenDeshielded {
|
||||
sender_addr: from,
|
||||
recipient_addr: to,
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
balance_to_move: amount,
|
||||
},
|
||||
)
|
||||
}
|
||||
(AddressPrivacyKind::Public, AddressPrivacyKind::Private) => {
|
||||
(AccountPrivacyKind::Public, AccountPrivacyKind::Private) => {
|
||||
TokenProgramSubcommand::Shielded(
|
||||
TokenProgramSubcommandShielded::TransferTokenShieldedOwned {
|
||||
sender_addr: from,
|
||||
recipient_addr: to,
|
||||
sender_account_id: from,
|
||||
recipient_account_id: to,
|
||||
balance_to_move: amount,
|
||||
},
|
||||
)
|
||||
@ -169,17 +172,17 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
let (from, from_privacy) = parse_addr_with_privacy_prefix(&from)?;
|
||||
|
||||
match from_privacy {
|
||||
AddressPrivacyKind::Private => TokenProgramSubcommand::Private(
|
||||
AccountPrivacyKind::Private => TokenProgramSubcommand::Private(
|
||||
TokenProgramSubcommandPrivate::TransferTokenPrivateForeign {
|
||||
sender_addr: from,
|
||||
sender_account_id: from,
|
||||
recipient_npk: to_npk,
|
||||
recipient_ipk: to_ipk,
|
||||
balance_to_move: amount,
|
||||
},
|
||||
),
|
||||
AddressPrivacyKind::Public => TokenProgramSubcommand::Shielded(
|
||||
AccountPrivacyKind::Public => TokenProgramSubcommand::Shielded(
|
||||
TokenProgramSubcommandShielded::TransferTokenShieldedForeign {
|
||||
sender_addr: from,
|
||||
sender_account_id: from,
|
||||
recipient_npk: to_npk,
|
||||
recipient_ipk: to_ipk,
|
||||
balance_to_move: amount,
|
||||
@ -195,79 +198,79 @@ impl WalletSubcommand for TokenProgramAgnosticSubcommand {
|
||||
}
|
||||
}
|
||||
|
||||
///Represents generic CLI subcommand for a wallet working with token_program
|
||||
/// Represents generic CLI subcommand for a wallet working with token_program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramSubcommand {
|
||||
///Public execution
|
||||
/// Public execution
|
||||
#[command(subcommand)]
|
||||
Public(TokenProgramSubcommandPublic),
|
||||
///Private execution
|
||||
/// Private execution
|
||||
#[command(subcommand)]
|
||||
Private(TokenProgramSubcommandPrivate),
|
||||
///Deshielded execution
|
||||
/// Deshielded execution
|
||||
#[command(subcommand)]
|
||||
Deshielded(TokenProgramSubcommandDeshielded),
|
||||
///Shielded execution
|
||||
/// Shielded execution
|
||||
#[command(subcommand)]
|
||||
Shielded(TokenProgramSubcommandShielded),
|
||||
}
|
||||
|
||||
///Represents generic public CLI subcommand for a wallet working with token_program
|
||||
/// Represents generic public CLI subcommand for a wallet working with token_program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramSubcommandPublic {
|
||||
//Create a new token using the token program
|
||||
// Create a new token using the token program
|
||||
CreateNewToken {
|
||||
#[arg(short, long)]
|
||||
definition_addr: String,
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
supply_addr: String,
|
||||
supply_account_id: String,
|
||||
#[arg(short, long)]
|
||||
name: String,
|
||||
#[arg(short, long)]
|
||||
total_supply: u128,
|
||||
},
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferToken {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
sender_account_id: String,
|
||||
#[arg(short, long)]
|
||||
recipient_addr: String,
|
||||
recipient_account_id: String,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
}
|
||||
|
||||
///Represents generic private CLI subcommand for a wallet working with token_program
|
||||
/// Represents generic private CLI subcommand for a wallet working with token_program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramSubcommandPrivate {
|
||||
//Create a new token using the token program
|
||||
// Create a new token using the token program
|
||||
CreateNewTokenPrivateOwned {
|
||||
#[arg(short, long)]
|
||||
definition_addr: String,
|
||||
definition_account_id: String,
|
||||
#[arg(short, long)]
|
||||
supply_addr: String,
|
||||
supply_account_id: String,
|
||||
#[arg(short, long)]
|
||||
name: String,
|
||||
#[arg(short, long)]
|
||||
total_supply: u128,
|
||||
},
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenPrivateOwned {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
sender_account_id: String,
|
||||
#[arg(short, long)]
|
||||
recipient_addr: String,
|
||||
recipient_account_id: String,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenPrivateForeign {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
///recipient_npk - valid 32 byte hex string
|
||||
sender_account_id: String,
|
||||
/// recipient_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
recipient_npk: String,
|
||||
///recipient_ipk - valid 33 byte hex string
|
||||
/// recipient_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
recipient_ipk: String,
|
||||
#[arg(short, long)]
|
||||
@ -275,40 +278,40 @@ pub enum TokenProgramSubcommandPrivate {
|
||||
},
|
||||
}
|
||||
|
||||
///Represents deshielded public CLI subcommand for a wallet working with token_program
|
||||
/// Represents deshielded public CLI subcommand for a wallet working with token_program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramSubcommandDeshielded {
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenDeshielded {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
sender_account_id: String,
|
||||
#[arg(short, long)]
|
||||
recipient_addr: String,
|
||||
recipient_account_id: String,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
}
|
||||
|
||||
///Represents generic shielded CLI subcommand for a wallet working with token_program
|
||||
/// Represents generic shielded CLI subcommand for a wallet working with token_program
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
pub enum TokenProgramSubcommandShielded {
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenShieldedOwned {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
sender_account_id: String,
|
||||
#[arg(short, long)]
|
||||
recipient_addr: String,
|
||||
recipient_account_id: String,
|
||||
#[arg(short, long)]
|
||||
balance_to_move: u128,
|
||||
},
|
||||
//Transfer tokens using the token program
|
||||
// Transfer tokens using the token program
|
||||
TransferTokenShieldedForeign {
|
||||
#[arg(short, long)]
|
||||
sender_addr: String,
|
||||
///recipient_npk - valid 32 byte hex string
|
||||
sender_account_id: String,
|
||||
/// recipient_npk - valid 32 byte hex string
|
||||
#[arg(long)]
|
||||
recipient_npk: String,
|
||||
///recipient_ipk - valid 33 byte hex string
|
||||
/// recipient_ipk - valid 33 byte hex string
|
||||
#[arg(long)]
|
||||
recipient_ipk: String,
|
||||
#[arg(short, long)]
|
||||
@ -323,8 +326,8 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
TokenProgramSubcommandPublic::CreateNewToken {
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name,
|
||||
total_supply,
|
||||
} => {
|
||||
@ -337,8 +340,8 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
name_bytes[..name.len()].copy_from_slice(name);
|
||||
wallet_core
|
||||
.send_new_token_definition(
|
||||
definition_addr.parse().unwrap(),
|
||||
supply_addr.parse().unwrap(),
|
||||
definition_account_id.parse().unwrap(),
|
||||
supply_account_id.parse().unwrap(),
|
||||
name_bytes,
|
||||
total_supply,
|
||||
)
|
||||
@ -346,14 +349,14 @@ impl WalletSubcommand for TokenProgramSubcommandPublic {
|
||||
Ok(SubcommandReturnValue::Empty)
|
||||
}
|
||||
TokenProgramSubcommandPublic::TransferToken {
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
} => {
|
||||
wallet_core
|
||||
.send_transfer_token_transaction(
|
||||
sender_addr.parse().unwrap(),
|
||||
recipient_addr.parse().unwrap(),
|
||||
sender_account_id.parse().unwrap(),
|
||||
recipient_account_id.parse().unwrap(),
|
||||
balance_to_move,
|
||||
)
|
||||
.await?;
|
||||
@ -370,8 +373,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
TokenProgramSubcommandPrivate::CreateNewTokenPrivateOwned {
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name,
|
||||
total_supply,
|
||||
} => {
|
||||
@ -383,13 +386,13 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
let mut name_bytes = [0; 6];
|
||||
name_bytes[..name.len()].copy_from_slice(name);
|
||||
|
||||
let definition_addr: Address = definition_addr.parse().unwrap();
|
||||
let supply_addr: Address = supply_addr.parse().unwrap();
|
||||
let definition_account_id: AccountId = definition_account_id.parse().unwrap();
|
||||
let supply_account_id: AccountId = supply_account_id.parse().unwrap();
|
||||
|
||||
let (res, [secret_supply]) = wallet_core
|
||||
.send_new_token_definition_private_owned(
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
name_bytes,
|
||||
total_supply,
|
||||
)
|
||||
@ -403,7 +406,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_supply, supply_addr)];
|
||||
let acc_decode_data = vec![(secret_supply, supply_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -418,23 +421,23 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::TransferTokenPrivateOwned {
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender_addr: Address = sender_addr.parse().unwrap();
|
||||
let recipient_addr: Address = recipient_addr.parse().unwrap();
|
||||
let sender_account_id: AccountId = sender_account_id.parse().unwrap();
|
||||
let recipient_account_id: AccountId = recipient_account_id.parse().unwrap();
|
||||
|
||||
let recipient_initialization = wallet_core
|
||||
.check_private_account_initialized(&recipient_addr)
|
||||
.check_private_account_initialized(&recipient_account_id)
|
||||
.await?;
|
||||
|
||||
let (res, [secret_sender, secret_recipient]) =
|
||||
if let Some(recipient_proof) = recipient_initialization {
|
||||
wallet_core
|
||||
.send_transfer_token_transaction_private_owned_account_already_initialized(
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
recipient_proof,
|
||||
)
|
||||
@ -442,8 +445,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
} else {
|
||||
wallet_core
|
||||
.send_transfer_token_transaction_private_owned_account_not_initialized(
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
)
|
||||
.await?
|
||||
@ -458,8 +461,8 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![
|
||||
(secret_sender, sender_addr),
|
||||
(secret_recipient, recipient_addr),
|
||||
(secret_sender, sender_account_id),
|
||||
(secret_recipient, recipient_account_id),
|
||||
];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
@ -475,12 +478,12 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandPrivate::TransferTokenPrivateForeign {
|
||||
sender_addr,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender_addr: Address = sender_addr.parse().unwrap();
|
||||
let sender_account_id: AccountId = sender_account_id.parse().unwrap();
|
||||
let recipient_npk_res = hex::decode(recipient_npk)?;
|
||||
let mut recipient_npk = [0; 32];
|
||||
recipient_npk.copy_from_slice(&recipient_npk_res);
|
||||
@ -495,7 +498,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
|
||||
let (res, [secret_sender, _]) = wallet_core
|
||||
.send_transfer_token_transaction_private_foreign_account(
|
||||
sender_addr,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
balance_to_move,
|
||||
@ -510,7 +513,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_sender, sender_addr)];
|
||||
let acc_decode_data = vec![(secret_sender, sender_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -535,17 +538,17 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
TokenProgramSubcommandDeshielded::TransferTokenDeshielded {
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender_addr: Address = sender_addr.parse().unwrap();
|
||||
let recipient_addr: Address = recipient_addr.parse().unwrap();
|
||||
let sender_account_id: AccountId = sender_account_id.parse().unwrap();
|
||||
let recipient_account_id: AccountId = recipient_account_id.parse().unwrap();
|
||||
|
||||
let (res, [secret_sender]) = wallet_core
|
||||
.send_transfer_token_transaction_deshielded(
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
)
|
||||
.await?;
|
||||
@ -558,7 +561,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_sender, sender_addr)];
|
||||
let acc_decode_data = vec![(secret_sender, sender_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
@ -583,12 +586,12 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
) -> Result<SubcommandReturnValue> {
|
||||
match self {
|
||||
TokenProgramSubcommandShielded::TransferTokenShieldedForeign {
|
||||
sender_addr,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender_addr: Address = sender_addr.parse().unwrap();
|
||||
let sender_account_id: AccountId = sender_account_id.parse().unwrap();
|
||||
let recipient_npk_res = hex::decode(recipient_npk)?;
|
||||
let mut recipient_npk = [0; 32];
|
||||
recipient_npk.copy_from_slice(&recipient_npk_res);
|
||||
@ -603,7 +606,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
|
||||
let res = wallet_core
|
||||
.send_transfer_token_transaction_shielded_foreign_account(
|
||||
sender_addr,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
balance_to_move,
|
||||
@ -628,23 +631,23 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash })
|
||||
}
|
||||
TokenProgramSubcommandShielded::TransferTokenShieldedOwned {
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
} => {
|
||||
let sender_addr: Address = sender_addr.parse().unwrap();
|
||||
let recipient_addr: Address = recipient_addr.parse().unwrap();
|
||||
let sender_account_id: AccountId = sender_account_id.parse().unwrap();
|
||||
let recipient_account_id: AccountId = recipient_account_id.parse().unwrap();
|
||||
|
||||
let recipient_initialization = wallet_core
|
||||
.check_private_account_initialized(&recipient_addr)
|
||||
.check_private_account_initialized(&recipient_account_id)
|
||||
.await?;
|
||||
|
||||
let (res, [secret_recipient]) =
|
||||
if let Some(recipient_proof) = recipient_initialization {
|
||||
wallet_core
|
||||
.send_transfer_token_transaction_shielded_owned_account_already_initialized(
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
recipient_proof,
|
||||
)
|
||||
@ -652,8 +655,8 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
} else {
|
||||
wallet_core
|
||||
.send_transfer_token_transaction_shielded_owned_account_not_initialized(
|
||||
sender_addr,
|
||||
recipient_addr,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
balance_to_move,
|
||||
)
|
||||
.await?
|
||||
@ -667,7 +670,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded {
|
||||
.await?;
|
||||
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx {
|
||||
let acc_decode_data = vec![(secret_recipient, recipient_addr)];
|
||||
let acc_decode_data = vec![(secret_recipient, recipient_account_id)];
|
||||
|
||||
wallet_core.decode_insert_privacy_preserving_transaction_results(
|
||||
tx,
|
||||
|
||||
@ -8,33 +8,34 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InitialAccountDataPublic {
|
||||
pub address: String,
|
||||
pub account_id: String,
|
||||
pub pub_sign_key: nssa::PrivateKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PersistentAccountDataPublic {
|
||||
pub address: nssa::Address,
|
||||
pub account_id: nssa::AccountId,
|
||||
pub chain_index: ChainIndex,
|
||||
pub data: ChildKeysPublic,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InitialAccountDataPrivate {
|
||||
pub address: String,
|
||||
pub account_id: String,
|
||||
pub account: nssa_core::account::Account,
|
||||
pub key_chain: KeyChain,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PersistentAccountDataPrivate {
|
||||
pub address: nssa::Address,
|
||||
pub account_id: nssa::AccountId,
|
||||
pub chain_index: ChainIndex,
|
||||
pub data: ChildKeysPrivate,
|
||||
}
|
||||
|
||||
//Big difference in enum variants sizes
|
||||
//however it is improbable, that we will have that much accounts, that it will substantialy affect memory
|
||||
// Big difference in enum variants sizes
|
||||
// however it is improbable, that we will have that much accounts, that it will substantialy affect
|
||||
// memory
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum InitialAccountData {
|
||||
@ -42,8 +43,9 @@ pub enum InitialAccountData {
|
||||
Private(InitialAccountDataPrivate),
|
||||
}
|
||||
|
||||
//Big difference in enum variants sizes
|
||||
//however it is improbable, that we will have that much accounts, that it will substantialy affect memory
|
||||
// Big difference in enum variants sizes
|
||||
// however it is improbable, that we will have that much accounts, that it will substantialy affect
|
||||
// memory
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum PersistentAccountData {
|
||||
@ -59,20 +61,20 @@ pub struct PersistentStorage {
|
||||
}
|
||||
|
||||
impl InitialAccountData {
|
||||
pub fn address(&self) -> nssa::Address {
|
||||
pub fn account_id(&self) -> nssa::AccountId {
|
||||
match &self {
|
||||
Self::Public(acc) => acc.address.parse().unwrap(),
|
||||
Self::Private(acc) => acc.address.parse().unwrap(),
|
||||
Self::Public(acc) => acc.account_id.parse().unwrap(),
|
||||
Self::Private(acc) => acc.account_id.parse().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PersistentAccountData {
|
||||
pub fn address(&self) -> nssa::Address {
|
||||
pub fn account_id(&self) -> nssa::AccountId {
|
||||
match &self {
|
||||
Self::Public(acc) => acc.address,
|
||||
Self::Private(acc) => acc.address,
|
||||
Self::Preconfigured(acc) => acc.address(),
|
||||
Self::Public(acc) => acc.account_id,
|
||||
Self::Private(acc) => acc.account_id,
|
||||
Self::Preconfigured(acc) => acc.account_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,19 +129,19 @@ pub struct GasConfig {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WalletConfig {
|
||||
///Override rust log (env var logging level)
|
||||
/// Override rust log (env var logging level)
|
||||
pub override_rust_log: Option<String>,
|
||||
///Sequencer URL
|
||||
/// Sequencer URL
|
||||
pub sequencer_addr: String,
|
||||
///Sequencer polling duration for new blocks in milliseconds
|
||||
/// Sequencer polling duration for new blocks in milliseconds
|
||||
pub seq_poll_timeout_millis: u64,
|
||||
///Sequencer polling max number of blocks
|
||||
/// Sequencer polling max number of blocks
|
||||
pub seq_poll_max_blocks: usize,
|
||||
///Sequencer polling max number error retries
|
||||
/// Sequencer polling max number error retries
|
||||
pub seq_poll_max_retries: u64,
|
||||
///Sequencer polling error retry delay in milliseconds
|
||||
/// Sequencer polling error retry delay in milliseconds
|
||||
pub seq_poll_retry_delay_millis: u64,
|
||||
///Initial accounts for wallet
|
||||
/// Initial accounts for wallet
|
||||
pub initial_accounts: Vec<InitialAccountData>,
|
||||
}
|
||||
|
||||
@ -157,7 +159,7 @@ impl Default for WalletConfig {
|
||||
[
|
||||
{
|
||||
"Public": {
|
||||
"address": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy",
|
||||
"pub_sign_key": [
|
||||
16,
|
||||
162,
|
||||
@ -196,7 +198,7 @@ impl Default for WalletConfig {
|
||||
},
|
||||
{
|
||||
"Public": {
|
||||
"address": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"account_id": "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw",
|
||||
"pub_sign_key": [
|
||||
113,
|
||||
121,
|
||||
@ -235,7 +237,7 @@ impl Default for WalletConfig {
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"address": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw",
|
||||
"account_id": "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw",
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
@ -464,7 +466,7 @@ impl Default for WalletConfig {
|
||||
},
|
||||
{
|
||||
"Private": {
|
||||
"address": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX",
|
||||
"account_id": "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX",
|
||||
"account": {
|
||||
"program_owner": [
|
||||
0,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use nssa_core::account::Nonce;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use key_protocol::key_protocol_core::NSSAUserData;
|
||||
use nssa::Account;
|
||||
use nssa_core::account::Nonce;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use serde::Serialize;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
use crate::{
|
||||
HOME_DIR_ENV_VAR,
|
||||
@ -91,7 +91,7 @@ pub async fn fetch_config() -> Result<WalletConfig> {
|
||||
|
||||
/// Fetch data stored at home
|
||||
///
|
||||
/// If file not present, it is considered as empty list of persistent accounts
|
||||
/// File must be created through setup beforehand.
|
||||
pub async fn fetch_persistent_storage() -> Result<PersistentStorage> {
|
||||
let home = get_home()?;
|
||||
let accs_path = home.join("storage.json");
|
||||
@ -120,11 +120,11 @@ pub fn produce_data_for_storage(
|
||||
) -> PersistentStorage {
|
||||
let mut vec_for_storage = vec![];
|
||||
|
||||
for (addr, key) in &user_data.public_key_tree.addr_map {
|
||||
for (account_id, key) in &user_data.public_key_tree.account_id_map {
|
||||
if let Some(data) = user_data.public_key_tree.key_map.get(key) {
|
||||
vec_for_storage.push(
|
||||
PersistentAccountDataPublic {
|
||||
address: *addr,
|
||||
account_id: *account_id,
|
||||
chain_index: key.clone(),
|
||||
data: data.clone(),
|
||||
}
|
||||
@ -133,11 +133,11 @@ pub fn produce_data_for_storage(
|
||||
}
|
||||
}
|
||||
|
||||
for (addr, key) in &user_data.private_key_tree.addr_map {
|
||||
for (account_id, key) in &user_data.private_key_tree.account_id_map {
|
||||
if let Some(data) = user_data.private_key_tree.key_map.get(key) {
|
||||
vec_for_storage.push(
|
||||
PersistentAccountDataPrivate {
|
||||
address: *addr,
|
||||
account_id: *account_id,
|
||||
chain_index: key.clone(),
|
||||
data: data.clone(),
|
||||
}
|
||||
@ -146,20 +146,20 @@ pub fn produce_data_for_storage(
|
||||
}
|
||||
}
|
||||
|
||||
for (addr, key) in &user_data.default_pub_account_signing_keys {
|
||||
for (account_id, key) in &user_data.default_pub_account_signing_keys {
|
||||
vec_for_storage.push(
|
||||
InitialAccountData::Public(InitialAccountDataPublic {
|
||||
address: addr.to_string(),
|
||||
account_id: account_id.to_string(),
|
||||
pub_sign_key: key.clone(),
|
||||
})
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
for (addr, (key_chain, account)) in &user_data.default_user_private_accounts {
|
||||
for (account_id, (key_chain, account)) in &user_data.default_user_private_accounts {
|
||||
vec_for_storage.push(
|
||||
InitialAccountData::Private(InitialAccountDataPrivate {
|
||||
address: addr.to_string(),
|
||||
account_id: account_id.to_string(),
|
||||
account: account.clone(),
|
||||
key_chain: key_chain.clone(),
|
||||
})
|
||||
@ -180,23 +180,23 @@ pub(crate) fn produce_random_nonces(size: usize) -> Vec<Nonce> {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AddressPrivacyKind {
|
||||
pub enum AccountPrivacyKind {
|
||||
Public,
|
||||
Private,
|
||||
}
|
||||
|
||||
pub(crate) fn parse_addr_with_privacy_prefix(
|
||||
addr_base58: &str,
|
||||
) -> Result<(String, AddressPrivacyKind)> {
|
||||
if addr_base58.starts_with("Public/") {
|
||||
account_base58: &str,
|
||||
) -> Result<(String, AccountPrivacyKind)> {
|
||||
if account_base58.starts_with("Public/") {
|
||||
Ok((
|
||||
addr_base58.strip_prefix("Public/").unwrap().to_string(),
|
||||
AddressPrivacyKind::Public,
|
||||
account_base58.strip_prefix("Public/").unwrap().to_string(),
|
||||
AccountPrivacyKind::Public,
|
||||
))
|
||||
} else if addr_base58.starts_with("Private/") {
|
||||
} else if account_base58.starts_with("Private/") {
|
||||
Ok((
|
||||
addr_base58.strip_prefix("Private/").unwrap().to_string(),
|
||||
AddressPrivacyKind::Private,
|
||||
account_base58.strip_prefix("Private/").unwrap().to_string(),
|
||||
AccountPrivacyKind::Private,
|
||||
))
|
||||
} else {
|
||||
anyhow::bail!("Unsupported privacy kind, available variants is Public/ and Private/");
|
||||
@ -249,12 +249,12 @@ mod tests {
|
||||
let addr_base58 = "Public/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||
let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap();
|
||||
|
||||
assert_eq!(addr_kind, AddressPrivacyKind::Public);
|
||||
assert_eq!(addr_kind, AccountPrivacyKind::Public);
|
||||
|
||||
let addr_base58 = "Private/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||
let (_, addr_kind) = parse_addr_with_privacy_prefix(addr_base58).unwrap();
|
||||
|
||||
assert_eq!(addr_kind, AddressPrivacyKind::Private);
|
||||
assert_eq!(addr_kind, AccountPrivacyKind::Private);
|
||||
|
||||
let addr_base58 = "asdsada/BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy";
|
||||
assert!(parse_addr_with_privacy_prefix(addr_base58).is_err());
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use base64::{Engine, engine::general_purpose::STANDARD as BASE64};
|
||||
use chain_storage::WalletChainStore;
|
||||
use clap::{Parser, Subcommand};
|
||||
use common::{
|
||||
block::HashableBlockData,
|
||||
sequencer_client::SequencerClient,
|
||||
transaction::{EncodedTransaction, NSSATransaction},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use chain_storage::WalletChainStore;
|
||||
use config::WalletConfig;
|
||||
use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::KeyNode};
|
||||
use log::info;
|
||||
use nssa::{
|
||||
Account, Address, privacy_preserving_transaction::message::EncryptedAccountData,
|
||||
Account, AccountId, privacy_preserving_transaction::message::EncryptedAccountData,
|
||||
program::Program,
|
||||
};
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use nssa_core::{Commitment, MembershipProof};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
@ -29,10 +27,7 @@ use crate::{
|
||||
token_program::TokenProgramAgnosticSubcommand,
|
||||
},
|
||||
config::PersistentStorage,
|
||||
helperfunctions::fetch_persistent_storage,
|
||||
};
|
||||
use crate::{
|
||||
helperfunctions::{fetch_config, get_home, produce_data_for_storage},
|
||||
helperfunctions::{fetch_config, fetch_persistent_storage, get_home, produce_data_for_storage},
|
||||
poller::TxPoller,
|
||||
};
|
||||
|
||||
@ -92,7 +87,7 @@ impl WalletCore {
|
||||
})
|
||||
}
|
||||
|
||||
///Store persistent data at home
|
||||
/// Store persistent data at home
|
||||
pub async fn store_persistent_data(&self) -> Result<PathBuf> {
|
||||
let home = get_home()?;
|
||||
let storage_path = home.join("storage.json");
|
||||
@ -108,7 +103,7 @@ impl WalletCore {
|
||||
Ok(storage_path)
|
||||
}
|
||||
|
||||
///Store persistent data at home
|
||||
/// Store persistent data at home
|
||||
pub async fn store_config_changes(&self) -> Result<PathBuf> {
|
||||
let home = get_home()?;
|
||||
let config_path = home.join("wallet_config.json");
|
||||
@ -122,20 +117,20 @@ impl WalletCore {
|
||||
Ok(config_path)
|
||||
}
|
||||
|
||||
pub fn create_new_account_public(&mut self, chain_index: ChainIndex) -> Address {
|
||||
pub fn create_new_account_public(&mut self, chain_index: ChainIndex) -> AccountId {
|
||||
self.storage
|
||||
.user_data
|
||||
.generate_new_public_transaction_private_key(chain_index)
|
||||
}
|
||||
|
||||
pub fn create_new_account_private(&mut self, chain_index: ChainIndex) -> Address {
|
||||
pub fn create_new_account_private(&mut self, chain_index: ChainIndex) -> AccountId {
|
||||
self.storage
|
||||
.user_data
|
||||
.generate_new_privacy_preserving_transaction_key_chain(chain_index)
|
||||
}
|
||||
|
||||
///Get account balance
|
||||
pub async fn get_account_balance(&self, acc: Address) -> Result<u128> {
|
||||
/// Get account balance
|
||||
pub async fn get_account_balance(&self, acc: AccountId) -> Result<u128> {
|
||||
Ok(self
|
||||
.sequencer_client
|
||||
.get_account_balance(acc.to_string())
|
||||
@ -143,8 +138,8 @@ impl WalletCore {
|
||||
.balance)
|
||||
}
|
||||
|
||||
///Get accounts nonces
|
||||
pub async fn get_accounts_nonces(&self, accs: Vec<Address>) -> Result<Vec<u128>> {
|
||||
/// Get accounts nonces
|
||||
pub async fn get_accounts_nonces(&self, accs: Vec<AccountId>) -> Result<Vec<u128>> {
|
||||
Ok(self
|
||||
.sequencer_client
|
||||
.get_accounts_nonces(accs.into_iter().map(|acc| acc.to_string()).collect())
|
||||
@ -152,25 +147,28 @@ impl WalletCore {
|
||||
.nonces)
|
||||
}
|
||||
|
||||
///Get account
|
||||
pub async fn get_account_public(&self, addr: Address) -> Result<Account> {
|
||||
let response = self.sequencer_client.get_account(addr.to_string()).await?;
|
||||
/// Get account
|
||||
pub async fn get_account_public(&self, account_id: AccountId) -> Result<Account> {
|
||||
let response = self
|
||||
.sequencer_client
|
||||
.get_account(account_id.to_string())
|
||||
.await?;
|
||||
Ok(response.account)
|
||||
}
|
||||
|
||||
pub fn get_account_private(&self, addr: &Address) -> Option<Account> {
|
||||
pub fn get_account_private(&self, account_id: &AccountId) -> Option<Account> {
|
||||
self.storage
|
||||
.user_data
|
||||
.get_private_account(addr)
|
||||
.get_private_account(account_id)
|
||||
.map(|value| value.1.clone())
|
||||
}
|
||||
|
||||
pub fn get_private_account_commitment(&self, addr: &Address) -> Option<Commitment> {
|
||||
let (keys, account) = self.storage.user_data.get_private_account(addr)?;
|
||||
pub fn get_private_account_commitment(&self, account_id: &AccountId) -> Option<Commitment> {
|
||||
let (keys, account) = self.storage.user_data.get_private_account(account_id)?;
|
||||
Some(Commitment::new(&keys.nullifer_public_key, account))
|
||||
}
|
||||
|
||||
///Poll transactions
|
||||
/// Poll transactions
|
||||
pub async fn poll_native_token_transfer(&self, hash: String) -> Result<NSSATransaction> {
|
||||
let transaction_encoded = self.poller.poll_tx(hash).await?;
|
||||
let tx_base64_decode = BASE64.decode(transaction_encoded)?;
|
||||
@ -181,9 +179,9 @@ impl WalletCore {
|
||||
|
||||
pub async fn check_private_account_initialized(
|
||||
&self,
|
||||
addr: &Address,
|
||||
account_id: &AccountId,
|
||||
) -> Result<Option<MembershipProof>> {
|
||||
if let Some(acc_comm) = self.get_private_account_commitment(addr) {
|
||||
if let Some(acc_comm) = self.get_private_account_commitment(account_id) {
|
||||
self.sequencer_client
|
||||
.get_proof_for_commitment(acc_comm)
|
||||
.await
|
||||
@ -196,9 +194,9 @@ impl WalletCore {
|
||||
pub fn decode_insert_privacy_preserving_transaction_results(
|
||||
&mut self,
|
||||
tx: nssa::privacy_preserving_transaction::PrivacyPreservingTransaction,
|
||||
acc_decode_data: &[(nssa_core::SharedSecretKey, Address)],
|
||||
acc_decode_data: &[(nssa_core::SharedSecretKey, AccountId)],
|
||||
) -> Result<()> {
|
||||
for (output_index, (secret, acc_address)) in acc_decode_data.iter().enumerate() {
|
||||
for (output_index, (secret, acc_account_id)) in acc_decode_data.iter().enumerate() {
|
||||
let acc_ead = tx.message.encrypted_private_post_states[output_index].clone();
|
||||
let acc_comm = tx.message.new_commitments[output_index].clone();
|
||||
|
||||
@ -213,7 +211,7 @@ impl WalletCore {
|
||||
println!("Received new acc {res_acc:#?}");
|
||||
|
||||
self.storage
|
||||
.insert_private_account_data(*acc_address, res_acc);
|
||||
.insert_private_account_data(*acc_account_id, res_acc);
|
||||
}
|
||||
|
||||
println!("Transaction data is {:?}", tx.message);
|
||||
@ -222,23 +220,23 @@ impl WalletCore {
|
||||
}
|
||||
}
|
||||
|
||||
///Represents CLI command for a wallet
|
||||
/// Represents CLI command for a wallet
|
||||
#[derive(Subcommand, Debug, Clone)]
|
||||
#[clap(about)]
|
||||
pub enum Command {
|
||||
///Authenticated transfer subcommand
|
||||
/// Authenticated transfer subcommand
|
||||
#[command(subcommand)]
|
||||
AuthTransfer(AuthTransferSubcommand),
|
||||
///Generic chain info subcommand
|
||||
/// Generic chain info subcommand
|
||||
#[command(subcommand)]
|
||||
ChainInfo(ChainSubcommand),
|
||||
///Account view and sync subcommand
|
||||
/// Account view and sync subcommand
|
||||
#[command(subcommand)]
|
||||
Account(AccountSubcommand),
|
||||
///Pinata program interaction subcommand
|
||||
/// Pinata program interaction subcommand
|
||||
#[command(subcommand)]
|
||||
Pinata(PinataProgramAgnosticSubcommand),
|
||||
///Token program interaction subcommand
|
||||
/// Token program interaction subcommand
|
||||
#[command(subcommand)]
|
||||
Token(TokenProgramAgnosticSubcommand),
|
||||
/// Check the wallet can connect to the node and builtin local programs
|
||||
@ -249,12 +247,14 @@ pub enum Command {
|
||||
Config(ConfigSubcommand),
|
||||
}
|
||||
|
||||
///Represents CLI command for a wallet with setup included
|
||||
/// Represents overarching CLI command for a wallet with setup included
|
||||
#[derive(Debug, Subcommand, Clone)]
|
||||
#[clap(about)]
|
||||
pub enum OverCommand {
|
||||
/// Represents CLI command for a wallet
|
||||
#[command(subcommand)]
|
||||
Command(Command),
|
||||
/// Setup of a storage. Initializes rots for public and private trees from `password`.
|
||||
Setup {
|
||||
#[arg(short, long)]
|
||||
password: String,
|
||||
@ -268,11 +268,11 @@ pub enum OverCommand {
|
||||
},
|
||||
}
|
||||
|
||||
///To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
||||
/// To execute commands, env var NSSA_WALLET_HOME_DIR must be set into directory with config
|
||||
///
|
||||
/// All account adresses must be valid 32 byte base58 strings.
|
||||
///
|
||||
/// All account addresses must be provided as {privacy_prefix}/{addr},
|
||||
/// All account account_ids must be provided as {privacy_prefix}/{account_id},
|
||||
/// where valid options for `privacy_prefix` is `Public` and `Private`
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version, about)]
|
||||
@ -288,7 +288,7 @@ pub struct Args {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SubcommandReturnValue {
|
||||
PrivacyPreservingTransfer { tx_hash: String },
|
||||
RegisterAccount { addr: nssa::Address },
|
||||
RegisterAccount { account_id: nssa::AccountId },
|
||||
Account(nssa::Account),
|
||||
Empty,
|
||||
SyncedToBlock(u64),
|
||||
@ -376,11 +376,8 @@ pub async fn parse_block_range(
|
||||
if let NSSATransaction::PrivacyPreserving(tx) = nssa_tx {
|
||||
let mut affected_accounts = vec![];
|
||||
|
||||
for (acc_addr, (key_chain, _)) in wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.default_user_private_accounts
|
||||
.iter()
|
||||
for (acc_account_id, (key_chain, _)) in
|
||||
&wallet_core.storage.user_data.default_user_private_accounts
|
||||
{
|
||||
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||
key_chain.nullifer_public_key.clone(),
|
||||
@ -408,23 +405,23 @@ pub async fn parse_block_range(
|
||||
|
||||
if let Some(res_acc) = res_acc {
|
||||
println!(
|
||||
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
|
||||
"Received new account for account_id {acc_account_id:#?} with account object {res_acc:#?}"
|
||||
);
|
||||
|
||||
affected_accounts.push((*acc_addr, res_acc));
|
||||
affected_accounts.push((*acc_account_id, res_acc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (_, keys_node) in wallet_core
|
||||
for keys_node in wallet_core
|
||||
.storage
|
||||
.user_data
|
||||
.private_key_tree
|
||||
.key_map
|
||||
.iter()
|
||||
.values()
|
||||
{
|
||||
let acc_addr = keys_node.address();
|
||||
let acc_account_id = keys_node.account_id();
|
||||
let key_chain = &keys_node.value.0;
|
||||
|
||||
let view_tag = EncryptedAccountData::compute_view_tag(
|
||||
@ -453,19 +450,19 @@ pub async fn parse_block_range(
|
||||
|
||||
if let Some(res_acc) = res_acc {
|
||||
println!(
|
||||
"Received new account for addr {acc_addr:#?} with account object {res_acc:#?}"
|
||||
"Received new account for account_id {acc_account_id:#?} with account object {res_acc:#?}"
|
||||
);
|
||||
|
||||
affected_accounts.push((acc_addr, res_acc));
|
||||
affected_accounts.push((acc_account_id, res_acc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (affected_addr, new_acc) in affected_accounts {
|
||||
for (affected_account_id, new_acc) in affected_accounts {
|
||||
wallet_core
|
||||
.storage
|
||||
.insert_private_account_data(affected_addr, new_acc);
|
||||
.insert_private_account_data(affected_account_id, new_acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,10 @@ use wallet::{
|
||||
|
||||
pub const NUM_THREADS: usize = 2;
|
||||
|
||||
// TODO #169: We have sample configs for sequencer, but not for wallet
|
||||
// TODO #168: Why it requires config as a directory? Maybe better to deduce directory from config
|
||||
// file path? TODO #172: Why it requires config as env var while sequencer_runner accepts as
|
||||
// argument? TODO #171: Running pinata doesn't give output about transaction hash and etc.
|
||||
fn main() -> Result<()> {
|
||||
let runtime = Builder::new_multi_thread()
|
||||
.worker_threads(NUM_THREADS)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder;
|
||||
use nssa::{Address, privacy_preserving_transaction::circuit};
|
||||
use nssa::{AccountId, privacy_preserving_transaction::circuit};
|
||||
use nssa_core::{MembershipProof, SharedSecretKey, account::AccountWithMetadata};
|
||||
|
||||
use crate::{
|
||||
@ -10,14 +10,14 @@ use crate::{
|
||||
impl WalletCore {
|
||||
pub async fn claim_pinata(
|
||||
&self,
|
||||
pinata_addr: Address,
|
||||
winner_addr: Address,
|
||||
pinata_account_id: AccountId,
|
||||
winner_account_id: AccountId,
|
||||
solution: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let addresses = vec![pinata_addr, winner_addr];
|
||||
let account_ids = vec![pinata_account_id, winner_account_id];
|
||||
let program_id = nssa::program::Program::pinata().id();
|
||||
let message =
|
||||
nssa::public_transaction::Message::try_new(program_id, addresses, vec![], solution)
|
||||
nssa::public_transaction::Message::try_new(program_id, account_ids, vec![], solution)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
@ -28,8 +28,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn claim_pinata_private_owned_account_already_initialized(
|
||||
&self,
|
||||
pinata_addr: Address,
|
||||
winner_addr: Address,
|
||||
pinata_account_id: AccountId,
|
||||
winner_account_id: AccountId,
|
||||
solution: u128,
|
||||
winner_proof: MembershipProof,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
@ -40,14 +40,14 @@ impl WalletCore {
|
||||
auth_acc: winner_pre,
|
||||
proof: _,
|
||||
} = self
|
||||
.private_acc_preparation(winner_addr, true, false)
|
||||
.private_acc_preparation(winner_account_id, true, false)
|
||||
.await?;
|
||||
|
||||
let pinata_acc = self.get_account_public(pinata_addr).await.unwrap();
|
||||
let pinata_acc = self.get_account_public(pinata_account_id).await.unwrap();
|
||||
|
||||
let program = nssa::program::Program::pinata();
|
||||
|
||||
let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_addr);
|
||||
let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_account_id);
|
||||
|
||||
let eph_holder_winner = EphemeralKeyHolder::new(&winner_npk);
|
||||
let shared_secret_winner = eph_holder_winner.calculate_shared_secret_sender(&winner_ipk);
|
||||
@ -65,7 +65,7 @@ impl WalletCore {
|
||||
|
||||
let message =
|
||||
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output(
|
||||
vec![pinata_addr],
|
||||
vec![pinata_account_id],
|
||||
vec![],
|
||||
vec![(
|
||||
winner_npk.clone(),
|
||||
@ -95,8 +95,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn claim_pinata_private_owned_account_not_initialized(
|
||||
&self,
|
||||
pinata_addr: Address,
|
||||
winner_addr: Address,
|
||||
pinata_account_id: AccountId,
|
||||
winner_account_id: AccountId,
|
||||
solution: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
let AccountPreparedData {
|
||||
@ -106,14 +106,14 @@ impl WalletCore {
|
||||
auth_acc: winner_pre,
|
||||
proof: _,
|
||||
} = self
|
||||
.private_acc_preparation(winner_addr, false, false)
|
||||
.private_acc_preparation(winner_account_id, false, false)
|
||||
.await?;
|
||||
|
||||
let pinata_acc = self.get_account_public(pinata_addr).await.unwrap();
|
||||
let pinata_acc = self.get_account_public(pinata_account_id).await.unwrap();
|
||||
|
||||
let program = nssa::program::Program::pinata();
|
||||
|
||||
let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_addr);
|
||||
let pinata_pre = AccountWithMetadata::new(pinata_acc.clone(), false, pinata_account_id);
|
||||
|
||||
let eph_holder_winner = EphemeralKeyHolder::new(&winner_npk);
|
||||
let shared_secret_winner = eph_holder_winner.calculate_shared_secret_sender(&winner_ipk);
|
||||
@ -131,7 +131,7 @@ impl WalletCore {
|
||||
|
||||
let message =
|
||||
nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output(
|
||||
vec![pinata_addr],
|
||||
vec![pinata_account_id],
|
||||
vec![],
|
||||
vec![(
|
||||
winner_npk.clone(),
|
||||
|
||||
@ -7,10 +7,11 @@ use log::{info, warn};
|
||||
use crate::config::WalletConfig;
|
||||
|
||||
#[derive(Clone)]
|
||||
///Helperstruct to poll transactions
|
||||
/// Helperstruct to poll transactions
|
||||
pub struct TxPoller {
|
||||
pub polling_max_blocks_to_query: usize,
|
||||
pub polling_max_error_attempts: u64,
|
||||
// TODO: This should be Duration
|
||||
pub polling_error_delay_millis: u64,
|
||||
pub polling_delay_millis: u64,
|
||||
pub client: Arc<SequencerClient>,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use nssa::{Account, Address, program::Program};
|
||||
use nssa::{Account, AccountId, program::Program};
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey,
|
||||
program::InstructionData,
|
||||
@ -15,7 +15,8 @@ impl WalletCore {
|
||||
Program,
|
||||
impl FnOnce(&Account, &Account) -> Result<(), ExecutionFailureKind>,
|
||||
) {
|
||||
// Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||
// Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 ||
|
||||
// 0x00 || 0x00 || 0x00].
|
||||
let mut instruction = [0; 23];
|
||||
instruction[0] = 0x01;
|
||||
instruction[1..17].copy_from_slice(&amount.to_le_bytes());
|
||||
@ -47,20 +48,24 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_new_token_definition(
|
||||
&self,
|
||||
definition_address: Address,
|
||||
supply_address: Address,
|
||||
definition_account_id: AccountId,
|
||||
supply_account_id: AccountId,
|
||||
name: [u8; 6],
|
||||
total_supply: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let addresses = vec![definition_address, supply_address];
|
||||
let account_ids = vec![definition_account_id, supply_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
// Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)]
|
||||
let mut instruction = [0; 23];
|
||||
instruction[1..17].copy_from_slice(&total_supply.to_le_bytes());
|
||||
instruction[17..].copy_from_slice(&name);
|
||||
let message =
|
||||
nssa::public_transaction::Message::try_new(program_id, addresses, vec![], instruction)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
vec![],
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]);
|
||||
|
||||
@ -71,8 +76,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_new_token_definition_private_owned(
|
||||
&self,
|
||||
definition_addr: Address,
|
||||
supply_addr: Address,
|
||||
definition_account_id: AccountId,
|
||||
supply_account_id: AccountId,
|
||||
name: [u8; 6],
|
||||
total_supply: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
@ -82,8 +87,8 @@ impl WalletCore {
|
||||
// Kind of non-obvious naming
|
||||
// Basically this funtion is called because authentication mask is [0, 2]
|
||||
self.shielded_two_accs_receiver_uninit(
|
||||
definition_addr,
|
||||
supply_addr,
|
||||
definition_account_id,
|
||||
supply_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -93,27 +98,32 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let addresses = vec![sender_address, recipient_address];
|
||||
let account_ids = vec![sender_account_id, recipient_account_id];
|
||||
let program_id = nssa::program::Program::token().id();
|
||||
// Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00].
|
||||
// Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 ||
|
||||
// 0x00 || 0x00 || 0x00].
|
||||
let mut instruction = [0; 23];
|
||||
instruction[0] = 0x01;
|
||||
instruction[1..17].copy_from_slice(&amount.to_le_bytes());
|
||||
let Ok(nonces) = self.get_accounts_nonces(vec![sender_address]).await else {
|
||||
let Ok(nonces) = self.get_accounts_nonces(vec![sender_account_id]).await else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
let message =
|
||||
nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction)
|
||||
.unwrap();
|
||||
let message = nssa::public_transaction::Message::try_new(
|
||||
program_id,
|
||||
account_ids,
|
||||
nonces,
|
||||
instruction,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let Some(signing_key) = self
|
||||
.storage
|
||||
.user_data
|
||||
.get_pub_account_signing_key(&sender_address)
|
||||
.get_pub_account_signing_key(&sender_account_id)
|
||||
else {
|
||||
return Err(ExecutionFailureKind::KeyNotFoundError);
|
||||
};
|
||||
@ -127,8 +137,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_private_owned_account_already_initialized(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
recipient_proof: MembershipProof,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
@ -136,8 +146,8 @@ impl WalletCore {
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.private_tx_two_accs_all_init(
|
||||
sender_address,
|
||||
recipient_address,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -148,16 +158,16 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_private_owned_account_not_initialized(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.private_tx_two_accs_receiver_uninit(
|
||||
sender_address,
|
||||
recipient_address,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -167,7 +177,7 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_private_foreign_account(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_npk: NullifierPublicKey,
|
||||
recipient_ipk: IncomingViewingPublicKey,
|
||||
amount: u128,
|
||||
@ -176,7 +186,7 @@ impl WalletCore {
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.private_tx_two_accs_receiver_outer(
|
||||
sender_address,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
instruction_data,
|
||||
@ -188,16 +198,16 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_deshielded(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.deshielded_tx_two_accs(
|
||||
sender_address,
|
||||
recipient_address,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -207,8 +217,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_shielded_owned_account_already_initialized(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
recipient_proof: MembershipProof,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
@ -216,8 +226,8 @@ impl WalletCore {
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.shielded_two_accs_all_init(
|
||||
sender_address,
|
||||
recipient_address,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -228,16 +238,16 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_shielded_owned_account_not_initialized(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
recipient_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_account_id: AccountId,
|
||||
amount: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.shielded_two_accs_receiver_uninit(
|
||||
sender_address,
|
||||
recipient_address,
|
||||
sender_account_id,
|
||||
recipient_account_id,
|
||||
instruction_data,
|
||||
tx_pre_check,
|
||||
program,
|
||||
@ -247,7 +257,7 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_transfer_token_transaction_shielded_foreign_account(
|
||||
&self,
|
||||
sender_address: Address,
|
||||
sender_account_id: AccountId,
|
||||
recipient_npk: NullifierPublicKey,
|
||||
recipient_ipk: IncomingViewingPublicKey,
|
||||
amount: u128,
|
||||
@ -256,7 +266,7 @@ impl WalletCore {
|
||||
WalletCore::token_program_preparation_transfer(amount);
|
||||
|
||||
self.shielded_two_accs_receiver_outer(
|
||||
sender_address,
|
||||
sender_account_id,
|
||||
recipient_npk,
|
||||
recipient_ipk,
|
||||
instruction_data,
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
|
||||
use crate::WalletCore;
|
||||
|
||||
impl WalletCore {
|
||||
pub async fn send_deshielded_native_token_transfer(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
) -> Result<(SendTxResponse, [nssa_core::SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey,
|
||||
};
|
||||
@ -9,7 +9,7 @@ use crate::WalletCore;
|
||||
impl WalletCore {
|
||||
pub async fn send_private_native_token_transfer_outer_account(
|
||||
&self,
|
||||
from: Address,
|
||||
from: AccountId,
|
||||
to_npk: NullifierPublicKey,
|
||||
to_ipk: IncomingViewingPublicKey,
|
||||
balance_to_move: u128,
|
||||
@ -30,8 +30,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_private_native_token_transfer_owned_account_not_initialized(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
@ -43,8 +43,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_private_native_token_transfer_owned_account_already_initialized(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
to_proof: MembershipProof,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 2]), ExecutionFailureKind> {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use nssa::{
|
||||
Address, PublicTransaction,
|
||||
AccountId, PublicTransaction,
|
||||
program::Program,
|
||||
public_transaction::{Message, WitnessSet},
|
||||
};
|
||||
@ -10,8 +10,8 @@ use crate::WalletCore;
|
||||
impl WalletCore {
|
||||
pub async fn send_public_native_token_transfer(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let Ok(balance) = self.get_account_balance(from).await else {
|
||||
@ -23,9 +23,10 @@ impl WalletCore {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
|
||||
let addresses = vec![from, to];
|
||||
let account_ids = vec![from, to];
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let message = Message::try_new(program_id, addresses, nonces, balance_to_move).unwrap();
|
||||
let message =
|
||||
Message::try_new(program_id, account_ids, nonces, balance_to_move).unwrap();
|
||||
|
||||
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
|
||||
|
||||
@ -45,16 +46,16 @@ impl WalletCore {
|
||||
|
||||
pub async fn register_account_under_authenticated_transfers_programs(
|
||||
&self,
|
||||
from: Address,
|
||||
from: AccountId,
|
||||
) -> Result<SendTxResponse, ExecutionFailureKind> {
|
||||
let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else {
|
||||
return Err(ExecutionFailureKind::SequencerError);
|
||||
};
|
||||
|
||||
let instruction: u128 = 0;
|
||||
let addresses = vec![from];
|
||||
let account_ids = vec![from];
|
||||
let program_id = Program::authenticated_transfer_program().id();
|
||||
let message = Message::try_new(program_id, addresses, nonces, instruction).unwrap();
|
||||
let message = Message::try_new(program_id, account_ids, nonces, instruction).unwrap();
|
||||
|
||||
let signing_key = self.storage.user_data.get_pub_account_signing_key(&from);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use common::{error::ExecutionFailureKind, sequencer_client::json::SendTxResponse};
|
||||
use nssa::Address;
|
||||
use nssa::AccountId;
|
||||
use nssa_core::{
|
||||
MembershipProof, NullifierPublicKey, SharedSecretKey, encryption::IncomingViewingPublicKey,
|
||||
};
|
||||
@ -9,8 +9,8 @@ use crate::WalletCore;
|
||||
impl WalletCore {
|
||||
pub async fn send_shielded_native_token_transfer_already_initialized(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
to_proof: MembershipProof,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
@ -23,8 +23,8 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_shielded_native_token_transfer_not_initialized(
|
||||
&self,
|
||||
from: Address,
|
||||
to: Address,
|
||||
from: AccountId,
|
||||
to: AccountId,
|
||||
balance_to_move: u128,
|
||||
) -> Result<(SendTxResponse, [SharedSecretKey; 1]), ExecutionFailureKind> {
|
||||
let (instruction_data, program, tx_pre_check) =
|
||||
@ -36,7 +36,7 @@ impl WalletCore {
|
||||
|
||||
pub async fn send_shielded_native_token_transfer_outer_account(
|
||||
&self,
|
||||
from: Address,
|
||||
from: AccountId,
|
||||
to_npk: NullifierPublicKey,
|
||||
to_ipk: IncomingViewingPublicKey,
|
||||
balance_to_move: u128,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user