├── rust-toolchain ├── .dockerignore ├── contrib ├── ansible │ ├── templates │ │ ├── bazuka.env.j2 │ │ └── bazuka.service.j2 │ ├── handlers │ │ └── main.yml │ ├── vars │ │ └── bazuka.yml │ ├── ansible.cfg │ ├── inventory.yml │ ├── vault.yml │ └── deploy.yml └── seeds │ ├── README.md │ └── generate_seeds.py ├── .gitignore ├── debugnet ├── src ├── common │ └── mod.rs ├── config │ ├── mod.rs │ ├── node.rs │ └── blockchain.rs ├── node │ ├── heartbeat │ │ ├── discover_peers.rs │ │ ├── refresh.rs │ │ ├── sync_peers.rs │ │ ├── log_info.rs │ │ ├── sync_mempool.rs │ │ ├── sync_clock.rs │ │ ├── mod.rs │ │ ├── sync_state.rs │ │ └── sync_blocks.rs │ ├── api │ │ ├── shutdown.rs │ │ ├── get_account.rs │ │ ├── get_mpn_account.rs │ │ ├── get_outdated_heights.rs │ │ ├── get_blocks.rs │ │ ├── get_peers.rs │ │ ├── post_block.rs │ │ ├── get_mempool.rs │ │ ├── get_states.rs │ │ ├── get_stats.rs │ │ ├── get_headers.rs │ │ ├── post_peer.rs │ │ ├── transact.rs │ │ ├── get_miner_puzzle.rs │ │ ├── get_zero_mempool.rs │ │ ├── transact_zero.rs │ │ ├── transact_contract_payment.rs │ │ ├── mod.rs │ │ └── post_miner_solution.rs │ ├── seeds.rs │ ├── http.rs │ ├── upnp.rs │ ├── firewall.rs │ ├── context.rs │ └── test │ │ └── simulation.rs ├── lib.rs ├── zk │ ├── poseidon │ │ ├── params │ │ │ ├── README.md │ │ │ ├── mod.rs │ │ │ └── poseidon_params_n255_t2_alpha5_M128.txt │ │ └── mod.rs │ ├── groth16.rs │ └── test │ │ └── mod.rs ├── core │ ├── blocks.rs │ ├── hash.rs │ ├── mod.rs │ ├── header.rs │ ├── address.rs │ ├── money.rs │ └── transaction.rs ├── utils │ └── mod.rs ├── crypto │ ├── mod.rs │ ├── ed25519.rs │ ├── merkle.rs │ └── jubjub │ │ ├── mod.rs │ │ └── curve.rs ├── db │ ├── ram.rs │ ├── keys.rs │ ├── disk.rs │ ├── test.rs │ └── mod.rs ├── consensus │ └── mod.rs ├── client │ ├── error.rs │ └── messages.rs ├── blockchain │ ├── error.rs │ └── test │ │ └── contract.rs └── wallet │ └── mod.rs ├── Dockerfile ├── test.py ├── .github └── workflows │ ├── deploy.yml │ └── actions.yml ├── LICENSE ├── Cargo.toml └── README.md /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | Dockerfile 3 | .dockerignore 4 | .git 5 | .gitignore 6 | -------------------------------------------------------------------------------- /contrib/ansible/templates/bazuka.env.j2: -------------------------------------------------------------------------------- 1 | BAZUKA_DATA_PATH="{{ bazuka.data_path }}" 2 | -------------------------------------------------------------------------------- /contrib/ansible/handlers/main.yml: -------------------------------------------------------------------------------- 1 | - name: reload systemctl 2 | command: systemctl daemon-reload 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | **/.idea 5 | .ansible_vault_password 6 | .ansible_key 7 | -------------------------------------------------------------------------------- /contrib/ansible/vars/bazuka.yml: -------------------------------------------------------------------------------- 1 | bazuka: 2 | installation_path: /var/bazuka 3 | data_path: /var/bazuka/data 4 | -------------------------------------------------------------------------------- /contrib/ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = ./inventory.yml 3 | vault_password_file = ./.ansible_vault_password 4 | -------------------------------------------------------------------------------- /contrib/seeds/README.md: -------------------------------------------------------------------------------- 1 | # Generate seed nodes file 2 | 3 | Python version required: >3.9 4 | 5 | ``` 6 | python3.9 generate_seeds.py seeds.txt 7 | ``` 8 | -------------------------------------------------------------------------------- /contrib/ansible/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | hosts: 3 | ir-1: 4 | ansible_host: 45.156.185.21 5 | ansible_connection: ssh 6 | ansible_user: root 7 | -------------------------------------------------------------------------------- /debugnet: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while : 4 | do 5 | git checkout testnet/debug 6 | git pull origin testnet/debug 7 | timeout --foreground 3600 cargo run --release -- $@ 8 | done 9 | -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | pub const KB: u64 = 1024; 2 | pub const MB: u64 = 1024 * KB; 3 | pub const GB: u64 = 1024 * MB; 4 | 5 | pub const SECOND: u32 = 1000; 6 | pub const MINUTE: u32 = 60 * SECOND; 7 | pub const HOUR: u32 = 60 * MINUTE; 8 | -------------------------------------------------------------------------------- /src/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod blockchain; 2 | 3 | #[cfg(feature = "node")] 4 | pub mod node; 5 | 6 | pub const CODE: &str = "ZIK"; 7 | pub const SYMBOL: &str = "ℤ"; 8 | pub const UNIT_ZEROS: u32 = 9; 9 | pub const UNIT: u64 = 10u64.pow(UNIT_ZEROS); 10 | -------------------------------------------------------------------------------- /src/node/heartbeat/discover_peers.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub async fn discover_peers( 4 | context: &Arc>>, 5 | ) -> Result<(), NodeError> { 6 | let _ctx = context.read().await; 7 | 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /src/node/heartbeat/refresh.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub async fn refresh( 4 | context: &Arc>>, 5 | ) -> Result<(), NodeError> { 6 | let mut ctx = context.write().await; 7 | ctx.refresh()?; 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | pub mod blockchain; 5 | 6 | pub mod common; 7 | pub mod config; 8 | pub mod consensus; 9 | pub mod core; 10 | pub mod crypto; 11 | pub mod db; 12 | pub mod utils; 13 | pub mod wallet; 14 | pub mod zk; 15 | 16 | #[cfg(feature = "node")] 17 | pub mod node; 18 | 19 | #[cfg(feature = "client")] 20 | pub mod client; 21 | -------------------------------------------------------------------------------- /contrib/ansible/vault.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 30333639383632356564336162643565356634323639316531386438663634386534366132333835 3 | 3531336661326634393061383737306532373739323962350a303733663965393961653435653933 4 | 62393561616639393531643639303865633863666336323936393064323531356230303862306239 5 | 3437356361386263330a623661316339356336343130633764373766356632306631333337393839 6 | 3166 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.60.0-buster 2 | 3 | RUN apt update && apt install -y openssl cmake build-essential 4 | 5 | ENV RUSTFLAGS="$RUSTFLAGS -L /usr/lib/" 6 | 7 | RUN cargo new bazuka 8 | WORKDIR /bazuka 9 | COPY ./Cargo.toml ./Cargo.toml 10 | RUN cargo build --release --features node 11 | RUN rm src/*.rs 12 | RUN rm ./target/release/deps/bazuka* 13 | 14 | COPY ./src ./src 15 | 16 | RUN cargo build --release --features node 17 | 18 | CMD ["./target/release/bazuka"] 19 | -------------------------------------------------------------------------------- /src/node/api/shutdown.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{ShutdownRequest, ShutdownResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn shutdown( 8 | context: Arc>>, 9 | _req: ShutdownRequest, 10 | ) -> Result { 11 | context.write().await.shutdown = true; 12 | Ok(ShutdownResponse {}) 13 | } 14 | -------------------------------------------------------------------------------- /src/node/seeds.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | * Please don't update this file manually 3 | * Auto generated by contrib/seeds/generate_seeds.py 4 | * 5 | * Last Modified: 2022-05-20 21:01:32.592805 6 | */ 7 | 8 | use super::PeerAddress; 9 | #[allow(unused_imports)] 10 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; 11 | 12 | pub fn seed_bootstrap_nodes() -> Vec { 13 | vec![ 14 | // --- BEGIN BOOTSTRAP NODES --- 15 | // --- END BOOTSTRAP NODES --- 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /src/node/http.rs: -------------------------------------------------------------------------------- 1 | use super::Peer; 2 | use futures::future::join_all; 3 | 4 | pub async fn group_request( 5 | peers: &[Peer], 6 | f: F, 7 | ) -> Vec<(Peer, ::Output)> 8 | where 9 | F: Fn(Peer) -> R, 10 | R: futures::Future, 11 | { 12 | peers 13 | .iter() 14 | .cloned() 15 | .zip( 16 | join_all(peers.iter().cloned().map(f).collect::>()) 17 | .await 18 | .into_iter(), 19 | ) 20 | .collect() 21 | } 22 | -------------------------------------------------------------------------------- /src/node/api/get_account.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetAccountRequest, GetAccountResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_account( 8 | context: Arc>>, 9 | req: GetAccountRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | Ok(GetAccountResponse { 13 | account: context.blockchain.get_account(req.address.parse()?)?, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /contrib/ansible/templates/bazuka.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Bazuka - zeeka-network blockchain instance 3 | Requires=network.target 4 | After=syslog.target network.target 5 | 6 | [Service] 7 | Type=forking 8 | EnvironmentFile={{ bazuka.installation_path }}/bazuka.env 9 | ExecStart={{ bazuka.installation_path }}/bin/bazuka --listen 0.0.0.0:8090 10 | Restart=always 11 | User=root 12 | PIDFile={{ bazuka.installation_path }}/bazuka.pid 13 | Environment="BAZUKA_PID_FILE_PATH={{ bazuka.installation_path }}/bazuka.pid" 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /src/node/upnp.rs: -------------------------------------------------------------------------------- 1 | use super::NodeError; 2 | use hyper::{Body, Client, Method, Request}; 3 | use std::net::IpAddr; 4 | 5 | pub async fn get_public_ip() -> Result { 6 | let client = Client::new(); 7 | let req = Request::builder() 8 | .method(Method::GET) 9 | .uri("http://ifconfig.io/ip") 10 | .body(Body::empty())?; 11 | let body = client.request(req).await?.into_body(); 12 | let resp_bytes = hyper::body::to_bytes(body).await?; 13 | let resp = std::str::from_utf8(&resp_bytes)?.trim(); 14 | Ok(resp.parse()?) 15 | } 16 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import subprocess 3 | 4 | NUM_NODES = 4 5 | 6 | procs = [ 7 | subprocess.Popen([ 8 | './target/debug/bazuka', 9 | '--db', 'nodes/node{}'.format(i), 10 | '--listen', "127.0.0.1:" + str(3030 + i), 11 | '--external', "127.0.0.1:" + str(3030 + i), 12 | '--bootstrap', "127.0.0.1:3030" 13 | ], stdout=subprocess.PIPE) 14 | for i in range(NUM_NODES) 15 | ] 16 | 17 | while True: 18 | for i, p in enumerate(procs): 19 | state = p.stdout.readline().decode('utf-8') 20 | print(i, ':', state) 21 | -------------------------------------------------------------------------------- /src/node/api/get_mpn_account.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetMpnAccountRequest, GetMpnAccountResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_mpn_account( 8 | context: Arc>>, 9 | req: GetMpnAccountRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | Ok(GetMpnAccountResponse { 13 | account: context.blockchain.get_mpn_account(req.index)?, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/zk/poseidon/params/README.md: -------------------------------------------------------------------------------- 1 | # Poseidon Parameters 2 | 3 | Parameters generated using the tools provided by the reference implementation. 4 | You should have `sagemath` installed on your system. 5 | 6 | Spec: 7 | 8 | * Curve: Bls12-381 9 | * Security: 128bit 10 | * S-box: x^5 11 | * Supported arities: 1 to 16 12 | 13 | ```sh 14 | git clone https://extgit.iaik.tugraz.at/krypto/hadeshash.git 15 | cd hadeshash/code 16 | for i in {2..17} 17 | do 18 | sage generate_params_poseidon.sage 1 0 255 $i 5 128 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 19 | done 20 | ``` 21 | -------------------------------------------------------------------------------- /src/node/api/get_outdated_heights.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetOutdatedHeightsRequest, GetOutdatedHeightsResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_outdated_heights( 8 | context: Arc>>, 9 | _req: GetOutdatedHeightsRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | Ok(GetOutdatedHeightsResponse { 13 | outdated_heights: context.blockchain.get_outdated_heights()?, 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/node/api/get_blocks.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetBlocksRequest, GetBlocksResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_blocks( 8 | context: Arc>>, 9 | req: GetBlocksRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | let count = std::cmp::min(context.opts.max_blocks_fetch, req.count); 13 | Ok(GetBlocksResponse { 14 | blocks: context.blockchain.get_blocks(req.since, count)?, 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/node/api/get_peers.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetPeersRequest, GetPeersResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_peers( 8 | context: Arc>>, 9 | _req: GetPeersRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | let num_peers = context.opts.num_peers; 13 | let random_peers = context.random_peers(&mut rand::thread_rng(), num_peers); 14 | Ok(GetPeersResponse { 15 | peers: random_peers, 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/node/api/post_block.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{PostBlockRequest, PostBlockResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn post_block( 8 | context: Arc>>, 9 | req: PostBlockRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | context 13 | .blockchain 14 | .extend(req.block.header.number, &[req.block])?; 15 | context.outdated_since = None; 16 | context.blockchain.update_states(&req.patch)?; 17 | Ok(PostBlockResponse {}) 18 | } 19 | -------------------------------------------------------------------------------- /src/core/blocks.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::crypto::merkle::MerkleTree; 4 | use crate::crypto::{SignatureScheme, ZkSignatureScheme}; 5 | 6 | use super::hash::Hash; 7 | use super::header::Header; 8 | use super::transaction::Transaction; 9 | 10 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 11 | pub struct Block { 12 | pub header: Header, 13 | pub body: Vec>, 14 | } 15 | 16 | impl Block { 17 | pub fn merkle_tree(&self) -> MerkleTree { 18 | MerkleTree::::new(self.body.iter().map(|tx| tx.hash()).collect()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/node/api/get_mempool.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetMempoolRequest, GetMempoolResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_mempool( 8 | context: Arc>>, 9 | _req: GetMempoolRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | context.refresh()?; 13 | Ok(GetMempoolResponse { 14 | tx: context.mempool.clone().into_keys().collect(), 15 | tx_zk: context 16 | .contract_payment_mempool 17 | .clone() 18 | .into_keys() 19 | .collect(), 20 | zk: context.zero_mempool.clone().into_keys().collect(), 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/node/api/get_states.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetStatesRequest, GetStatesResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use crate::core::{hash::Hash, Hasher}; 5 | use std::sync::Arc; 6 | use tokio::sync::RwLock; 7 | 8 | pub async fn get_states( 9 | context: Arc>>, 10 | req: GetStatesRequest, 11 | ) -> Result { 12 | let context = context.read().await; 13 | let to = 14 | ::Output::try_from(hex::decode(req.to).map_err(|_| NodeError::InputError)?) 15 | .map_err(|_| NodeError::InputError)?; 16 | let patch = context 17 | .blockchain 18 | .generate_state_patch(req.outdated_heights, to)?; 19 | Ok(GetStatesResponse { patch }) 20 | } 21 | -------------------------------------------------------------------------------- /src/node/api/get_stats.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetStatsRequest, GetStatsResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_stats( 8 | context: Arc>>, 9 | _req: GetStatsRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | Ok(GetStatsResponse { 13 | social_profiles: context.social_profiles.clone(), 14 | height: context.blockchain.get_height()?, 15 | power: context.blockchain.get_power()?, 16 | next_reward: context.blockchain.next_reward()?, 17 | timestamp: context.network_timestamp(), 18 | version: env!("CARGO_PKG_VERSION").into(), 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /src/node/api/get_headers.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetHeadersRequest, GetHeadersResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::{Blockchain, BlockchainError}; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_headers( 8 | context: Arc>>, 9 | req: GetHeadersRequest, 10 | ) -> Result { 11 | let context = context.read().await; 12 | let count = std::cmp::min(context.opts.max_blocks_fetch, req.count); 13 | let headers = context.blockchain.get_headers(req.since, count)?; 14 | let pow_keys = headers 15 | .iter() 16 | .map(|h| context.blockchain.pow_key(h.number)) 17 | .collect::>, BlockchainError>>()?; 18 | Ok(GetHeadersResponse { headers, pow_keys }) 19 | } 20 | -------------------------------------------------------------------------------- /src/core/hash.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | use serde::{Deserialize, Serialize}; 3 | use sha3::{Digest, Sha3_256}; 4 | use std::fmt::Debug; 5 | 6 | pub trait Hash: Debug + Clone + Serialize + 'static { 7 | type Output: Debug 8 | + Serialize 9 | + DeserializeOwned 10 | + AsRef<[u8]> 11 | + AsMut<[u8]> 12 | + Default 13 | + Copy 14 | + PartialOrd 15 | + TryFrom>; 16 | 17 | fn hash(s: &[u8]) -> Self::Output; 18 | } 19 | 20 | #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, Copy, Eq, std::hash::Hash)] 21 | pub struct Sha3Hasher; 22 | 23 | impl Hash for Sha3Hasher { 24 | type Output = [u8; 32]; 25 | 26 | fn hash(s: &[u8]) -> Self::Output { 27 | let mut h = Sha3_256::new(); 28 | h.update(s); 29 | h.finalize().into() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/node/api/post_peer.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{PostPeerRequest, PostPeerResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use crate::client::Peer; 5 | use std::sync::Arc; 6 | use tokio::sync::RwLock; 7 | 8 | pub async fn post_peer( 9 | context: Arc>>, 10 | req: PostPeerRequest, 11 | ) -> Result { 12 | let mut context = context.write().await; 13 | context 14 | .peers 15 | .entry(req.address) 16 | .and_modify(|s| { 17 | s.info = Some(req.info.clone()); 18 | }) 19 | .or_insert(Peer { 20 | pub_key: None, 21 | address: req.address, 22 | info: Some(req.info), 23 | }); 24 | Ok(PostPeerResponse { 25 | info: context.get_info()?, 26 | timestamp: context.network_timestamp(), 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /src/node/api/transact.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{TransactRequest, TransactResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::{Blockchain, TransactionStats}; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn transact( 8 | context: Arc>>, 9 | req: TransactRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | let now = context.network_timestamp(); 13 | // Prevent spamming mempool 14 | match context.blockchain.validate_transaction(&req.tx_delta) { 15 | Ok(_) => { 16 | context 17 | .mempool 18 | .insert(req.tx_delta, TransactionStats { first_seen: now }); 19 | } 20 | Err(e) => { 21 | log::warn!("Rejected transaction. Error: {}", e); 22 | } 23 | } 24 | Ok(TransactResponse {}) 25 | } 26 | -------------------------------------------------------------------------------- /src/node/api/get_miner_puzzle.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetMinerPuzzleRequest, GetMinerPuzzleResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_miner_puzzle( 8 | context: Arc>>, 9 | _req: GetMinerPuzzleRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | if let Some((_, puzzle)) = context.miner_puzzle.as_ref() { 13 | Ok(GetMinerPuzzleResponse { 14 | puzzle: Some(puzzle.clone()), 15 | }) 16 | } else { 17 | let wallet = context.wallet.clone().ok_or(NodeError::NoWalletError)?; 18 | context.miner_puzzle = context.get_puzzle(wallet)?; 19 | Ok(GetMinerPuzzleResponse { 20 | puzzle: context.miner_puzzle.as_ref().map(|(_, pzl)| pzl.clone()), 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/node/api/get_zero_mempool.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{GetZeroMempoolRequest, GetZeroMempoolResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::Blockchain; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn get_zero_mempool( 8 | context: Arc>>, 9 | _req: GetZeroMempoolRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | if context.blockchain.get_outdated_heights()?.len() > 0 { 13 | Err(NodeError::StatesOutdated) 14 | } else { 15 | context.refresh()?; 16 | Ok(GetZeroMempoolResponse { 17 | updates: context.zero_mempool.clone().into_keys().collect(), 18 | payments: context 19 | .contract_payment_mempool 20 | .clone() 21 | .into_keys() 22 | .collect(), 23 | }) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/node/api/transact_zero.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{TransactZeroRequest, TransactZeroResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::{Blockchain, TransactionStats}; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn transact_zero( 8 | context: Arc>>, 9 | req: TransactZeroRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | let now = context.network_timestamp(); 13 | // Prevent spamming mempool 14 | match context.blockchain.validate_zero_transaction(&req.tx) { 15 | Ok(_) => { 16 | context 17 | .zero_mempool 18 | .insert(req.tx, TransactionStats { first_seen: now }); 19 | } 20 | Err(e) => { 21 | log::warn!("Rejected zero-transaction. Error: {}", e); 22 | } 23 | } 24 | Ok(TransactZeroResponse {}) 25 | } 26 | -------------------------------------------------------------------------------- /src/node/api/transact_contract_payment.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{TransactContractPaymentRequest, TransactContractPaymentResponse}; 2 | use super::{NodeContext, NodeError}; 3 | use crate::blockchain::{Blockchain, TransactionStats}; 4 | use std::sync::Arc; 5 | use tokio::sync::RwLock; 6 | 7 | pub async fn transact_contract_payment( 8 | context: Arc>>, 9 | req: TransactContractPaymentRequest, 10 | ) -> Result { 11 | let mut context = context.write().await; 12 | let now = context.network_timestamp(); 13 | // Prevent spamming mempool 14 | match context.blockchain.validate_contract_payment(&req.tx) { 15 | Ok(_) => { 16 | context 17 | .contract_payment_mempool 18 | .insert(req.tx, TransactionStats { first_seen: now }); 19 | } 20 | Err(e) => { 21 | log::warn!("Rejected contract payment. Error: {}", e); 22 | } 23 | } 24 | Ok(TransactContractPaymentResponse {}) 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deployment 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | git-ref: 7 | description: Git Ref (Optional) 8 | required: false 9 | 10 | jobs: 11 | deployment: 12 | runs-on: ubuntu-20.04 13 | environment: production 14 | concurrency: production 15 | steps: 16 | - name: Clone Repository (Latest) 17 | uses: actions/checkout@v3 18 | if: github.event.inputs.git-ref == '' 19 | - name: Clone Repository (Custom Ref) 20 | uses: actions/checkout@v3 21 | if: github.event.inputs.git-ref != '' 22 | with: 23 | ref: ${{ github.event.inputs.git-ref }} 24 | - name: Build binary 25 | run: cargo build --release 26 | - name: Run deploy branch playbook 27 | uses: dawidd6/action-ansible-playbook@v2 28 | with: 29 | playbook: deploy.yml 30 | directory: ./contrib/ansible 31 | key: ${{secrets.SSH_PRIVATE_KEY}} 32 | vault_password: ${{secrets.VAULT_PASSWORD}} 33 | options: | 34 | --verbose 35 | -------------------------------------------------------------------------------- /src/node/api/mod.rs: -------------------------------------------------------------------------------- 1 | use super::{http, Limit, NodeContext, NodeError}; 2 | 3 | use crate::client::messages; 4 | 5 | mod get_stats; 6 | pub use get_stats::*; 7 | mod get_peers; 8 | pub use get_peers::*; 9 | mod post_peer; 10 | pub use post_peer::*; 11 | mod post_block; 12 | pub use post_block::*; 13 | mod get_blocks; 14 | pub use get_blocks::*; 15 | mod get_states; 16 | pub use get_states::*; 17 | mod get_outdated_heights; 18 | pub use get_outdated_heights::*; 19 | mod get_headers; 20 | pub use get_headers::*; 21 | mod transact; 22 | pub use transact::*; 23 | mod transact_zero; 24 | pub use transact_zero::*; 25 | mod transact_contract_payment; 26 | pub use transact_contract_payment::*; 27 | mod shutdown; 28 | pub use shutdown::*; 29 | mod get_zero_mempool; 30 | pub use get_zero_mempool::*; 31 | mod get_miner_puzzle; 32 | pub use get_miner_puzzle::*; 33 | mod post_miner_solution; 34 | pub use post_miner_solution::*; 35 | mod get_account; 36 | pub use get_account::*; 37 | mod get_mpn_account; 38 | pub use get_mpn_account::*; 39 | mod get_mempool; 40 | pub use get_mempool::*; 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Zeeka Network 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contrib/ansible/deploy.yml: -------------------------------------------------------------------------------- 1 | - name: run bazuka from branch 2 | hosts: all 3 | gather_facts: false 4 | vars_files: 5 | - vars/bazuka.yml 6 | 7 | tasks: 8 | - name: create bazuka directory 9 | file: 10 | path: '{{ bazuka.installation_path }}/bin/' 11 | state: directory 12 | - name: create bazuka data directory 13 | file: 14 | path: '{{ bazuka.installation_path }}/data/' 15 | state: directory 16 | - name: copy bazuka to remote 17 | copy: 18 | src: ../target/release/bazuka 19 | dest: '{{ bazuka.installation_path }}/bin/bazuka' 20 | mode: a+x 21 | - name: copy service environment 22 | template: 23 | src: bazuka.env.j2 24 | dest: '{{ bazuka.installation_path }}/bazuka.env' 25 | mode: 644 26 | - name: install service 27 | template: 28 | src: bazuka.service.j2 29 | dest: /lib/systemd/system/bazuka.service 30 | mode: 644 31 | notify: 32 | - reload systemctl 33 | - name: start bazuka 34 | service: 35 | name: bazuka.service 36 | state: started 37 | enabled: yes 38 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::core::ProofOfWork; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | 4 | pub fn local_timestamp() -> u32 { 5 | SystemTime::now() 6 | .duration_since(UNIX_EPOCH) 7 | .expect("Time went backwards") 8 | .as_secs() as u32 9 | } 10 | 11 | pub fn median(inps: &[T]) -> T { 12 | let mut sorted = inps.to_vec(); 13 | sorted.sort(); 14 | sorted[sorted.len() / 2].clone() 15 | } 16 | 17 | // TODO: Move to consensus folder 18 | pub fn calc_pow_difficulty( 19 | diff_calc_interval: u64, 20 | block_time: usize, 21 | min_diff: crate::consensus::pow::Difficulty, 22 | last_pow: &ProofOfWork, 23 | prev_pow: &ProofOfWork, 24 | ) -> crate::consensus::pow::Difficulty { 25 | let time_delta = last_pow.timestamp - prev_pow.timestamp; 26 | let avg_block_time = time_delta / (diff_calc_interval - 1) as u32; 27 | let diff_change = (block_time as f32 / avg_block_time as f32).clamp(0.5f32, 2f32); 28 | let new_diff = rust_randomx::Difficulty::new(last_pow.target.0).scale(diff_change); 29 | std::cmp::max( 30 | crate::consensus::pow::Difficulty(new_diff.to_u32()), 31 | min_diff, 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display}; 2 | use std::str::FromStr; 3 | 4 | use crate::zk; 5 | use serde::de::DeserializeOwned; 6 | use serde::Serialize; 7 | 8 | pub mod merkle; 9 | 10 | pub mod ed25519; 11 | pub mod jubjub; 12 | 13 | pub trait SignatureScheme: Clone + Serialize { 14 | type Pub: Clone 15 | + Debug 16 | + PartialEq 17 | + Serialize 18 | + DeserializeOwned 19 | + FromStr 20 | + Display 21 | + From; 22 | type Priv: Clone; 23 | type Sig: Clone + Debug + PartialEq + Serialize + DeserializeOwned; 24 | fn generate_keys(seed: &[u8]) -> (Self::Pub, Self::Priv); 25 | fn sign(sk: &Self::Priv, msg: &[u8]) -> Self::Sig; 26 | fn verify(pk: &Self::Pub, msg: &[u8], sig: &Self::Sig) -> bool; 27 | } 28 | 29 | pub trait ZkSignatureScheme: Clone + Serialize { 30 | type Pub: Clone + Debug + PartialEq + Serialize + DeserializeOwned; 31 | type Priv: Clone; 32 | type Sig: Clone + Debug + PartialEq + Serialize + DeserializeOwned; 33 | fn generate_keys(seed: &[u8]) -> (Self::Pub, Self::Priv); 34 | fn sign(sk: &Self::Priv, msg: zk::ZkScalar) -> Self::Sig; 35 | fn verify(pk: &Self::Pub, msg: zk::ZkScalar, sig: &Self::Sig) -> bool; 36 | } 37 | -------------------------------------------------------------------------------- /src/db/ram.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::collections::HashMap; 3 | 4 | pub struct RamKvStore(HashMap); 5 | impl RamKvStore { 6 | pub fn new() -> RamKvStore { 7 | RamKvStore(HashMap::new()) 8 | } 9 | } 10 | 11 | impl Default for RamKvStore { 12 | fn default() -> Self { 13 | Self::new() 14 | } 15 | } 16 | 17 | impl KvStore for RamKvStore { 18 | fn get(&self, k: StringKey) -> Result, KvStoreError> { 19 | Ok(self.0.get(&k.0).cloned()) 20 | } 21 | fn update(&mut self, ops: &[WriteOp]) -> Result<(), KvStoreError> { 22 | for op in ops.iter() { 23 | match op { 24 | WriteOp::Remove(k) => self.0.remove(&k.0), 25 | WriteOp::Put(k, v) => self.0.insert(k.0.clone(), v.clone()), 26 | }; 27 | } 28 | Ok(()) 29 | } 30 | fn pairs(&self, prefix: StringKey) -> Result, KvStoreError> { 31 | Ok(self 32 | .0 33 | .clone() 34 | .into_iter() 35 | .filter_map(|(k, v)| { 36 | if k.starts_with(&prefix.0) { 37 | Some((StringKey::new(&k), v)) 38 | } else { 39 | None 40 | } 41 | }) 42 | .collect()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod blocks; 3 | pub mod hash; 4 | mod header; 5 | mod money; 6 | mod transaction; 7 | 8 | use crate::crypto; 9 | 10 | pub use money::Money; 11 | 12 | pub type Hasher = hash::Sha3Hasher; 13 | pub type Signer = crypto::ed25519::Ed25519; 14 | 15 | pub type ZkHasher = crate::zk::PoseidonHasher; 16 | pub type ZkSigner = crypto::jubjub::JubJub; 17 | 18 | pub type Address = address::Address; 19 | pub type ParseAddressError = address::ParseAddressError; 20 | pub type Account = address::Account; 21 | pub type Signature = address::Signature; 22 | pub type Transaction = transaction::Transaction; 23 | pub type TransactionData = transaction::TransactionData; 24 | pub type ContractAccount = transaction::ContractAccount; 25 | pub type ContractUpdate = transaction::ContractUpdate; 26 | pub type ContractPayment = transaction::ContractPayment; 27 | pub type PaymentDirection = transaction::PaymentDirection; 28 | pub type Header = header::Header; 29 | pub type Block = blocks::Block; 30 | 31 | pub type ProofOfWork = header::ProofOfWork; 32 | pub type ContractId = transaction::ContractId; 33 | 34 | pub type TransactionAndDelta = transaction::TransactionAndDelta; 35 | -------------------------------------------------------------------------------- /src/config/node.rs: -------------------------------------------------------------------------------- 1 | use crate::common::*; 2 | use crate::node::NodeOptions; 3 | use std::time::Duration; 4 | 5 | pub fn get_node_options() -> NodeOptions { 6 | NodeOptions { 7 | tx_max_time_alive: Some(600), 8 | heartbeat_interval: Duration::from_secs(5), 9 | num_peers: 8, 10 | max_blocks_fetch: 16, 11 | default_punish: 60, 12 | no_response_punish: 600, 13 | invalid_data_punish: 3600, 14 | incorrect_power_punish: 3600, 15 | max_punish: 7200, 16 | outdated_heights_threshold: 15, 17 | state_unavailable_ban_time: 30, 18 | ip_request_limit_per_minute: 60, 19 | traffic_limit_per_15m: 4 * GB, 20 | unresponsive_count_limit_per_15m: 3, 21 | } 22 | } 23 | 24 | pub fn get_simulator_options() -> NodeOptions { 25 | NodeOptions { 26 | tx_max_time_alive: None, 27 | heartbeat_interval: Duration::from_millis(300), 28 | num_peers: 8, 29 | max_blocks_fetch: 16, 30 | default_punish: 0, 31 | no_response_punish: 0, 32 | invalid_data_punish: 0, 33 | incorrect_power_punish: 0, 34 | max_punish: 0, 35 | outdated_heights_threshold: 5, 36 | state_unavailable_ban_time: 10, 37 | ip_request_limit_per_minute: 6000, 38 | traffic_limit_per_15m: 4 * GB, 39 | unresponsive_count_limit_per_15m: usize::MAX, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: Bazuka 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-20.04 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Build binary 15 | run: cargo build --release 16 | - name: Archive binary artifacts 17 | uses: actions/upload-artifact@v3 18 | with: 19 | name: bin-x86_64-unknown-linux-gnu 20 | path: ./target/release/bazuka 21 | test: 22 | runs-on: ubuntu-20.04 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Check fmt 26 | run: cargo fmt --check 27 | - name: Check clippy 28 | run: cargo clippy 29 | - name: Run tests 30 | run: cargo test --release 31 | reports: 32 | name: coverage 33 | runs-on: ubuntu-latest 34 | container: 35 | image: xd009642/tarpaulin:latest 36 | options: --security-opt seccomp=unconfined 37 | steps: 38 | - name: Checkout repository 39 | uses: actions/checkout@v3 40 | - name: Install requirements 41 | run: | 42 | apt update -y 43 | apt install -y libssl-dev cmake 44 | - name: Generate code coverage 45 | run: | 46 | cargo tarpaulin --verbose --all-features --workspace --timeout 120 --out Xml 47 | - name: Upload to codecov.io 48 | uses: codecov/codecov-action@v2 49 | -------------------------------------------------------------------------------- /src/core/header.rs: -------------------------------------------------------------------------------- 1 | use crate::consensus::pow::Difficulty; 2 | 3 | use super::hash::Hash; 4 | 5 | #[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize, Hash)] 6 | pub struct ProofOfWork { 7 | /// when the miner started mining this block 8 | pub timestamp: u32, 9 | /// difficulty target 10 | pub target: Difficulty, 11 | /// arbitrary data 12 | pub nonce: u64, 13 | } 14 | 15 | #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, Hash)] 16 | pub struct Header { 17 | /// the parent hash 18 | pub parent_hash: H::Output, 19 | /// block number or block height 20 | pub number: u64, 21 | /// the merkle root of current block 22 | pub block_root: H::Output, 23 | /// aux data for Proof-of-Work consensus 24 | pub proof_of_work: ProofOfWork, 25 | } 26 | 27 | impl Eq for Header {} 28 | 29 | impl Header { 30 | pub fn hash(&self) -> H::Output { 31 | H::hash(&bincode::serialize(&self).expect("convert header to bincode format")) 32 | } 33 | 34 | // Approximate number of hashes run in order to generate this block 35 | pub fn power(&self) -> u128 { 36 | self.proof_of_work.target.power() 37 | } 38 | 39 | pub fn meets_target(&self, key: &[u8]) -> bool { 40 | let bin = bincode::serialize(&self).expect("convert header to bincode format"); 41 | crate::consensus::pow::meets_difficulty(key, &bin, self.proof_of_work.target) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bazuka" 3 | version = "0.1.0" 4 | authors = ["Keyvan Kambakhsh "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | log = "0.4" 9 | env_logger = "0.9.0" 10 | lazy_static = "1.4" 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_derive = "1.0" 13 | serde_json = "1.0" 14 | serde_bytes = "0.11.5" 15 | serde_qs = "0.8" 16 | home = "0.5.3" 17 | db-key = "0.0.5" 18 | ff = { version = "0.12", features = ["derive", "derive_bits"] } 19 | sha3 = "0.10.0" 20 | bincode = "1.3.3" 21 | rand = "0.7.0" 22 | thiserror = "1.0" 23 | num-bigint = "0.4" 24 | num-integer = "0.1" 25 | num-traits = "0.2" 26 | hex = "0.4.3" 27 | colored = "2.0.0" 28 | rust-randomx = "0.6.0" 29 | bellman = "0.13.0" 30 | bls12_381 = "0.7.0" 31 | ed25519-dalek = { version = "1", features = ["serde"] } 32 | rayon = "1.5.3" 33 | 34 | # Node related deps 35 | tokio = { version = "1", features = ["full"], optional = true } 36 | hyper = { version = "0.14", features = ["full"], optional = true } 37 | futures = { version = "0.3", optional = true } 38 | leveldb = { version = "0.8.6", optional = true } 39 | structopt = { version = "0.3", default-features = false, optional = true } 40 | async-trait = { version = "0.1.53", optional = true } 41 | serde_yaml = { version = "0.8", optional = true } 42 | tempdir = { version = "0.3.7", optional = true } 43 | 44 | [features] 45 | default = ["node"] 46 | db = ["leveldb", "tempdir"] 47 | client = ["tokio", "hyper", "futures", "structopt", "serde_yaml"] 48 | node = ["client", "db", "async-trait"] 49 | -------------------------------------------------------------------------------- /src/consensus/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pow { 2 | use rust_randomx::{Context, Hasher}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::cmp::Ordering; 5 | use std::collections::HashMap; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] 9 | pub struct Difficulty(pub u32); 10 | 11 | impl Difficulty { 12 | pub fn powerf(&self) -> f64 { 13 | rust_randomx::Difficulty::new(self.0).power() 14 | } 15 | pub fn power(&self) -> u128 { 16 | self.powerf() as u128 17 | } 18 | } 19 | 20 | impl Ord for Difficulty { 21 | fn cmp(&self, other: &Self) -> Ordering { 22 | self.powerf().partial_cmp(&other.powerf()).unwrap() 23 | } 24 | } 25 | 26 | impl PartialOrd for Difficulty { 27 | fn partial_cmp(&self, other: &Self) -> Option { 28 | Some(self.cmp(other)) 29 | } 30 | } 31 | 32 | lazy_static! { 33 | pub static ref HASHER: Arc, Hasher>>> = 34 | Arc::new(Mutex::new(HashMap::new())); 35 | } 36 | 37 | pub fn meets_difficulty(key: &[u8], input: &[u8], diff: Difficulty) -> bool { 38 | let mut hasher = HASHER.lock().unwrap(); 39 | 40 | #[cfg(not(test))] 41 | hasher.retain(|k, _| k == &key); 42 | 43 | let key = key.to_vec(); 44 | hasher 45 | .entry(key.clone()) 46 | .or_insert_with(|| Hasher::new(Arc::new(Context::new(&key, false)))) 47 | .hash(input) 48 | .meets_difficulty(rust_randomx::Difficulty::new(diff.0)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/node/heartbeat/sync_peers.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::common::*; 3 | use rand::prelude::IteratorRandom; 4 | use rand::rngs::OsRng; 5 | 6 | pub async fn sync_peers( 7 | context: &Arc>>, 8 | ) -> Result<(), NodeError> { 9 | let ctx = context.read().await; 10 | 11 | let net = ctx.outgoing.clone(); 12 | let opts = ctx.opts.clone(); 13 | 14 | let peer_addresses = ctx.random_peers(&mut rand::thread_rng(), opts.num_peers); 15 | drop(ctx); 16 | 17 | log::info!("Syncing peers with: {:?}", peer_addresses); 18 | let peer_responses: Vec<(Peer, Result)> = 19 | http::group_request(&peer_addresses, |peer| { 20 | net.json_get::( 21 | format!("{}/peers", peer.address), 22 | GetPeersRequest {}, 23 | Limit::default().size(1 * MB).time(3 * SECOND), 24 | ) 25 | }) 26 | .await; 27 | 28 | { 29 | let mut ctx = context.write().await; 30 | let resps = punish_non_responding(&mut ctx, &peer_responses) 31 | .into_iter() 32 | .map(|(_, r)| r.peers) 33 | .collect::>(); 34 | for peers in resps { 35 | for p in peers 36 | .into_iter() 37 | .choose_multiple(&mut OsRng, ctx.opts.num_peers) 38 | .into_iter() 39 | { 40 | ctx.peers.entry(p.address).or_insert(Peer { 41 | pub_key: None, 42 | address: p.address, 43 | info: None, 44 | }); 45 | } 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /src/node/heartbeat/log_info.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use colored::Colorize; 4 | 5 | pub async fn log_info( 6 | context: &Arc>>, 7 | ) -> Result<(), NodeError> { 8 | let ctx = context.read().await; 9 | let mut inf = Vec::new(); 10 | inf.extend([ 11 | ("Height", ctx.blockchain.get_height()?.to_string()), 12 | ( 13 | "Outdated states", 14 | ctx.blockchain.get_outdated_contracts()?.len().to_string(), 15 | ), 16 | ("Timestamp", ctx.network_timestamp().to_string()), 17 | ("Active peers", ctx.active_peers().len().to_string()), 18 | ]); 19 | 20 | inf.push(("Power", ctx.blockchain.get_power()?.to_string())); 21 | 22 | inf.push(("Tx Pool", ctx.mempool.len().to_string())); 23 | inf.push(("Tx/Zk Pool", ctx.contract_payment_mempool.len().to_string())); 24 | inf.push(("Zk Pool", ctx.zero_mempool.len().to_string())); 25 | 26 | if let Some(wallet) = ctx.wallet.clone() { 27 | let acc = ctx.blockchain.get_account(wallet.get_address())?; 28 | inf.push(("Balance", acc.balance.to_string())); 29 | } 30 | 31 | // TODO: Embed MPN in test environment 32 | #[cfg(not(test))] 33 | { 34 | let mpn_contract_id = ctx.blockchain.config().mpn_contract_id; 35 | let mpn_account = ctx.blockchain.get_contract_account(mpn_contract_id)?; 36 | inf.push(("MPN Height", mpn_account.height.to_string())); 37 | inf.push(("MPN Balance", mpn_account.balance.to_string())); 38 | inf.push(( 39 | "MPN Size", 40 | mpn_account.compressed_state.state_size.to_string(), 41 | )); 42 | } 43 | 44 | println!( 45 | "{}", 46 | inf.into_iter() 47 | .map(|(k, v)| format!("{}: {}", k.bright_blue(), v)) 48 | .collect::>() 49 | .join(" ") 50 | ); 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /src/node/heartbeat/sync_mempool.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::blockchain::TransactionStats; 3 | use crate::common::*; 4 | 5 | pub async fn sync_mempool( 6 | context: &Arc>>, 7 | ) -> Result<(), NodeError> { 8 | let ctx = context.read().await; 9 | 10 | let net = ctx.outgoing.clone(); 11 | let opts = ctx.opts.clone(); 12 | 13 | let peer_addresses = ctx.random_peers(&mut rand::thread_rng(), opts.num_peers); 14 | drop(ctx); 15 | 16 | let peer_responses: Vec<(Peer, Result)> = 17 | http::group_request(&peer_addresses, |peer| { 18 | net.bincode_get::( 19 | format!("{}/bincode/mempool", peer.address), 20 | GetMempoolRequest {}, 21 | Limit::default().size(10 * MB).time(10 * SECOND), 22 | ) 23 | }) 24 | .await; 25 | 26 | { 27 | let mut ctx = context.write().await; 28 | let now = ctx.network_timestamp(); 29 | let resps = punish_non_responding(&mut ctx, &peer_responses) 30 | .into_iter() 31 | .map(|(_, r)| (r.tx, r.tx_zk, r.zk)) 32 | .collect::>(); 33 | for (tx_s, tx_zk_s, zk_s) in resps { 34 | for tx in tx_s { 35 | ctx.mempool 36 | .entry(tx) 37 | .or_insert(TransactionStats { first_seen: now }); 38 | } 39 | for tx in tx_zk_s { 40 | ctx.contract_payment_mempool 41 | .entry(tx) 42 | .or_insert(TransactionStats { first_seen: now }); 43 | } 44 | for tx in zk_s { 45 | ctx.zero_mempool 46 | .entry(tx) 47 | .or_insert(TransactionStats { first_seen: now }); 48 | } 49 | } 50 | } 51 | 52 | Ok(()) 53 | } 54 | -------------------------------------------------------------------------------- /src/node/api/post_miner_solution.rs: -------------------------------------------------------------------------------- 1 | use super::messages::{ 2 | PostBlockRequest, PostBlockResponse, PostMinerSolutionRequest, PostMinerSolutionResponse, 3 | }; 4 | use super::{http, Limit, NodeContext, NodeError}; 5 | use crate::blockchain::Blockchain; 6 | use crate::common::*; 7 | use std::sync::Arc; 8 | use tokio::sync::RwLock; 9 | 10 | pub async fn post_miner_solution( 11 | context: Arc>>, 12 | req: PostMinerSolutionRequest, 13 | ) -> Result { 14 | let mut context = context.write().await; 15 | let net = context.outgoing.clone(); 16 | 17 | let mut nonce_bytes = [0u8; 8]; 18 | nonce_bytes.copy_from_slice(&hex::decode(req.nonce).unwrap()); 19 | let (mut draft, _) = context 20 | .miner_puzzle 21 | .as_ref() 22 | .ok_or(NodeError::NoCurrentlyMiningBlockError)? 23 | .clone(); 24 | draft.block.header.proof_of_work.nonce = u64::from_le_bytes(nonce_bytes); 25 | if context 26 | .blockchain 27 | .extend(draft.block.header.number, &[draft.block.clone()]) 28 | .is_ok() 29 | { 30 | context.outdated_since = None; 31 | let _ = context.blockchain.update_states(&draft.patch.clone()); 32 | 33 | let peer_addresses = context.random_peers(&mut rand::thread_rng(), context.opts.num_peers); 34 | http::group_request(&peer_addresses, |peer| { 35 | net.bincode_post::( 36 | format!("{}/bincode/blocks", peer.address), 37 | PostBlockRequest { 38 | block: draft.block.clone(), 39 | patch: draft.patch.clone(), 40 | }, 41 | Limit::default().size(1 * KB).time(3 * SECOND), 42 | ) 43 | }) 44 | .await; 45 | 46 | context.miner_puzzle = None; 47 | } 48 | Ok(PostMinerSolutionResponse {}) 49 | } 50 | -------------------------------------------------------------------------------- /src/node/heartbeat/sync_clock.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::common::*; 3 | 4 | pub async fn sync_clock( 5 | context: &Arc>>, 6 | ) -> Result<(), NodeError> { 7 | let ctx = context.read().await; 8 | 9 | // Sync-clock not run when no-expose enabled 10 | if let Some(address) = ctx.address.clone() { 11 | let opts = ctx.opts.clone(); 12 | 13 | let net = ctx.outgoing.clone(); 14 | 15 | let timestamp = ctx.network_timestamp(); 16 | let info = ctx.get_info()?; 17 | let peer_addresses = ctx.random_peers(&mut rand::thread_rng(), opts.num_peers); 18 | drop(ctx); 19 | 20 | log::info!("Syncing clocks with: {:?}", peer_addresses); 21 | let peer_responses: Vec<(Peer, Result)> = 22 | http::group_request(&peer_addresses, |peer| { 23 | net.json_post::( 24 | format!("{}/peers", peer.address), 25 | PostPeerRequest { 26 | address, 27 | timestamp, 28 | info: info.clone(), 29 | }, 30 | Limit::default().size(1 * KB).time(3 * SECOND), 31 | ) 32 | }) 33 | .await; 34 | 35 | { 36 | let mut ctx = context.write().await; 37 | let timestamps = punish_non_responding(&mut ctx, &peer_responses) 38 | .into_iter() 39 | .map(|(_, r)| r.timestamp) 40 | .collect::>(); 41 | if !timestamps.is_empty() { 42 | // Set timestamp_offset according to median timestamp of the network 43 | let median_timestamp = utils::median(×tamps); 44 | ctx.timestamp_offset = median_timestamp as i32 - utils::local_timestamp() as i32; 45 | } 46 | } 47 | } 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /src/client/error.rs: -------------------------------------------------------------------------------- 1 | use crate::blockchain::BlockchainError; 2 | use crate::zk::ZkError; 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum NodeError { 7 | #[error("node not listening")] 8 | NotListeningError, 9 | #[error("node not answering")] 10 | NotAnsweringError, 11 | #[error("blockchain error happened: {0}")] 12 | BlockchainError(#[from] BlockchainError), 13 | #[error("server error happened: {0}")] 14 | ServerError(#[from] hyper::Error), 15 | #[error("client error happened: {0}")] 16 | ClientError(#[from] hyper::http::Error), 17 | #[error("http invalid header error happened: {0}")] 18 | InvalidHeaderError(#[from] hyper::header::InvalidHeaderValue), 19 | #[error("serde json error happened: {0}")] 20 | JsonError(#[from] serde_json::Error), 21 | #[error("serde qs error happened: {0}")] 22 | QueryStringError(#[from] serde_qs::Error), 23 | #[error("bincode error happened: {0}")] 24 | BincodeError(#[from] bincode::Error), 25 | #[error("utf8 error happened: {0}")] 26 | Utf8Error(#[from] std::str::Utf8Error), 27 | #[error("addr parse error happened: {0}")] 28 | AddrParseError(#[from] std::net::AddrParseError), 29 | #[error("cannot parse account address: {0}")] 30 | AccountParseAddressError(#[from] crate::core::ParseAddressError), 31 | #[error("no wallet available")] 32 | NoWalletError, 33 | #[error("no block is currently being mined")] 34 | NoCurrentlyMiningBlockError, 35 | #[error("timeout reached: {0}")] 36 | TimeoutError(#[from] tokio::time::error::Elapsed), 37 | #[error("http body size limit error")] 38 | SizeLimitError, 39 | #[error("bad input")] 40 | InputError, 41 | #[error("signature (authorization) header is invalid")] 42 | InvalidSignatureHeader, 43 | #[error("signature required on this message")] 44 | SignatureRequired, 45 | #[error("zk error: {0}")] 46 | ZkError(#[from] ZkError), 47 | #[error("wrong network")] 48 | WrongNetwork, 49 | #[error("states are outdated")] 50 | StatesOutdated, 51 | } 52 | -------------------------------------------------------------------------------- /src/node/heartbeat/mod.rs: -------------------------------------------------------------------------------- 1 | mod log_info; 2 | 3 | mod discover_peers; 4 | mod refresh; 5 | mod sync_blocks; 6 | mod sync_clock; 7 | mod sync_mempool; 8 | mod sync_peers; 9 | mod sync_state; 10 | 11 | use super::{http, Limit, NodeContext, NodeError, Peer, PeerAddress}; 12 | use crate::blockchain::Blockchain; 13 | use crate::client::messages::*; 14 | use crate::utils; 15 | use std::sync::Arc; 16 | use tokio::join; 17 | use tokio::sync::{RwLock, RwLockWriteGuard}; 18 | use tokio::time::sleep; 19 | 20 | pub async fn heartbeat( 21 | context: Arc>>, 22 | ) -> Result<(), NodeError> { 23 | refresh::refresh(&context).await?; 24 | log_info::log_info(&context).await?; 25 | sync_peers::sync_peers(&context).await?; 26 | discover_peers::discover_peers(&context).await?; 27 | sync_clock::sync_clock(&context).await?; 28 | sync_blocks::sync_blocks(&context).await?; 29 | sync_state::sync_state(&context).await?; 30 | sync_mempool::sync_mempool(&context).await?; 31 | Ok(()) 32 | } 33 | 34 | pub async fn heartbeater( 35 | context: Arc>>, 36 | ) -> Result<(), NodeError> { 37 | loop { 38 | if context.read().await.shutdown { 39 | break; 40 | } 41 | let heartbeat_future = heartbeat(Arc::clone(&context)); 42 | let sleep_future = sleep(context.read().await.opts.heartbeat_interval); 43 | let (res, _) = join!(heartbeat_future, sleep_future); 44 | if let Err(e) = res { 45 | log::error!("Error happened: {}", e); 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | 52 | fn punish_non_responding( 53 | ctx: &mut RwLockWriteGuard<'_, NodeContext>, 54 | resps: &[(Peer, Result)], 55 | ) -> Vec<(PeerAddress, R)> { 56 | resps 57 | .iter() 58 | .filter_map(|(peer, resp)| { 59 | if let Ok(resp) = resp { 60 | Some((peer.address, resp.clone())) 61 | } else { 62 | ctx.punish_unresponsive(peer.address); 63 | None 64 | } 65 | }) 66 | .collect() 67 | } 68 | -------------------------------------------------------------------------------- /src/core/address.rs: -------------------------------------------------------------------------------- 1 | use super::Money; 2 | use crate::crypto::SignatureScheme; 3 | use std::str::FromStr; 4 | use thiserror::Error; 5 | 6 | // All of the Zeeka's supply exists in Treasury account when the blockchain begins. 7 | // Validator/Miner fees are collected from the Treasury account. This simplifies 8 | // the process of money creation. 9 | #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] 10 | pub enum Address { 11 | Treasury, 12 | PublicKey(S::Pub), 13 | } 14 | 15 | impl PartialEq for Address { 16 | fn eq(&self, other: &Self) -> bool { 17 | bincode::serialize(self).unwrap() == bincode::serialize(other).unwrap() 18 | } 19 | } 20 | 21 | impl Eq for Address {} 22 | 23 | impl std::hash::Hash for Address { 24 | fn hash(&self, state: &mut Hasher) 25 | where 26 | Hasher: std::hash::Hasher, 27 | { 28 | state.write(&bincode::serialize(self).unwrap()); 29 | state.finish(); 30 | } 31 | } 32 | 33 | #[derive(Error, Debug)] 34 | pub enum ParseAddressError { 35 | #[error("address invalid")] 36 | Invalid, 37 | } 38 | 39 | impl std::fmt::Display for Address { 40 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 41 | match self { 42 | Address::::Treasury => write!(f, "Treasury"), 43 | Address::::PublicKey(pk) => write!(f, "{}", pk), 44 | } 45 | } 46 | } 47 | 48 | impl FromStr for Address 49 | where 50 | ::Err: std::fmt::Debug, 51 | { 52 | type Err = ParseAddressError; 53 | fn from_str(s: &str) -> Result { 54 | Ok(Address::::PublicKey( 55 | S::Pub::from_str(s).map_err(|_| ParseAddressError::Invalid)?, 56 | )) 57 | } 58 | } 59 | 60 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 61 | pub enum Signature { 62 | Unsigned, 63 | Signed(S::Sig), 64 | } 65 | 66 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 67 | pub struct Account { 68 | pub balance: Money, 69 | pub nonce: u32, 70 | } 71 | -------------------------------------------------------------------------------- /src/node/heartbeat/sync_state.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::common::*; 3 | 4 | pub async fn sync_state( 5 | context: &Arc>>, 6 | ) -> Result<(), NodeError> { 7 | let mut ctx = context.write().await; 8 | 9 | let net = ctx.outgoing.clone(); 10 | 11 | let ts = ctx.network_timestamp(); 12 | 13 | let outdated_heights = ctx.blockchain.get_outdated_heights()?; 14 | if !outdated_heights.is_empty() && ctx.outdated_since.is_none() { 15 | ctx.outdated_since = Some(ts); 16 | } else if outdated_heights.is_empty() && ctx.outdated_since.is_some() { 17 | ctx.outdated_since = None; 18 | } 19 | 20 | if !outdated_heights.is_empty() { 21 | if let Some(outdated_since) = ctx.outdated_since { 22 | if (ts as i64 - outdated_since as i64) > ctx.opts.outdated_heights_threshold as i64 { 23 | let tip = ctx.blockchain.get_tip()?; 24 | ctx.banned_headers.insert(tip, ts); 25 | ctx.blockchain.rollback()?; 26 | ctx.outdated_since = None; 27 | return Ok(()); 28 | } 29 | } 30 | let height = ctx.blockchain.get_height()?; 31 | let last_header = ctx.blockchain.get_tip()?; 32 | 33 | // Find clients which their height is equal with our height 34 | let same_height_peers = ctx 35 | .active_peers() 36 | .into_iter() 37 | .filter(|p| p.info.as_ref().map(|i| i.height == height).unwrap_or(false)); 38 | drop(ctx); 39 | 40 | for peer in same_height_peers { 41 | let patch = net 42 | .bincode_get::( 43 | format!("{}/bincode/states", peer.address), 44 | GetStatesRequest { 45 | outdated_heights: outdated_heights.clone(), 46 | to: hex::encode(last_header.hash()), 47 | }, 48 | Limit::default().time(30 * MINUTE), 49 | ) 50 | .await? // TODO: Do not throw error, punish if needed 51 | .patch; 52 | match context.write().await.blockchain.update_states(&patch) { 53 | Ok(_) => {} 54 | Err(e) => { 55 | log::warn!("Wrong state-patch given! Error: {}", e); 56 | } 57 | } 58 | } 59 | } 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /src/db/keys.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::core::{Address, ContractId}; 3 | use crate::zk::ZkDataLocator; 4 | 5 | pub fn height() -> StringKey { 6 | "height".into() 7 | } 8 | 9 | pub fn outdated() -> StringKey { 10 | "outdated".into() 11 | } 12 | 13 | pub fn block(index: u64) -> StringKey { 14 | format!("block_{:010}", index).into() 15 | } 16 | 17 | pub fn header(index: u64) -> StringKey { 18 | format!("header_{:010}", index).into() 19 | } 20 | 21 | pub fn power(index: u64) -> StringKey { 22 | format!("power_{:010}", index).into() 23 | } 24 | 25 | pub fn rollback(index: u64) -> StringKey { 26 | format!("rollback_{:010}", index).into() 27 | } 28 | 29 | pub fn merkle(index: u64) -> StringKey { 30 | format!("merkle_{:010}", index).into() 31 | } 32 | 33 | pub fn compressed_state_at(contract_id: &ContractId, at: u64) -> StringKey { 34 | format!("contract_compressed_state_{}_{}", contract_id, at).into() 35 | } 36 | 37 | pub fn account(address: &Address) -> StringKey { 38 | format!("account_{}", address).into() 39 | } 40 | 41 | pub fn contract_account(contract_id: &ContractId) -> StringKey { 42 | format!("contract_account_{}", contract_id).into() 43 | } 44 | 45 | pub fn contract(contract_id: &ContractId) -> StringKey { 46 | format!("contract_{}", contract_id).into() 47 | } 48 | 49 | pub fn contract_updates(index: u64) -> StringKey { 50 | format!("contract_updates_{:010}", index).into() 51 | } 52 | 53 | pub fn local_prefix(contract_id: &ContractId) -> String { 54 | format!("{}", contract_id) 55 | } 56 | 57 | pub fn local_height(contract_id: &ContractId) -> StringKey { 58 | format!("{}_height", local_prefix(contract_id)).into() 59 | } 60 | 61 | pub fn local_root(contract_id: &ContractId) -> StringKey { 62 | format!("{}_compressed", local_prefix(contract_id)).into() 63 | } 64 | 65 | pub fn local_tree_aux( 66 | contract_id: &ContractId, 67 | tree_loc: &ZkDataLocator, 68 | aux_id: u32, 69 | ) -> StringKey { 70 | format!("{}_{}_aux_{}", local_prefix(contract_id), tree_loc, aux_id).into() 71 | } 72 | 73 | pub fn local_rollback_to_height(contract_id: &ContractId, height: u64) -> StringKey { 74 | format!("{}_rollback_{}", local_prefix(contract_id), height).into() 75 | } 76 | 77 | pub fn local_scalar_value_prefix(contract_id: &ContractId) -> String { 78 | format!("{}_s", local_prefix(contract_id),).into() 79 | } 80 | 81 | pub fn local_non_scalar_value_prefix(contract_id: &ContractId) -> String { 82 | format!("{}", local_prefix(contract_id),).into() 83 | } 84 | 85 | pub fn local_value( 86 | contract_id: &ContractId, 87 | locator: &ZkDataLocator, 88 | is_scalar: bool, 89 | ) -> StringKey { 90 | format!( 91 | "{}_{}", 92 | if is_scalar { 93 | local_scalar_value_prefix(contract_id) 94 | } else { 95 | local_non_scalar_value_prefix(contract_id) 96 | }, 97 | locator 98 | ) 99 | .into() 100 | } 101 | -------------------------------------------------------------------------------- /src/zk/groth16.rs: -------------------------------------------------------------------------------- 1 | use super::ZkScalar; 2 | use bls12_381::{Bls12, G1Affine as BellmanG1, G2Affine as BellmanG2, Scalar as BellmanFr}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | impl From for BellmanFr { 6 | fn from(s: ZkScalar) -> BellmanFr { 7 | unsafe { std::mem::transmute::(s) } 8 | } 9 | } 10 | 11 | impl From for ZkScalar { 12 | fn from(bls: BellmanFr) -> ZkScalar { 13 | unsafe { std::mem::transmute::(bls) } 14 | } 15 | } 16 | 17 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 18 | pub struct Fp([u64; 6]); 19 | 20 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 21 | pub struct Groth16VerifyingKey { 22 | alpha_g1: (Fp, Fp, bool), 23 | beta_g1: (Fp, Fp, bool), 24 | beta_g2: ((Fp, Fp), (Fp, Fp), bool), 25 | gamma_g2: ((Fp, Fp), (Fp, Fp), bool), 26 | delta_g1: (Fp, Fp, bool), 27 | delta_g2: ((Fp, Fp), (Fp, Fp), bool), 28 | ic: Vec<(Fp, Fp, bool)>, 29 | } 30 | 31 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 32 | pub struct Groth16Proof { 33 | a: (Fp, Fp, bool), 34 | b: ((Fp, Fp), (Fp, Fp), bool), 35 | c: (Fp, Fp, bool), 36 | } 37 | 38 | pub fn groth16_verify( 39 | vk: &Groth16VerifyingKey, 40 | prev_state: ZkScalar, 41 | aux_data: ZkScalar, 42 | next_state: ZkScalar, 43 | proof: &Groth16Proof, 44 | ) -> bool { 45 | let (vk, proof) = unsafe { 46 | let alpha_g1 = std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(vk.alpha_g1.clone()); 47 | let beta_g1 = std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(vk.beta_g1.clone()); 48 | let beta_g2 = 49 | std::mem::transmute::<((Fp, Fp), (Fp, Fp), bool), BellmanG2>(vk.beta_g2.clone()); 50 | let gamma_g2 = 51 | std::mem::transmute::<((Fp, Fp), (Fp, Fp), bool), BellmanG2>(vk.gamma_g2.clone()); 52 | let delta_g1 = std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(vk.delta_g1.clone()); 53 | let delta_g2 = 54 | std::mem::transmute::<((Fp, Fp), (Fp, Fp), bool), BellmanG2>(vk.delta_g2.clone()); 55 | let ic = vk 56 | .ic 57 | .iter() 58 | .cloned() 59 | .map(|p| std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(p)) 60 | .collect(); 61 | let proof = bellman::groth16::Proof:: { 62 | a: std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(proof.a.clone()), 63 | b: std::mem::transmute::<((Fp, Fp), (Fp, Fp), bool), BellmanG2>(proof.b.clone()), 64 | c: std::mem::transmute::<(Fp, Fp, bool), BellmanG1>(proof.c.clone()), 65 | }; 66 | let vk = 67 | bellman::groth16::prepare_verifying_key(&bellman::groth16::VerifyingKey:: { 68 | alpha_g1, 69 | beta_g1, 70 | beta_g2, 71 | gamma_g2, 72 | delta_g1, 73 | delta_g2, 74 | ic, 75 | }); 76 | (vk, proof) 77 | }; 78 | bellman::groth16::verify_proof( 79 | &vk, 80 | &proof, 81 | &[prev_state.into(), aux_data.into(), next_state.into()], 82 | ) 83 | .is_ok() 84 | } 85 | -------------------------------------------------------------------------------- /src/zk/poseidon/params/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::zk::ZkScalar; 2 | use ff::PrimeField; 3 | use num_bigint::BigUint; 4 | use num_traits::Num; 5 | 6 | const PARAM_FILES: [&str; 16] = [ 7 | include_str!("poseidon_params_n255_t2_alpha5_M128.txt"), 8 | include_str!("poseidon_params_n255_t3_alpha5_M128.txt"), 9 | include_str!("poseidon_params_n255_t4_alpha5_M128.txt"), 10 | include_str!("poseidon_params_n255_t5_alpha5_M128.txt"), 11 | include_str!("poseidon_params_n255_t6_alpha5_M128.txt"), 12 | include_str!("poseidon_params_n255_t7_alpha5_M128.txt"), 13 | include_str!("poseidon_params_n255_t8_alpha5_M128.txt"), 14 | include_str!("poseidon_params_n255_t9_alpha5_M128.txt"), 15 | include_str!("poseidon_params_n255_t10_alpha5_M128.txt"), 16 | include_str!("poseidon_params_n255_t11_alpha5_M128.txt"), 17 | include_str!("poseidon_params_n255_t12_alpha5_M128.txt"), 18 | include_str!("poseidon_params_n255_t13_alpha5_M128.txt"), 19 | include_str!("poseidon_params_n255_t14_alpha5_M128.txt"), 20 | include_str!("poseidon_params_n255_t15_alpha5_M128.txt"), 21 | include_str!("poseidon_params_n255_t16_alpha5_M128.txt"), 22 | include_str!("poseidon_params_n255_t17_alpha5_M128.txt"), 23 | ]; 24 | 25 | pub const MAX_ARITY: usize = PARAM_FILES.len(); 26 | 27 | fn read_constants(line: &str) -> Vec { 28 | let mut constants_str = line.to_string().replace("0x", ""); 29 | constants_str.retain(|c| c != '\'' && c != '[' && c != ']' && c != ' '); 30 | constants_str 31 | .split(',') 32 | .map(|s| { 33 | ZkScalar::from_str_vartime(&BigUint::from_str_radix(s, 16).unwrap().to_string()) 34 | .unwrap() 35 | }) 36 | .collect() 37 | } 38 | 39 | fn parse_params(source: &str) -> PoseidonParams { 40 | let lines = source.lines().collect::>(); 41 | let opts = lines[0].split(",").map(|s| s.trim()).collect::>(); 42 | let capacity: usize = opts[1].split("=").collect::>()[1].parse().unwrap(); 43 | let full_rounds: usize = opts[4].split("=").collect::>()[1].parse().unwrap(); 44 | let partial_rounds: usize = opts[5].split("=").collect::>()[1].parse().unwrap(); 45 | let round_constants = read_constants(lines[3]); 46 | let mds_constants = read_constants(lines[15]) 47 | .chunks(capacity) 48 | .map(|chunk| chunk.to_vec()) 49 | .collect::>(); 50 | PoseidonParams { 51 | capacity, 52 | full_rounds, 53 | partial_rounds, 54 | round_constants, 55 | mds_constants, 56 | } 57 | } 58 | 59 | #[derive(Debug, Clone)] 60 | pub struct PoseidonParams { 61 | pub capacity: usize, 62 | pub full_rounds: usize, 63 | pub partial_rounds: usize, 64 | pub round_constants: Vec, 65 | pub mds_constants: Vec>, 66 | } 67 | 68 | lazy_static! { 69 | static ref PARAMS: [PoseidonParams; 16] = PARAM_FILES 70 | .iter() 71 | .map(|src| parse_params(src)) 72 | .collect::>() 73 | .try_into() 74 | .unwrap(); 75 | } 76 | 77 | impl PoseidonParams { 78 | pub fn for_width(width: usize) -> Option<&'static Self> { 79 | PARAMS.get(width - 2) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/blockchain/error.rs: -------------------------------------------------------------------------------- 1 | use crate::db::KvStoreError; 2 | use crate::zk::{StateManagerError, ZkError}; 3 | use thiserror::Error; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum BlockchainError { 7 | #[error("kvstore error happened: {0}")] 8 | KvStoreError(#[from] KvStoreError), 9 | #[error("different genesis block exists on the database")] 10 | DifferentGenesis, 11 | #[error("transaction signature is invalid")] 12 | SignatureError, 13 | #[error("balance insufficient")] 14 | BalanceInsufficient, 15 | #[error("contract balance insufficient")] 16 | ContractBalanceInsufficient, 17 | #[error("inconsistency error")] 18 | Inconsistency, 19 | #[error("block not found")] 20 | BlockNotFound, 21 | #[error("cannot extend from the genesis block")] 22 | ExtendFromGenesis, 23 | #[error("cannot extend from very future blocks")] 24 | ExtendFromFuture, 25 | #[error("block number invalid")] 26 | InvalidBlockNumber, 27 | #[error("parent hash invalid")] 28 | InvalidParentHash, 29 | #[error("merkle root invalid")] 30 | InvalidMerkleRoot, 31 | #[error("transaction nonce invalid")] 32 | InvalidTransactionNonce, 33 | #[error("block timestamp is in past")] 34 | InvalidTimestamp, 35 | #[error("unmet difficulty target")] 36 | DifficultyTargetUnmet, 37 | #[error("wrong difficulty target on blocks")] 38 | DifficultyTargetWrong, 39 | #[error("miner reward not present")] 40 | MinerRewardNotFound, 41 | #[error("illegal access to treasury funds")] 42 | IllegalTreasuryAccess, 43 | #[error("miner reward transaction is invalid")] 44 | InvalidMinerReward, 45 | #[error("contract not found")] 46 | ContractNotFound, 47 | #[error("update function not found in the given contract")] 48 | ContractFunctionNotFound, 49 | #[error("Incorrect zero-knowledge proof")] 50 | IncorrectZkProof, 51 | #[error("Full-state not found in the update provided")] 52 | FullStateNotFound, 53 | #[error("Invalid full-state in the update provided")] 54 | FullStateNotValid, 55 | #[error("cannot draft a new block when full-states are outdated")] 56 | StatesOutdated, 57 | #[error("contract states at requested height are unavailable")] 58 | StatesUnavailable, 59 | #[error("block too big")] 60 | BlockTooBig, 61 | #[error("state-delta too big")] 62 | StateDeltaTooBig, 63 | #[error("compressed-state at specified height not found")] 64 | CompressedStateNotFound, 65 | #[error("full-state has invalid deltas")] 66 | DeltasInvalid, 67 | #[error("no blocks to roll back")] 68 | NoBlocksToRollback, 69 | #[error("zk error happened: {0}")] 70 | ZkError(#[from] ZkError), 71 | #[error("state-manager error happened: {0}")] 72 | StateManagerError(#[from] StateManagerError), 73 | #[error("invalid deposit/withdraw signature")] 74 | InvalidContractPaymentSignature, 75 | #[error("insufficient mpn updates")] 76 | InsufficientMpnUpdates, 77 | #[error("invalid zero-transaction")] 78 | InvalidZeroTransaction, 79 | #[error("cannot send funds to yourself")] 80 | SelfPaymentNotAllowed, 81 | #[error("executor cannot execute his own payments")] 82 | CannotExecuteOwnPayments, 83 | #[error("contract contains invalid state-model")] 84 | InvalidStateModel, 85 | } 86 | -------------------------------------------------------------------------------- /src/crypto/ed25519.rs: -------------------------------------------------------------------------------- 1 | use super::SignatureScheme; 2 | 3 | use crate::core::hash::Hash; 4 | use ed25519_dalek::{Signer, Verifier}; 5 | use serde::{Deserialize, Serialize}; 6 | use std::str::FromStr; 7 | use thiserror::Error; 8 | 9 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 10 | pub struct Ed25519(std::marker::PhantomData); 11 | 12 | pub struct PrivateKey(pub ed25519_dalek::Keypair); 13 | 14 | // Why not derivable? 15 | impl Clone for PrivateKey { 16 | fn clone(&self) -> Self { 17 | PrivateKey(ed25519_dalek::Keypair::from_bytes(&self.0.to_bytes()).unwrap()) 18 | } 19 | } 20 | 21 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 22 | pub struct PublicKey(pub ed25519_dalek::PublicKey); 23 | 24 | impl From for PublicKey { 25 | fn from(priv_key: PrivateKey) -> Self { 26 | Self(priv_key.0.public) 27 | } 28 | } 29 | 30 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 31 | pub struct Signature(pub ed25519_dalek::Signature); 32 | 33 | impl SignatureScheme for Ed25519 { 34 | type Pub = PublicKey; 35 | type Priv = PrivateKey; 36 | type Sig = Signature; 37 | fn generate_keys(seed: &[u8]) -> (PublicKey, PrivateKey) { 38 | let mut x = H::hash(seed); 39 | x.as_mut()[31] &= 0x7f; 40 | let secret = ed25519_dalek::SecretKey::from_bytes(x.as_ref()).unwrap(); 41 | let public = ed25519_dalek::PublicKey::from(&secret); 42 | let keypair = ed25519_dalek::Keypair { public, secret }; 43 | (PublicKey(public), PrivateKey(keypair)) 44 | } 45 | fn sign(sk: &PrivateKey, message: &[u8]) -> Signature { 46 | Signature(sk.0.sign(message)) 47 | } 48 | fn verify(pk: &PublicKey, message: &[u8], sig: &Signature) -> bool { 49 | pk.0.verify(message, &sig.0).is_ok() 50 | } 51 | } 52 | 53 | impl std::fmt::Display for PublicKey { 54 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 55 | write!(f, "0x")?; 56 | for byte in self.0.as_bytes().iter().rev() { 57 | write!(f, "{:02x}", byte)?; 58 | } 59 | Ok(()) 60 | } 61 | } 62 | 63 | #[derive(Error, Debug)] 64 | pub enum ParsePublicKeyError { 65 | #[error("public key invalid")] 66 | Invalid, 67 | } 68 | 69 | impl FromStr for PublicKey { 70 | type Err = ParsePublicKeyError; 71 | fn from_str(mut s: &str) -> Result { 72 | if s.len() != 66 || !s.to_lowercase().starts_with("0x") { 73 | return Err(ParsePublicKeyError::Invalid); 74 | } 75 | s = &s[2..]; 76 | let bytes = hex::decode(s) 77 | .map_err(|_| ParsePublicKeyError::Invalid)? 78 | .into_iter() 79 | .rev() 80 | .collect::>(); 81 | Ok(PublicKey( 82 | ed25519_dalek::PublicKey::from_bytes(&bytes) 83 | .map_err(|_| ParsePublicKeyError::Invalid)?, 84 | )) 85 | } 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use super::*; 91 | 92 | #[test] 93 | fn test_ed25519_signature_verification() { 94 | let (pk, sk) = Ed25519::::generate_keys(b"ABC"); 95 | let msg = b"salam1"; 96 | let fake_msg = b"salam2"; 97 | let sig = Ed25519::::sign(&sk, msg); 98 | 99 | assert!(Ed25519::::verify(&pk, msg, &sig)); 100 | assert!(!Ed25519::::verify(&pk, fake_msg, &sig)); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /contrib/seeds/generate_seeds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3.9 2 | 3 | import re 4 | import sys 5 | import ipaddress 6 | import datetime 7 | from collections import defaultdict 8 | 9 | TEMPLATE = """ 10 | /*! 11 | * Please don't update this file manually 12 | * Auto generated by contrib/seeds/generate_seeds.py 13 | * 14 | * Last Modified: {last_modified} 15 | */ 16 | 17 | use super::PeerAddress; 18 | #[allow(unused_imports)] 19 | use std::net::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; 20 | 21 | pub fn seed_bootstrap_nodes() -> Vec { 22 | vec![ 23 | // --- BEGIN BOOTSTRAP NODES --- 24 | {seed_nodes} 25 | // --- END BOOTSTRAP NODES --- 26 | ] 27 | } 28 | """ 29 | 30 | HOST_TEMPLATES = { 31 | "ipv4": "PeerAddress(SocketAddr::new(IpAddr::V4(Ipv4Addr::new({host_hex})), {port_hex})),", 32 | "ipv6": "PeerAddress(SocketAddr::new(IpAddr::V6(Ipv6Addr::new({host_hex})), {port_hex}))," 33 | } 34 | 35 | ipV4 = re.compile(r"((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}") 36 | ipV6 = re.compile(r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))") 37 | onionV2 = re.compile(r"[a-z2-7]{16}.onion") 38 | onionV3 = re.compile(r"[a-z2-7]{56}.onion") 39 | 40 | 41 | def hexify_ip(ip, type="ipv4"): 42 | ip_address = ipaddress.ip_address(ip) 43 | if type == "ipv6": 44 | return ", ".join([f"0x{p}" for p in format(ip_address, "_X").split("_")]) 45 | elif type == "ipv4": 46 | return ", ".join([f"0x{p}" for p in re.findall("..", format(ip_address, "x"))]) 47 | 48 | 49 | def get_formatted_hosts(lines): 50 | formatted = defaultdict(list) 51 | for r_line in lines: 52 | line = r_line.strip() 53 | if line: 54 | port = line.split(":")[-1] 55 | ip = line[:-(len(port)+1)] 56 | 57 | # replace brackets for ipv6s 58 | ip = ip.replace("[", "").replace("]", "") 59 | if ipV4.match(ip): 60 | formatted["ipv4"].append((ip, port)) 61 | elif ipV6.match(ip): 62 | formatted["ipv6"].append((ip, port)) 63 | elif onionV2.match(ip): 64 | formatted["onionv2"].append((ip, port)) 65 | elif onionV3.match(ip): 66 | formatted["onionv3"].append((ip, port)) 67 | 68 | return formatted 69 | 70 | 71 | if __name__ == "__main__": 72 | if len(sys.argv) < 2: 73 | print("Please provide the path to the file containing the seed hosts") 74 | exit(1) 75 | 76 | raw_seeds_path = sys.argv[1] 77 | with open(raw_seeds_path, "r") as r_handler: 78 | lines = r_handler.readlines() 79 | 80 | formatted_hosts = get_formatted_hosts(lines) 81 | 82 | supported_types = ["ipv4", "ipv6"] 83 | 84 | hosts = [] 85 | space_padding = " " * 8 86 | for type in supported_types: 87 | hosts.append(f"{space_padding}// {type} addresses") 88 | hosts.append("") 89 | for host in formatted_hosts[type]: 90 | hosts.append( 91 | space_padding + HOST_TEMPLATES[type].format(host_hex=hexify_ip(host[0], type=type), port_hex=hex(int(host[1]))) 92 | ) 93 | hosts.append("") 94 | 95 | 96 | final = TEMPLATE.replace("{last_modified}", str(datetime.datetime.now())).replace("{seed_nodes}", "\n".join(hosts)) 97 | 98 | destination = "../../src/node/seeds.rs" 99 | 100 | with open(destination, "w") as w_handler: 101 | w_handler.write(final) 102 | -------------------------------------------------------------------------------- /src/db/disk.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use leveldb::batch::Batch; 3 | use leveldb::database::batch::Writebatch; 4 | use leveldb::database::cache::Cache; 5 | use leveldb::database::{ 6 | snapshots::{Snapshot, Snapshots}, 7 | Database, 8 | }; 9 | use leveldb::iterator::Iterable; 10 | use leveldb::iterator::LevelDBIterator; 11 | use leveldb::kv::KV; 12 | use leveldb::options::{Options, ReadOptions, WriteOptions}; 13 | use std::fs; 14 | use std::path::Path; 15 | use tempdir::TempDir; 16 | 17 | pub struct ReadOnlyLevelDbKvStore(Database); 18 | pub struct LevelDbSnapshot<'a>(Snapshot<'a, StringKey>); 19 | impl ReadOnlyLevelDbKvStore { 20 | pub fn read_only( 21 | path: &Path, 22 | cache_size: usize, 23 | ) -> Result { 24 | let link_dir = TempDir::new("bazuka_mirror")?.into_path(); 25 | for p in std::fs::read_dir(path)? { 26 | let p = p?; 27 | if !p.file_name().eq_ignore_ascii_case("lock") { 28 | std::os::unix::fs::symlink(p.path(), link_dir.join(p.file_name()))?; 29 | } 30 | } 31 | let mut options = Options::new(); 32 | options.cache = Some(Cache::new(cache_size)); 33 | Ok(ReadOnlyLevelDbKvStore(Database::open(&link_dir, options)?)) 34 | } 35 | pub fn snapshot(&self) -> LevelDbSnapshot { 36 | LevelDbSnapshot(self.0.snapshot()) 37 | } 38 | } 39 | 40 | pub struct LevelDbKvStore(Database); 41 | impl LevelDbKvStore { 42 | pub fn new(path: &Path, cache_size: usize) -> Result { 43 | fs::create_dir_all(&path)?; 44 | let mut options = Options::new(); 45 | options.create_if_missing = true; 46 | options.cache = Some(Cache::new(cache_size)); 47 | Ok(LevelDbKvStore(Database::open(path, options)?)) 48 | } 49 | } 50 | 51 | impl KvStore for LevelDbKvStore { 52 | fn get(&self, k: StringKey) -> Result, KvStoreError> { 53 | let read_opts = ReadOptions::new(); 54 | match self.0.get(read_opts, k) { 55 | Ok(v) => Ok(v.map(Blob)), 56 | Err(_) => Err(KvStoreError::Failure), 57 | } 58 | } 59 | fn update(&mut self, ops: &[WriteOp]) -> Result<(), KvStoreError> { 60 | let write_opts = WriteOptions::new(); 61 | let mut batch = Writebatch::new(); 62 | for op in ops.iter() { 63 | match op { 64 | WriteOp::Remove(k) => batch.delete(k.clone()), 65 | WriteOp::Put(k, v) => batch.put(k.clone(), &v.0), 66 | } 67 | } 68 | match self.0.write(write_opts, &batch) { 69 | Ok(_) => Ok(()), 70 | Err(_) => Err(KvStoreError::Failure), 71 | } 72 | } 73 | fn pairs(&self, prefix: StringKey) -> Result, KvStoreError> { 74 | let it = self.0.iter(ReadOptions::new()); 75 | it.seek(&prefix); 76 | Ok(it 77 | .collect::>() 78 | .into_iter() 79 | .take_while(|(k, _)| k.0.starts_with(&prefix.0)) 80 | .map(|(k, v)| (k, Blob(v))) 81 | .collect()) 82 | } 83 | } 84 | 85 | impl<'a> KvStore for LevelDbSnapshot<'a> { 86 | fn get(&self, k: StringKey) -> Result, KvStoreError> { 87 | let read_opts = ReadOptions::new(); 88 | match self.0.get(read_opts, k) { 89 | Ok(v) => Ok(v.map(Blob)), 90 | Err(_) => Err(KvStoreError::Failure), 91 | } 92 | } 93 | fn update(&mut self, _: &[WriteOp]) -> Result<(), KvStoreError> { 94 | panic!("Cannot update!"); 95 | } 96 | fn pairs(&self, prefix: StringKey) -> Result, KvStoreError> { 97 | let it = self.0.iter(ReadOptions::new()); 98 | it.seek(&prefix); 99 | Ok(it 100 | .collect::>() 101 | .into_iter() 102 | .take_while(|(k, _)| k.0.starts_with(&prefix.0)) 103 | .map(|(k, v)| (k, Blob(v))) 104 | .collect()) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/db/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[cfg(feature = "db")] 4 | use tempdir::TempDir; 5 | 6 | #[cfg(feature = "db")] 7 | fn temp_disk_store() -> Result { 8 | LevelDbKvStore::new(TempDir::new("bazuka_test").unwrap().path(), 64) 9 | } 10 | 11 | #[test] 12 | #[cfg(feature = "db")] 13 | fn test_ram_and_disk_pair_prefix() -> Result<(), KvStoreError> { 14 | let mut ram = RamKvStore::default(); 15 | let mut disk = temp_disk_store()?; 16 | 17 | assert_eq!(ram.checksum::()?, disk.checksum::()?); 18 | 19 | let ops = &[ 20 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 3])), 21 | WriteOp::Put("aa".into(), Blob(vec![3, 2, 1, 0])), 22 | WriteOp::Put("a0a".into(), Blob(vec![])), 23 | WriteOp::Put("bge".into(), Blob(vec![])), 24 | WriteOp::Put("def".into(), Blob(vec![])), 25 | ]; 26 | 27 | ram.update(ops)?; 28 | disk.update(ops)?; 29 | 30 | assert_eq!(disk.pairs("".into())?.len(), 5); 31 | assert_eq!(ram.pairs("".into())?.len(), 5); 32 | assert_eq!(disk.pairs("a".into())?.len(), 2); 33 | assert_eq!(ram.pairs("a".into())?.len(), 2); 34 | assert_eq!(disk.pairs("b".into())?.len(), 2); 35 | assert_eq!(ram.pairs("b".into())?.len(), 2); 36 | assert_eq!(disk.pairs("d".into())?.len(), 1); 37 | assert_eq!(ram.pairs("d".into())?.len(), 1); 38 | assert_eq!(disk.pairs("a0".into())?.len(), 1); 39 | assert_eq!(ram.pairs("a0".into())?.len(), 1); 40 | assert_eq!(disk.pairs("a1".into())?.len(), 0); 41 | assert_eq!(ram.pairs("a1".into())?.len(), 0); 42 | 43 | Ok(()) 44 | } 45 | 46 | #[test] 47 | #[cfg(feature = "db")] 48 | fn test_ram_and_disk_db_consistency() -> Result<(), KvStoreError> { 49 | let mut ram = RamKvStore::default(); 50 | let mut disk = temp_disk_store()?; 51 | 52 | assert_eq!(ram.checksum::()?, disk.checksum::()?); 53 | 54 | let ops = &[ 55 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 3])), 56 | WriteOp::Put("aa".into(), Blob(vec![3, 2, 1, 0])), 57 | WriteOp::Put("def".into(), Blob(vec![])), 58 | ]; 59 | 60 | ram.update(ops)?; 61 | disk.update(ops)?; 62 | 63 | assert_eq!(ram.checksum::()?, disk.checksum::()?); 64 | 65 | let new_ops = &[ 66 | WriteOp::Remove("aa".into()), 67 | WriteOp::Put("def".into(), Blob(vec![1, 1, 1, 2])), 68 | WriteOp::Put("ghi".into(), Blob(vec![3, 3, 3, 3])), 69 | ]; 70 | 71 | ram.update(new_ops)?; 72 | disk.update(new_ops)?; 73 | 74 | assert_eq!(ram.checksum::()?, disk.checksum::()?); 75 | 76 | Ok(()) 77 | } 78 | 79 | #[test] 80 | fn test_mirror_kv_store() -> Result<(), KvStoreError> { 81 | let mut ram = RamKvStore::default(); 82 | 83 | let ops = &[ 84 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 3])), 85 | WriteOp::Put("aa".into(), Blob(vec![3, 2, 1, 0])), 86 | WriteOp::Put("def".into(), Blob(vec![])), 87 | ]; 88 | 89 | ram.update(ops)?; 90 | 91 | let prev_ram_checksum = ram.checksum::()?; 92 | 93 | let mut mirror = RamMirrorKvStore::new(&ram); 94 | 95 | let ops_on_mirror = &[ 96 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 4])), 97 | WriteOp::Put("dd".into(), Blob(vec![1, 1, 1])), 98 | WriteOp::Put("ghi".into(), Blob(vec![2, 3])), 99 | ]; 100 | 101 | mirror.update(ops_on_mirror)?; 102 | 103 | let mirror_checksum = mirror.checksum::()?; 104 | 105 | let mirror_ops = mirror.to_ops(); 106 | 107 | assert_eq!(ram.checksum::()?, prev_ram_checksum); 108 | 109 | ram.update(&mirror_ops)?; 110 | 111 | assert_eq!(ram.checksum::()?, mirror_checksum); 112 | 113 | Ok(()) 114 | } 115 | 116 | #[test] 117 | fn test_mirror_rollback() -> Result<(), KvStoreError> { 118 | let mut ram = RamKvStore::default(); 119 | 120 | let ops = &[ 121 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 3])), 122 | WriteOp::Put("aa".into(), Blob(vec![3, 2, 1, 0])), 123 | WriteOp::Put("def".into(), Blob(vec![])), 124 | ]; 125 | 126 | ram.update(ops)?; 127 | 128 | let mirror1 = ram.mirror(); 129 | assert_eq!(mirror1.rollback()?, vec![]); 130 | 131 | let mut mirror2 = ram.mirror(); 132 | mirror2.update(&[WriteOp::Remove("kk".into())])?; 133 | assert_eq!(mirror2.rollback()?, vec![WriteOp::Remove("kk".into())]); 134 | 135 | let mut mirror3 = ram.mirror(); 136 | mirror3.update(&[ 137 | WriteOp::Put("bc".into(), Blob(vec![3, 2, 1])), 138 | WriteOp::Put("gg".into(), Blob(vec![2, 2, 2, 2])), 139 | WriteOp::Put("fre".into(), Blob(vec![1, 1])), 140 | WriteOp::Remove("aa".into()), 141 | ])?; 142 | let mut mirror3_rollback = mirror3.rollback()?; 143 | mirror3_rollback.sort_by_key(|v| match v { 144 | WriteOp::Put(k, _) => k.clone(), 145 | WriteOp::Remove(k) => k.clone(), 146 | }); 147 | assert_eq!( 148 | mirror3_rollback, 149 | vec![ 150 | WriteOp::Put("aa".into(), Blob(vec![3, 2, 1, 0])), 151 | WriteOp::Put("bc".into(), Blob(vec![0, 1, 2, 3])), 152 | WriteOp::Remove("fre".into()), 153 | WriteOp::Remove("gg".into()), 154 | ] 155 | ); 156 | 157 | Ok(()) 158 | } 159 | -------------------------------------------------------------------------------- /src/node/firewall.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub struct Firewall { 4 | request_count_limit_per_minute: usize, 5 | traffic_limit_per_15m: u64, 6 | unresponsive_count_limit_per_15m: usize, 7 | bad_ips: HashMap, 8 | unresponsive_ips: HashMap, 9 | unresponsive_count_last_reset: Timestamp, 10 | unresponsive_count: HashMap, 11 | request_count_last_reset: Timestamp, 12 | request_count: HashMap, 13 | traffic_last_reset: Timestamp, 14 | traffic: HashMap, 15 | } 16 | 17 | impl Firewall { 18 | pub fn new( 19 | request_count_limit_per_minute: usize, 20 | traffic_limit_per_15m: u64, 21 | unresponsive_count_limit_per_15m: usize, 22 | ) -> Self { 23 | Self { 24 | request_count_limit_per_minute, 25 | traffic_limit_per_15m, 26 | unresponsive_count_limit_per_15m, 27 | bad_ips: HashMap::new(), 28 | unresponsive_ips: HashMap::new(), 29 | request_count_last_reset: 0, 30 | unresponsive_count_last_reset: 0, 31 | request_count: HashMap::new(), 32 | traffic_last_reset: 0, 33 | traffic: HashMap::new(), 34 | unresponsive_count: HashMap::new(), 35 | } 36 | } 37 | pub fn refresh(&mut self) { 38 | for ip in self.bad_ips.clone().into_keys() { 39 | if !self.is_ip_bad(ip) { 40 | self.bad_ips.remove(&ip); 41 | } 42 | } 43 | for ip in self.unresponsive_ips.clone().into_keys() { 44 | if !self.is_ip_unresponsive(ip) { 45 | self.unresponsive_ips.remove(&ip); 46 | } 47 | } 48 | 49 | let ts = local_timestamp(); 50 | 51 | if ts - self.unresponsive_count_last_reset > 900 { 52 | self.unresponsive_count.clear(); 53 | self.unresponsive_count_last_reset = ts; 54 | } 55 | 56 | if ts - self.request_count_last_reset > 60 { 57 | self.request_count.clear(); 58 | self.request_count_last_reset = ts; 59 | } 60 | 61 | if ts - self.traffic_last_reset > 900 { 62 | self.traffic.clear(); 63 | self.traffic_last_reset = ts; 64 | } 65 | } 66 | pub fn add_traffic(&mut self, ip: IpAddr, amount: u64) { 67 | *self.traffic.entry(ip).or_insert(0) += amount; 68 | } 69 | pub fn punish_bad(&mut self, ip: IpAddr, secs: u32) { 70 | let now = local_timestamp(); 71 | let ts = self.bad_ips.entry(ip).or_insert(0); 72 | *ts = std::cmp::max(*ts, now) + secs; 73 | } 74 | pub fn is_peer_dead(&self, peer: PeerAddress) -> bool { 75 | let ip = peer.0.ip(); 76 | 77 | // Loopback is never dead 78 | if ip.is_loopback() { 79 | return false; 80 | } 81 | 82 | if let Some(cnt) = self.unresponsive_count.get(&ip) { 83 | if *cnt > self.unresponsive_count_limit_per_15m { 84 | return true; 85 | } 86 | } 87 | false 88 | } 89 | pub fn punish_unresponsive(&mut self, ip: IpAddr, secs: u32, max_punish: u32) { 90 | let now = local_timestamp(); 91 | let ts = self.unresponsive_ips.entry(ip).or_insert(0); 92 | let cnt = self.unresponsive_count.entry(ip).or_insert(0); 93 | *cnt += 1; 94 | *ts = std::cmp::min(std::cmp::max(*ts, now) + secs, now + max_punish); 95 | } 96 | fn is_ip_bad(&self, ip: IpAddr) -> bool { 97 | // Loopback is never bad 98 | if ip.is_loopback() { 99 | return false; 100 | } 101 | 102 | if let Some(punished_until) = self.bad_ips.get(&ip) { 103 | if local_timestamp() < *punished_until { 104 | return true; 105 | } 106 | } 107 | false 108 | } 109 | fn is_ip_unresponsive(&self, ip: IpAddr) -> bool { 110 | // Loopback is never unresponsive 111 | if ip.is_loopback() { 112 | return false; 113 | } 114 | 115 | if let Some(punished_until) = self.unresponsive_ips.get(&ip) { 116 | if local_timestamp() < *punished_until { 117 | return true; 118 | } 119 | } 120 | false 121 | } 122 | pub fn outgoing_permitted(&self, peer: PeerAddress) -> bool { 123 | let ip = peer.0.ip(); 124 | 125 | // Outgoing to loopback is always permitted. 126 | if ip.is_loopback() { 127 | return true; 128 | } 129 | 130 | if self.is_ip_bad(ip) || self.is_ip_unresponsive(ip) { 131 | return false; 132 | } 133 | 134 | true 135 | } 136 | pub fn incoming_permitted(&mut self, client: SocketAddr) -> bool { 137 | // Incoming from loopback is always permitted 138 | if client.ip().is_loopback() { 139 | return true; 140 | } 141 | 142 | if self.is_ip_bad(client.ip()) { 143 | return false; 144 | } 145 | 146 | if self.traffic.get(&client.ip()).cloned().unwrap_or(0) > self.traffic_limit_per_15m { 147 | return false; 148 | } 149 | 150 | let cnt = self.request_count.entry(client.ip()).or_insert(0); 151 | if *cnt > self.request_count_limit_per_minute { 152 | return false; 153 | } 154 | 155 | *cnt += 1; 156 | true 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/crypto/merkle.rs: -------------------------------------------------------------------------------- 1 | use crate::core::hash::Hash; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Clone, Debug, Serialize, Deserialize)] 5 | pub struct MerkleTree { 6 | data: Vec, 7 | } 8 | 9 | fn merge_hash(a: &H::Output, b: &H::Output) -> H::Output { 10 | let mut inp = Vec::new(); 11 | if a < b { 12 | inp.extend(a.as_ref()); 13 | inp.extend(b.as_ref()); 14 | } else { 15 | inp.extend(b.as_ref()); 16 | inp.extend(a.as_ref()); 17 | } 18 | H::hash(&inp) 19 | } 20 | 21 | impl MerkleTree { 22 | pub fn depth(&self) -> u32 { 23 | let len = self.data.len(); 24 | if len == 1 { 25 | 0 26 | } else { 27 | len.next_power_of_two().trailing_zeros() - 1 28 | } 29 | } 30 | 31 | pub fn num_leaves(&self) -> usize { 32 | (self.data.len() + 1) >> 1 33 | } 34 | 35 | fn parent_map(&self, i: usize) -> usize { 36 | (i - 1) >> 1 37 | } 38 | 39 | fn sibling_map(&self, i: usize) -> usize { 40 | if i % 2 == 0 { 41 | i - 1 42 | } else { 43 | i + 1 44 | } 45 | } 46 | 47 | fn leaf_map(&self, i: usize) -> usize { 48 | let len = self.data.len(); 49 | let dep = self.depth(); 50 | let lower_start = (1 << dep) - 1; 51 | let lower_leaves = len - lower_start; 52 | if lower_start + i < len { 53 | lower_start + i 54 | } else { 55 | let upper_start = (1 << (dep - 1)) - 1; 56 | let upper_offset = lower_leaves >> 1; 57 | upper_start - upper_offset + i 58 | } 59 | } 60 | 61 | fn make_parents(&mut self) { 62 | let total = self.data.len(); 63 | for d in (1..self.depth() + 1).rev() { 64 | let start = (1 << d) - 1; 65 | let len = 1 << d; 66 | for k in (0..len).step_by(2) { 67 | let i = start + k; 68 | let j = start + k + 1; 69 | if i >= total { 70 | break; 71 | } 72 | let merged = merge_hash::(&self.data[i], &self.data[j]); 73 | let parent = self.parent_map(i); 74 | self.data[parent] = merged; 75 | } 76 | } 77 | } 78 | 79 | pub fn root(&self) -> H::Output { 80 | self.data[0] 81 | } 82 | 83 | pub fn prove(&self, leaf: usize) -> Vec { 84 | let mut proof = Vec::new(); 85 | let mut ind = self.leaf_map(leaf); 86 | while ind != 0 { 87 | proof.push(self.data[self.sibling_map(ind)]); 88 | ind = self.parent_map(ind); 89 | } 90 | proof 91 | } 92 | 93 | pub fn new(leaves: Vec) -> MerkleTree { 94 | if leaves.is_empty() { 95 | return MerkleTree:: { 96 | data: vec![H::Output::default()], 97 | }; 98 | } 99 | let mut tree = MerkleTree:: { 100 | data: vec![H::Output::default(); leaves.len() * 2 - 1], 101 | }; 102 | for (i, val) in leaves.iter().enumerate() { 103 | let mapped = tree.leaf_map(i); 104 | tree.data[mapped] = *val; 105 | } 106 | tree.make_parents(); 107 | tree 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | use crate::core::hash::Sha3Hasher; 115 | 116 | #[test] 117 | fn test_merkle_proof() { 118 | let tree = MerkleTree::::new((0..10).map(|i| Sha3Hasher::hash(&[i])).collect()); 119 | for i in 0..10 { 120 | let proof = tree.prove(i); 121 | let root = tree.root(); 122 | let mut curr = Sha3Hasher::hash(&[i as u8]); 123 | for entry in proof { 124 | curr = merge_hash::(&curr, &entry); 125 | } 126 | assert_eq!(curr, root); 127 | } 128 | } 129 | 130 | #[test] 131 | fn test_calculation() { 132 | assert_eq!(MerkleTree::::new(Vec::new()).root(), [0u8; 32]); 133 | assert_eq!( 134 | MerkleTree::::new(vec![Sha3Hasher::hash(&[1])]).root(), 135 | [ 136 | 39, 103, 241, 92, 138, 242, 242, 199, 34, 93, 82, 115, 253, 214, 131, 237, 199, 20, 137 | 17, 10, 152, 125, 16, 84, 105, 124, 52, 138, 237, 78, 108, 199 138 | ] 139 | ); 140 | assert_eq!( 141 | MerkleTree::::new((2..4).map(|i| Sha3Hasher::hash(&[i])).collect()).root(), 142 | [ 143 | 147, 148, 62, 236, 12, 170, 57, 157, 174, 243, 124, 220, 81, 74, 187, 99, 252, 243, 144 | 77, 85, 3, 93, 223, 166, 184, 93, 190, 149, 217, 73, 107, 7 145 | ] 146 | ); 147 | assert_eq!( 148 | MerkleTree::::new((0..10).map(|i| Sha3Hasher::hash(&[i])).collect()).root(), 149 | [ 150 | 170, 152, 247, 242, 8, 76, 139, 70, 132, 168, 19, 116, 29, 8, 9, 42, 0, 85, 164, 151 | 237, 192, 106, 123, 174, 180, 217, 32, 126, 18, 38, 210, 79 152 | ] 153 | ); 154 | assert_eq!( 155 | MerkleTree::::new((0..16).map(|i| Sha3Hasher::hash(&[i])).collect()).root(), 156 | [ 157 | 205, 127, 119, 130, 101, 244, 191, 81, 239, 175, 89, 0, 91, 183, 65, 61, 170, 6, 158 | 253, 155, 249, 90, 186, 20, 71, 105, 83, 24, 118, 68, 70, 119 159 | ] 160 | ); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/crypto/jubjub/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::zk::{hash_to_scalar, ZkHasher, ZkScalar, ZkScalarRepr}; 2 | use ff::{Field, PrimeField}; 3 | use num_bigint::BigUint; 4 | use num_integer::Integer; 5 | use serde::{Deserialize, Serialize}; 6 | use std::ops::{AddAssign, MulAssign}; 7 | 8 | use super::ZkSignatureScheme; 9 | 10 | use std::str::FromStr; 11 | use thiserror::Error; 12 | 13 | mod curve; 14 | pub use curve::*; 15 | 16 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] 17 | pub struct JubJub(std::marker::PhantomData); 18 | 19 | #[derive(Error, Debug)] 20 | pub enum ParsePublicKeyError { 21 | #[error("public key invalid")] 22 | Invalid, 23 | } 24 | 25 | #[derive(Clone)] 26 | pub struct PrivateKey { 27 | pub public_key: PointAffine, 28 | pub randomness: ZkScalar, 29 | pub scalar: ZkScalar, 30 | } 31 | 32 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] 33 | pub struct PublicKey(pub PointCompressed); 34 | 35 | impl From for PublicKey { 36 | fn from(priv_key: PrivateKey) -> Self { 37 | Self(priv_key.public_key.compress()) 38 | } 39 | } 40 | 41 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] 42 | pub struct Signature { 43 | pub r: PointAffine, 44 | pub s: ZkScalar, 45 | } 46 | 47 | impl std::fmt::Display for PublicKey { 48 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 49 | write!(f, "0x{}", if self.0 .1 { 3 } else { 2 })?; 50 | for byte in self.0 .0.to_repr().as_ref().iter().rev() { 51 | write!(f, "{:02x}", byte)?; 52 | } 53 | Ok(()) 54 | } 55 | } 56 | 57 | impl FromStr for PublicKey { 58 | type Err = ParsePublicKeyError; 59 | fn from_str(mut s: &str) -> Result { 60 | if s.len() != 67 { 61 | return Err(ParsePublicKeyError::Invalid); 62 | } 63 | let oddity = if s.starts_with("0x3") { 64 | true 65 | } else if s.starts_with("0x2") { 66 | false 67 | } else { 68 | return Err(ParsePublicKeyError::Invalid); 69 | }; 70 | s = &s[3..]; 71 | let bytes = (0..32) 72 | .map(|i| u8::from_str_radix(&s[2 * i..2 * i + 2], 16)) 73 | .rev() 74 | .collect::, std::num::ParseIntError>>() 75 | .map_err(|_| ParsePublicKeyError::Invalid)?; 76 | let mut repr = ZkScalar::zero().to_repr(); 77 | repr.as_mut().clone_from_slice(&bytes); 78 | Ok(PublicKey(PointCompressed( 79 | ZkScalar::from_repr(repr).unwrap(), 80 | oddity, 81 | ))) 82 | } 83 | } 84 | 85 | impl ZkSignatureScheme for JubJub { 86 | type Pub = PublicKey; 87 | type Priv = PrivateKey; 88 | type Sig = Signature; 89 | fn generate_keys(seed: &[u8]) -> (PublicKey, PrivateKey) { 90 | let randomness = hash_to_scalar(seed); 91 | let scalar = hash_to_scalar(randomness.to_repr().as_ref()); 92 | let point = BASE.multiply(&scalar); 93 | let pk = PublicKey(point.compress()); 94 | ( 95 | pk, 96 | PrivateKey { 97 | public_key: point, 98 | randomness, 99 | scalar, 100 | }, 101 | ) 102 | } 103 | fn sign(sk: &PrivateKey, message: ZkScalar) -> Signature { 104 | // r=H(b,M) 105 | let r = H::hash(&[sk.randomness, message]); 106 | 107 | // R=rB 108 | let rr = BASE.multiply(&r); 109 | 110 | // h=H(R,A,M) 111 | let h = H::hash(&[rr.0, rr.1, sk.public_key.0, sk.public_key.1, message]); 112 | 113 | // s = (r + ha) mod ORDER 114 | let mut s = BigUint::from_bytes_le(r.to_repr().as_ref()); 115 | let mut ha = BigUint::from_bytes_le(h.to_repr().as_ref()); 116 | ha.mul_assign(&BigUint::from_bytes_le(sk.scalar.to_repr().as_ref())); 117 | s.add_assign(&ha); 118 | s = s.mod_floor(&*ORDER); 119 | let s_as_fr = { 120 | let s_bytes = s.to_bytes_le(); 121 | let mut s_repr = ZkScalarRepr([0u8; 32]); 122 | s_repr.0[0..s_bytes.len()].copy_from_slice(&s_bytes); 123 | ZkScalar::from_repr(s_repr).unwrap() 124 | }; 125 | 126 | Signature { r: rr, s: s_as_fr } 127 | } 128 | fn verify(pk: &PublicKey, message: ZkScalar, sig: &Signature) -> bool { 129 | let pk = pk.0.decompress(); 130 | 131 | if !pk.is_on_curve() || !sig.r.is_on_curve() { 132 | return false; 133 | } 134 | 135 | // h=H(R,A,M) 136 | let h = H::hash(&[sig.r.0, sig.r.1, pk.0, pk.1, message]); 137 | 138 | let sb = BASE.multiply(&sig.s); 139 | 140 | let mut r_plus_ha = pk.multiply(&h); 141 | r_plus_ha.add_assign(&sig.r); 142 | 143 | r_plus_ha == sb 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use super::*; 150 | 151 | #[test] 152 | fn test_jubjub_public_key_compression() { 153 | let p1 = BASE.multiply(&ZkScalar::from(123_u64)); 154 | let p2 = p1.compress().decompress(); 155 | 156 | assert_eq!(p1, p2); 157 | } 158 | 159 | #[test] 160 | fn test_jubjub_signature_verification() { 161 | let (pk, sk) = JubJub::::generate_keys(b"ABC"); 162 | let msg = ZkScalar::from(123456); 163 | let fake_msg = ZkScalar::from(123457); 164 | let sig = JubJub::::sign(&sk, msg); 165 | 166 | assert!(JubJub::::verify(&pk, msg, &sig)); 167 | assert!(!JubJub::::verify( 168 | &pk, fake_msg, &sig 169 | )); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/zk/poseidon/mod.rs: -------------------------------------------------------------------------------- 1 | mod params; 2 | pub use params::*; 3 | 4 | use super::ZkScalar; 5 | use ff::Field; 6 | use std::ops::MulAssign; 7 | 8 | #[derive(Debug, Clone, PartialEq)] 9 | struct PoseidonState { 10 | constants_offset: usize, 11 | present_elements: u64, 12 | elements: Vec, 13 | } 14 | 15 | impl PoseidonState { 16 | pub fn new(elems: &[ZkScalar]) -> Self { 17 | let mut elements = elems.to_vec(); 18 | elements.insert(0, ZkScalar::zero()); 19 | Self { 20 | present_elements: 0u64, 21 | constants_offset: 0, 22 | elements, 23 | } 24 | } 25 | 26 | pub fn hash(&mut self) -> ZkScalar { 27 | let params = PoseidonParams::for_width(self.elements.len()).unwrap(); 28 | 29 | self.elements[0] = ZkScalar::from(self.present_elements); 30 | 31 | for _ in 0..params.full_rounds / 2 { 32 | self.full_round(params); 33 | } 34 | 35 | for _ in 0..params.partial_rounds { 36 | self.partial_round(params); 37 | } 38 | 39 | for _ in 0..params.full_rounds / 2 { 40 | self.full_round(params); 41 | } 42 | 43 | self.elements[1] 44 | } 45 | 46 | fn full_round(&mut self, params: &PoseidonParams) { 47 | self.add_round_constants(params); 48 | self.elements.iter_mut().for_each(quintic_s_box); 49 | self.product_mds(params); 50 | } 51 | 52 | fn partial_round(&mut self, params: &PoseidonParams) { 53 | self.add_round_constants(params); 54 | quintic_s_box(&mut self.elements[0]); 55 | self.product_mds(params); 56 | } 57 | 58 | fn add_round_constants(&mut self, params: &PoseidonParams) { 59 | self.elements.iter_mut().for_each(|l| { 60 | *l += params.round_constants[self.constants_offset]; 61 | self.constants_offset += 1; 62 | }); 63 | } 64 | 65 | fn product_mds(&mut self, params: &PoseidonParams) { 66 | let mut result = vec![ZkScalar::from(0u64); self.elements.len()]; 67 | for j in 0..self.elements.len() { 68 | for k in 0..self.elements.len() { 69 | result[j] += params.mds_constants[j][k] * self.elements[k]; 70 | } 71 | } 72 | self.elements.copy_from_slice(&result); 73 | } 74 | } 75 | 76 | fn quintic_s_box(l: &mut ZkScalar) { 77 | let mut tmp = *l; 78 | tmp = tmp.square(); // l^2 79 | tmp = tmp.square(); // l^4 80 | l.mul_assign(&tmp); // l^5 81 | } 82 | 83 | pub fn poseidon(vals: &[ZkScalar]) -> ZkScalar { 84 | let mut h = PoseidonState::new(vals); 85 | h.hash() 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use super::*; 91 | use ff::{Field, PrimeField}; 92 | 93 | #[test] 94 | fn test_hash_deterministic() { 95 | let mut h = PoseidonState::new(&[ 96 | ZkScalar::one(), 97 | ZkScalar::one(), 98 | ZkScalar::one(), 99 | ZkScalar::one(), 100 | ]); 101 | 102 | let mut h2 = h.clone(); 103 | let result = h.hash(); 104 | 105 | assert_eq!(result, h2.hash()); 106 | } 107 | 108 | #[test] 109 | fn test_hash_reflects_changes() { 110 | for arity in 1..MAX_ARITY + 1 { 111 | let mut vals = vec![ZkScalar::zero(); arity]; 112 | let original = poseidon(&vals); 113 | for i in 0..vals.len() { 114 | vals[i] = ZkScalar::one(); 115 | assert!(poseidon(&vals) != original); 116 | } 117 | } 118 | } 119 | 120 | #[test] 121 | fn test_hash_samples() { 122 | let expected = [ 123 | "27570695323925995271701303589514430472678239829854264417883970952440292573348", 124 | "6587584068506488869767403662460111870851709789694140241572542699619538605403", 125 | "11065162352055215342882956665028806373710857144056793315618843991574034541745", 126 | "27235437669367044799899874028200860893259633691548428184978833555844239099210", 127 | "39122459949963443953695513827515422590145971775731164693081784821001500765271", 128 | "14822541353598610072073758561600133199190898904019472753356348939736178856242", 129 | "32119039894111509393883349238591117345166479914896997011437787663480858229324", 130 | "43492451727584886720328582747486156090763899250669626113572962177392830153672", 131 | "23782521420058920239581486714235942233162905749917547091367129332109148150964", 132 | "1950261058989975858181381159018748926889722679795466088362775920975943983890", 133 | "47763254094198808066374497304963224993617822320088130264863862435119574697678", 134 | "44035521596650126254580286193043646937530018324533162959282567836364656349620", 135 | "45248278075433906869650374149660178834237900630357739057386839430392516698709", 136 | "30558481537294127342952125056358924225581206938869947160862017954746718634085", 137 | "10702554392571105609953066033536365418563149392782994983402406449789876497692", 138 | "34319425623279664398659085846739236990635100324667226409415519671072072962346", 139 | ] 140 | .into_iter() 141 | .map(|s| ZkScalar::from_str_vartime(s).unwrap()) 142 | .collect::>(); 143 | 144 | let results = (1..MAX_ARITY + 1) 145 | .map(|count| { 146 | poseidon( 147 | &(0..count) 148 | .map(|i| ZkScalar::from(i as u64)) 149 | .collect::>(), 150 | ) 151 | }) 152 | .collect::>(); 153 | 154 | assert_eq!(results, expected); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/node/context.rs: -------------------------------------------------------------------------------- 1 | use super::{Firewall, NodeOptions, OutgoingSender, Peer, PeerAddress, PeerInfo, Timestamp}; 2 | use crate::blockchain::{BlockAndPatch, Blockchain, BlockchainError, TransactionStats}; 3 | use crate::client::messages::SocialProfiles; 4 | use crate::core::{ContractPayment, Header, Signer, TransactionAndDelta}; 5 | use crate::crypto::SignatureScheme; 6 | use crate::utils; 7 | use crate::wallet::Wallet; 8 | use crate::zk; 9 | use rand::seq::IteratorRandom; 10 | use rand::RngCore; 11 | use std::collections::HashMap; 12 | use std::sync::Arc; 13 | 14 | use crate::client::messages::Puzzle; 15 | 16 | pub type BlockPuzzle = (BlockAndPatch, Puzzle); 17 | 18 | pub struct NodeContext { 19 | pub firewall: Firewall, 20 | pub social_profiles: SocialProfiles, 21 | pub opts: NodeOptions, 22 | pub network: String, 23 | pub pub_key: ::Pub, 24 | pub address: Option, // None means node is not exposed on the Internet 25 | pub shutdown: bool, 26 | pub outgoing: Arc, 27 | pub blockchain: B, 28 | pub wallet: Option, 29 | pub peers: HashMap, 30 | pub timestamp_offset: i32, 31 | pub miner_puzzle: Option, 32 | 33 | pub mempool: HashMap, 34 | pub zero_mempool: HashMap, 35 | pub contract_payment_mempool: HashMap, 36 | 37 | pub outdated_since: Option, 38 | pub banned_headers: HashMap, 39 | } 40 | 41 | impl NodeContext { 42 | pub fn network_timestamp(&self) -> u32 { 43 | (utils::local_timestamp() as i32 + self.timestamp_offset) as u32 44 | } 45 | pub fn punish_bad_behavior(&mut self, bad_peer: PeerAddress, secs: u32, reason: &str) { 46 | log::warn!("Peer {} is behaving bad! Reason: {}", bad_peer, reason); 47 | log::warn!("Punishing {} for {} seconds...", bad_peer, secs); 48 | self.firewall.punish_bad(bad_peer.0.ip(), secs); 49 | } 50 | pub fn punish_unresponsive(&mut self, bad_peer: PeerAddress) { 51 | log::warn!("Peer {} is unresponsive!", bad_peer); 52 | log::warn!( 53 | "Punishing {} for {} seconds...", 54 | bad_peer, 55 | self.opts.no_response_punish 56 | ); 57 | self.firewall.punish_unresponsive( 58 | bad_peer.0.ip(), 59 | self.opts.no_response_punish, 60 | self.opts.max_punish, 61 | ); 62 | } 63 | pub fn get_info(&self) -> Result { 64 | Ok(PeerInfo { 65 | height: self.blockchain.get_height()?, 66 | power: self.blockchain.get_power()?, 67 | }) 68 | } 69 | pub fn random_peers(&self, rng: &mut R, count: usize) -> Vec { 70 | self.active_peers() 71 | .into_iter() 72 | .choose_multiple(rng, count) 73 | .into_iter() 74 | .collect() 75 | } 76 | pub fn active_peers(&self) -> Vec { 77 | self.peers 78 | .values() 79 | .cloned() 80 | .filter(|p| { 81 | self.firewall.outgoing_permitted(p.address) && Some(p.address) != self.address 82 | }) 83 | .collect() 84 | } 85 | 86 | pub fn refresh(&mut self) -> Result<(), BlockchainError> { 87 | for p in self.peers.clone().into_keys() { 88 | if self.firewall.is_peer_dead(p) { 89 | self.peers.remove(&p); 90 | } 91 | } 92 | 93 | let ts = self.network_timestamp(); 94 | for (h, banned_at) in self.banned_headers.clone().into_iter() { 95 | if ts - banned_at > self.opts.state_unavailable_ban_time { 96 | self.banned_headers.remove(&h); 97 | } 98 | } 99 | 100 | self.firewall.refresh(); 101 | self.blockchain 102 | .cleanup_contract_payment_mempool(&mut self.contract_payment_mempool)?; 103 | self.blockchain.cleanup_mempool(&mut self.mempool)?; 104 | self.blockchain 105 | .cleanup_zero_mempool(&mut self.zero_mempool)?; 106 | 107 | if let Some(max) = self.opts.tx_max_time_alive { 108 | for (tx, stats) in self.mempool.clone().into_iter() { 109 | if ts - stats.first_seen > max { 110 | self.mempool.remove(&tx); 111 | } 112 | } 113 | for (tx, stats) in self.contract_payment_mempool.clone().into_iter() { 114 | if ts - stats.first_seen > max { 115 | self.contract_payment_mempool.remove(&tx); 116 | } 117 | } 118 | for (tx, stats) in self.zero_mempool.clone().into_iter() { 119 | if ts - stats.first_seen > max { 120 | self.zero_mempool.remove(&tx); 121 | } 122 | } 123 | } 124 | Ok(()) 125 | } 126 | 127 | pub fn get_puzzle(&mut self, wallet: Wallet) -> Result, BlockchainError> { 128 | let ts = self.network_timestamp(); 129 | let draft = self 130 | .blockchain 131 | .draft_block(ts, &self.mempool, &wallet, true)?; 132 | if let Some(draft) = draft { 133 | let puzzle = Puzzle { 134 | key: hex::encode(self.blockchain.pow_key(draft.block.header.number)?), 135 | blob: hex::encode(bincode::serialize(&draft.block.header).unwrap()), 136 | offset: 80, 137 | size: 8, 138 | target: draft.block.header.proof_of_work.target, 139 | }; 140 | Ok(Some((draft, puzzle))) 141 | } else { 142 | Ok(None) 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/wallet/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::core::{ 2 | Address, ContractId, ContractPayment, ContractUpdate, Money, PaymentDirection, Signature, 3 | Signer, Transaction, TransactionAndDelta, TransactionData, ZkSigner, 4 | }; 5 | use crate::crypto::SignatureScheme; 6 | use crate::crypto::ZkSignatureScheme; 7 | use crate::zk; 8 | 9 | #[derive(Clone)] 10 | pub struct Wallet { 11 | seed: Vec, 12 | private_key: ::Priv, 13 | zk_private_key: ::Priv, 14 | address: ::Pub, 15 | zk_address: ::Pub, 16 | } 17 | 18 | impl Wallet { 19 | pub fn new(seed: Vec) -> Self { 20 | let (pk, sk) = Signer::generate_keys(&seed); 21 | let (zk_pk, zk_sk) = ZkSigner::generate_keys(&seed); 22 | Self { 23 | seed, 24 | address: pk, 25 | zk_address: zk_pk, 26 | private_key: sk, 27 | zk_private_key: zk_sk, 28 | } 29 | } 30 | pub fn get_address(&self) -> Address { 31 | Address::PublicKey(self.address.clone()) 32 | } 33 | pub fn get_zk_address(&self) -> ::Pub { 34 | self.zk_address.clone() 35 | } 36 | pub fn sign(&self, tx: &mut Transaction) { 37 | let bytes = bincode::serialize(&tx).unwrap(); 38 | tx.sig = Signature::Signed(Signer::sign(&self.private_key, &bytes)); 39 | } 40 | pub fn create_transaction( 41 | &self, 42 | dst: Address, 43 | amount: Money, 44 | fee: Money, 45 | nonce: u32, 46 | ) -> TransactionAndDelta { 47 | let mut tx = Transaction { 48 | src: self.get_address(), 49 | data: TransactionData::RegularSend { dst, amount }, 50 | nonce, 51 | fee, 52 | sig: Signature::Unsigned, 53 | }; 54 | self.sign(&mut tx); 55 | TransactionAndDelta { 56 | tx, 57 | state_delta: None, 58 | } 59 | } 60 | pub fn create_mpn_transaction( 61 | &self, 62 | from_index: u32, 63 | to_index: u32, 64 | to: ::Pub, 65 | amount: Money, 66 | fee: Money, 67 | nonce: u64, 68 | ) -> zk::ZeroTransaction { 69 | let mut tx = zk::ZeroTransaction { 70 | nonce, 71 | src_index: from_index, 72 | dst_index: to_index, 73 | dst_pub_key: to, 74 | amount, 75 | fee, 76 | sig: Default::default(), 77 | }; 78 | tx.sign(&self.zk_private_key); 79 | tx 80 | } 81 | pub fn create_contract( 82 | &self, 83 | contract: zk::ZkContract, 84 | initial_state: zk::ZkDataPairs, 85 | fee: Money, 86 | nonce: u32, 87 | ) -> TransactionAndDelta { 88 | let mut tx = Transaction { 89 | src: self.get_address(), 90 | data: TransactionData::CreateContract { contract }, 91 | nonce, 92 | fee, 93 | sig: Signature::Unsigned, 94 | }; 95 | self.sign(&mut tx); 96 | TransactionAndDelta { 97 | tx, 98 | state_delta: Some(initial_state.as_delta()), 99 | } 100 | } 101 | 102 | #[allow(clippy::too_many_arguments)] 103 | pub fn call_function( 104 | &self, 105 | contract_id: ContractId, 106 | function_id: u32, 107 | state_delta: zk::ZkDeltaPairs, 108 | next_state: zk::ZkCompressedState, 109 | proof: zk::ZkProof, 110 | exec_fee: Money, 111 | miner_fee: Money, 112 | nonce: u32, 113 | ) -> TransactionAndDelta { 114 | let (_, sk) = Signer::generate_keys(&self.seed); 115 | let mut tx = Transaction { 116 | src: self.get_address(), 117 | data: TransactionData::UpdateContract { 118 | contract_id, 119 | updates: vec![ContractUpdate::FunctionCall { 120 | function_id, 121 | next_state, 122 | proof, 123 | fee: exec_fee, 124 | }], 125 | }, 126 | nonce, 127 | fee: miner_fee, 128 | sig: Signature::Unsigned, 129 | }; 130 | let bytes = bincode::serialize(&tx).unwrap(); 131 | tx.sig = Signature::Signed(Signer::sign(&sk, &bytes)); 132 | TransactionAndDelta { 133 | tx, 134 | state_delta: Some(state_delta), 135 | } 136 | } 137 | 138 | #[allow(clippy::too_many_arguments)] 139 | pub fn pay_contract( 140 | &self, 141 | contract_id: ContractId, 142 | address_index: u32, 143 | nonce: u32, 144 | amount: Money, 145 | fee: Money, 146 | withdraw: bool, 147 | ) -> ContractPayment { 148 | let mut tx = ContractPayment { 149 | address: self.private_key.clone().into(), 150 | zk_address: self.zk_private_key.clone().into(), 151 | zk_address_index: address_index, 152 | contract_id, 153 | nonce, 154 | amount, 155 | fee, 156 | direction: if withdraw { 157 | PaymentDirection::Withdraw(None) 158 | } else { 159 | PaymentDirection::Deposit(None) 160 | }, 161 | }; 162 | let bytes = bincode::serialize(&tx).unwrap(); 163 | match &mut tx.direction { 164 | PaymentDirection::Withdraw(sig) => { 165 | *sig = Some(ZkSigner::sign( 166 | &self.zk_private_key, 167 | crate::zk::hash_to_scalar(&bytes), 168 | )); 169 | } 170 | PaymentDirection::Deposit(sig) => { 171 | *sig = Some(Signer::sign(&self.private_key, &bytes)); 172 | } 173 | } 174 | tx 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ℤ - Bazuka! 2 | 3 | [![Bazuka](https://github.com/zeeka-network/bazuka/actions/workflows/actions.yml/badge.svg)](https://github.com/zeeka-network/bazuka/actions/workflows/actions.yml) 4 | [![codecov](https://codecov.io/gh/zeeka-network/bazuka/branch/master/graph/badge.svg?token=8XTLET5GQN)](https://codecov.io/gh/zeeka-network/bazuka) 5 | 6 | Bazuka is a wallet and node software for the Zeeka (ℤ) Protocol. Zeeka is a novel 7 | layer-1 cryptocurrency which uses Zero-Knowledge proofs as the backend of its 8 | smart-contract (I.e Zero Contracts). 9 | 10 | Bazuka ensures the availability of latest contract-states, so that they remain 11 | public and everybody is able to update and build on them, making Zeeka a more 12 | decentralized protocol compared to similar projects. 13 | 14 | ### Links 15 | 16 | - Website: https://zeeka.io 17 | - Whitepaper: https://hackmd.io/@keyvank/zeeka 18 | - Discord: https://discord.gg/4gbf9gZh8H 19 | 20 | ### How to run a Bazuka node? 21 | 22 | **NOTE:** ***Bazuka! is a very early-stage implementation of the Zeeka Protocol, 23 | highly unstable and not yet complete!*** 24 | 25 | If you only want to run a Zeeka node and do not want to execute Zero Contract or 26 | mine Zeeka, you will only need to install `bazuka` (This repo). In case you also 27 | want to mine Zeeka, you will need to install ![zoro](https://github.com/zeeka-network/zoro) 28 | (The Main Payment Network executor) and also the ![uzi-miner](https://github.com/zeeka-network/uzi-miner) 29 | (A RandomX CPU miner). 30 | 31 | **How to install `bazuka`?** 32 | 33 | * Prepare a Linux machine. 34 | * Make sure you have installed `libssl-dev` and `cmake` packages. 35 | * Install the Rust toolchain (https://rustup.rs/) 36 | * Clone the `bazuka` repo: `git clone https://github.com/zeeka-network/bazuka`. 37 | * Compile and install: `cd bazuka && cargo install --path .` 38 | 39 | Now if you want to join the `debug` testnet, you first have to initialize your 40 | node by running: 41 | 42 | ```sh 43 | bazuka init --seed [your seed phrase] --network debug --node 127.0.0.1:8765 44 | ``` 45 | 46 | Your regular and zero-knowledge private-keys are derived from the `--seed` value, 47 | so **don't forget to keep them somewhere safe**! You can choose your target network 48 | (E.g `mainnet`, `chaos`, `debug` or etc.) through the `--network` option. You should 49 | also choose the node which you want to send your transactions to, through the 50 | `--node` option. (Here we choose our local node which we will run later) 51 | 52 | Run your node: 53 | 54 | ```sh 55 | bazuka node --listen 0.0.0.0:8765 --external [your external ip]:8765 \ 56 | --network debug --db ~/.bazuka-debug --bootstrap [bootstrap node 1] --bootstrap [bootstrap node 2] ... 57 | ``` 58 | 59 | You can use the nodes introduced by the community as your `--bootstrap` nodes. 60 | You either have to run your node on a machine with a static IP, or configure a NAT 61 | Virtual Server in order to expose your node on a public IP. Specify your public IP 62 | through the `--external` option. 63 | 64 | Highly recommended to also provide your Discord handle through the 65 | `--discord-handle` flag. By providing your handle, you will leave our bots a 66 | way to contact you regarding the problems you may have in your node and its status. 67 | 68 | ### What is the Main Payment Network? 69 | 70 | The Main Payment Network (MPN) is a special, builtin smart-contract that is 71 | created in the genesis-block of the Zeeka Protocol. It manages a merkle-tree of 72 | millions of accounts that can transfer ℤ with each other with nearly zero-cost 73 | transactions. It uses the Groth16 proving system and has a fixed number of 74 | transaction slots per update. 75 | 76 | People who want to transfer their Zeeka tokens cheaply, would need to deposit 77 | their funds to MPN through the `deposit` command. 78 | 79 | ### How to mine ℤeeka? 80 | 81 | In order to be a miner, besides working on a PoW puzzle, you will also need to 82 | execute the MPN contract on each block you generate. The `zoro` software is 83 | a CPU-executor of MPN contract. In order to run `zoro`, you'll need a machine 84 | with at least 32GB of RAM. If you want to be competitive you'll also need a 85 | competitive CPU. (In future versions, GPU will be used instead of CPU) 86 | 87 | 1. Make sure Bazuka is the latest version. 88 | 89 | ``` 90 | cd bazuka 91 | git pull origin master 92 | cargo install --path . 93 | ``` 94 | 95 | If you get a `DifferentGenesis` error, it means that the genesis block has changed 96 | in the updated software. So you need to start fresh. Remove the `~/.bazuka-debug` 97 | folder by running: `rm -rf ~/.bazuka-debug` 98 | 99 | Now run Bazuka again. 100 | 101 | 2. Install the MPN executor (`zoro`) 102 | 103 | ``` 104 | git clone https://github.com/zeeka-network/zoro 105 | cd zoro 106 | cargo install --path . 107 | ``` 108 | 109 | 3. Download the proving parameters 110 | 111 | - Payment parameters (~700MB): https://drive.google.com/file/d/1sR-dJlr4W_A0sk37NkZaZm8UncMxqM-0/view?usp=sharing 112 | - Update parameters (~6GB): https://drive.google.com/file/d/149tUhC0oXJxsXDnx7vODkOZtIYzC_5HO/view?usp=sharing 113 | 114 | 115 | 4. Run `zoro` beside your node 116 | 117 | ```sh 118 | zoro --node 127.0.0.1:8765 --seed [seed phrase for the executor account] --network debug \ 119 | --update-circuit-params [path to update_params.dat] --payment-circuit-params [path to payment_params.dat] \ 120 | --db [absolute path to ~/.bazuka-debug] 121 | ``` 122 | 123 | (Note: The seed phrase for the executor account needs to be different from the 124 | seed you use for your node!) 125 | 126 | 5. After a new block is generated, the `uzi-miner` should start working on the PoW 127 | puzzle, so you will also need to have `uzi-miner` running on your system: 128 | 129 | ``` 130 | git clone https://github.com/zeeka-network/uzi-miner 131 | cd uzi-miner 132 | cargo install --path . 133 | uzi-miner --node 127.0.0.1:8765 --threads 32 134 | ``` 135 | 136 | (Note: Change number of `--threads` based on the spec of your system) 137 | -------------------------------------------------------------------------------- /src/client/messages.rs: -------------------------------------------------------------------------------- 1 | use crate::blockchain::ZkBlockchainPatch; 2 | use crate::consensus::pow::Difficulty; 3 | use crate::core::{ 4 | Account, Address, Block, ContractId, ContractPayment, Header, Money, TransactionAndDelta, 5 | }; 6 | use crate::zk; 7 | use std::collections::HashMap; 8 | 9 | use super::{Peer, PeerAddress, PeerInfo}; 10 | use serde::{Deserialize, Serialize}; 11 | 12 | #[derive(Deserialize, Serialize, Debug, Clone, Default)] 13 | pub struct SocialProfiles { 14 | pub discord: Option, 15 | } 16 | 17 | #[derive(Deserialize, Serialize, Debug, Clone)] 18 | pub struct GetStatsRequest {} 19 | 20 | #[derive(Deserialize, Serialize, Debug, Clone)] 21 | pub struct GetStatsResponse { 22 | pub social_profiles: SocialProfiles, 23 | pub height: u64, 24 | pub power: u128, 25 | pub next_reward: Money, 26 | pub timestamp: u32, 27 | pub version: String, 28 | } 29 | 30 | #[derive(Deserialize, Serialize, Debug, Clone)] 31 | pub struct GetAccountRequest { 32 | pub address: String, 33 | } 34 | 35 | #[derive(Deserialize, Serialize, Debug, Clone)] 36 | pub struct GetAccountResponse { 37 | pub account: Account, 38 | } 39 | 40 | #[derive(Deserialize, Serialize, Debug, Clone)] 41 | pub struct GetMpnAccountRequest { 42 | pub index: u32, 43 | } 44 | 45 | #[derive(Deserialize, Serialize, Debug, Clone)] 46 | pub struct GetMpnAccountResponse { 47 | pub account: zk::MpnAccount, 48 | } 49 | 50 | #[derive(Deserialize, Serialize, Debug, Clone)] 51 | pub struct PostMinerSolutionRequest { 52 | pub nonce: String, 53 | } 54 | 55 | #[derive(Deserialize, Serialize, Debug, Clone)] 56 | pub struct PostMinerSolutionResponse {} 57 | 58 | #[derive(Deserialize, Serialize, Debug, Clone)] 59 | pub struct GetMinerPuzzleRequest {} 60 | 61 | #[derive(Deserialize, Serialize, Debug, Clone)] 62 | pub struct GetMinerPuzzleResponse { 63 | pub puzzle: Option, 64 | } 65 | 66 | #[derive(Deserialize, Serialize, Debug, Clone)] 67 | pub struct Puzzle { 68 | pub key: String, // Puzzle key encoded in hex 69 | pub blob: String, // Blob encoded in hex 70 | pub offset: usize, // From which byte the nonce starts? 71 | pub size: usize, // How big is the nonce? (Bytes) 72 | pub target: Difficulty, // Difficulty target 73 | } 74 | 75 | #[derive(Deserialize, Serialize, Debug, Clone)] 76 | pub struct PostPeerRequest { 77 | pub address: PeerAddress, 78 | pub info: PeerInfo, 79 | pub timestamp: u32, 80 | } 81 | 82 | #[derive(Deserialize, Serialize, Debug, Clone)] 83 | pub struct PostPeerResponse { 84 | pub info: PeerInfo, 85 | pub timestamp: u32, 86 | } 87 | 88 | #[derive(Deserialize, Serialize, Debug, Clone)] 89 | pub struct GetPeersRequest {} 90 | 91 | #[derive(Deserialize, Serialize, Debug, Clone)] 92 | pub struct GetPeersResponse { 93 | pub peers: Vec, 94 | } 95 | 96 | #[derive(Deserialize, Serialize, Debug, Clone)] 97 | pub struct PostBlockRequest { 98 | pub block: Block, 99 | pub patch: ZkBlockchainPatch, 100 | } 101 | 102 | #[derive(Deserialize, Serialize, Debug, Clone)] 103 | pub struct PostBlockResponse {} 104 | 105 | #[derive(Deserialize, Serialize, Debug, Clone)] 106 | pub struct GetBlocksRequest { 107 | pub since: u64, 108 | pub count: u64, 109 | } 110 | 111 | #[derive(Deserialize, Serialize, Debug, Clone)] 112 | pub struct GetBlocksResponse { 113 | pub blocks: Vec, 114 | } 115 | 116 | #[derive(Deserialize, Serialize, Debug, Clone)] 117 | pub struct GetOutdatedHeightsRequest {} 118 | 119 | #[derive(Deserialize, Serialize, Debug, Clone)] 120 | pub struct GetOutdatedHeightsResponse { 121 | pub outdated_heights: HashMap, 122 | } 123 | 124 | #[derive(Deserialize, Serialize, Debug, Clone)] 125 | pub struct GetStatesRequest { 126 | pub outdated_heights: HashMap, 127 | pub to: String, 128 | } 129 | 130 | #[derive(Deserialize, Serialize, Debug, Clone)] 131 | pub struct GetStatesResponse { 132 | pub patch: ZkBlockchainPatch, 133 | } 134 | 135 | #[derive(Deserialize, Serialize, Debug, Clone)] 136 | pub struct GetHeadersRequest { 137 | pub since: u64, 138 | pub count: u64, 139 | } 140 | 141 | #[derive(Deserialize, Serialize, Debug, Clone)] 142 | pub struct GetHeadersResponse { 143 | pub headers: Vec
, 144 | pub pow_keys: Vec>, 145 | } 146 | 147 | #[derive(Deserialize, Serialize, Debug, Clone)] 148 | pub struct GetBalanceRequest { 149 | pub addr: Address, 150 | } 151 | 152 | #[derive(Deserialize, Serialize, Debug, Clone)] 153 | pub struct GetBalanceResponse { 154 | pub amount: Money, 155 | } 156 | 157 | #[derive(Deserialize, Serialize, Debug, Clone)] 158 | pub struct TransactRequest { 159 | pub tx_delta: TransactionAndDelta, 160 | } 161 | 162 | #[derive(Deserialize, Serialize, Debug)] 163 | pub struct TransactResponse {} 164 | 165 | #[derive(Deserialize, Serialize, Debug, Clone)] 166 | pub struct TransactZeroRequest { 167 | pub tx: zk::ZeroTransaction, 168 | } 169 | 170 | #[derive(Deserialize, Serialize, Debug)] 171 | pub struct TransactZeroResponse {} 172 | 173 | #[derive(Deserialize, Serialize, Debug, Clone)] 174 | pub struct ShutdownRequest {} 175 | 176 | #[derive(Deserialize, Serialize, Debug)] 177 | pub struct ShutdownResponse {} 178 | 179 | #[derive(Deserialize, Serialize, Debug, Clone)] 180 | pub struct GetZeroMempoolRequest {} 181 | 182 | #[derive(Deserialize, Serialize, Debug, Clone)] 183 | pub struct GetZeroMempoolResponse { 184 | pub updates: Vec, 185 | pub payments: Vec, 186 | } 187 | 188 | #[derive(Deserialize, Serialize, Debug, Clone)] 189 | pub struct TransactContractPaymentRequest { 190 | pub tx: ContractPayment, 191 | } 192 | 193 | #[derive(Deserialize, Serialize, Debug)] 194 | pub struct TransactContractPaymentResponse {} 195 | 196 | #[derive(Deserialize, Serialize, Debug, Clone)] 197 | pub struct GetMempoolRequest {} 198 | 199 | #[derive(Deserialize, Serialize, Debug, Clone)] 200 | pub struct GetMempoolResponse { 201 | pub tx: Vec, 202 | pub tx_zk: Vec, 203 | pub zk: Vec, 204 | } 205 | -------------------------------------------------------------------------------- /src/node/test/simulation.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use crate::blockchain::{BlockchainConfig, KvStoreChain}; 4 | use crate::client::{messages::SocialProfiles, BazukaClient}; 5 | use crate::config; 6 | use crate::core::Signer; 7 | use crate::crypto::SignatureScheme; 8 | use crate::db::RamKvStore; 9 | use crate::wallet::Wallet; 10 | 11 | use std::sync::Arc; 12 | use tokio::sync::RwLock; 13 | use tokio::time::{sleep, Duration}; 14 | 15 | struct Node { 16 | addr: PeerAddress, 17 | incoming: BazukaClient, 18 | outgoing: mpsc::UnboundedReceiver, 19 | } 20 | 21 | pub struct NodeOpts { 22 | pub config: BlockchainConfig, 23 | pub priv_key: ::Priv, 24 | pub wallet: Option, 25 | pub addr: u16, 26 | pub bootstrap: Vec, 27 | pub timestamp_offset: i32, 28 | } 29 | 30 | fn create_test_node( 31 | opts: NodeOpts, 32 | ) -> (impl futures::Future>, Node) { 33 | let addr = PeerAddress(SocketAddr::from(([127, 0, 0, 1], opts.addr))); 34 | let chain = KvStoreChain::new(RamKvStore::new(), opts.config).unwrap(); 35 | let (inc_send, inc_recv) = mpsc::unbounded_channel::(); 36 | let (out_send, out_recv) = mpsc::unbounded_channel::(); 37 | let simulator_options = config::node::get_simulator_options(); 38 | let node = node_create( 39 | simulator_options.clone(), 40 | "simulator", 41 | Some(addr), 42 | opts.priv_key.clone(), 43 | opts.bootstrap 44 | .iter() 45 | .map(|p| PeerAddress(SocketAddr::from(([127, 0, 0, 1], *p)))) 46 | .collect(), 47 | chain, 48 | opts.timestamp_offset, 49 | opts.wallet, 50 | SocialProfiles::default(), 51 | inc_recv, 52 | out_send, 53 | ); 54 | ( 55 | node, 56 | Node { 57 | addr, 58 | incoming: BazukaClient { 59 | peer: addr, 60 | sender: Arc::new(OutgoingSender { 61 | chan: inc_send, 62 | network: "simulator".into(), 63 | priv_key: opts.priv_key, 64 | }), 65 | }, 66 | outgoing: out_recv, 67 | }, 68 | ) 69 | } 70 | 71 | async fn route( 72 | rules: Arc>>, 73 | src: PeerAddress, 74 | mut outgoing: mpsc::UnboundedReceiver, 75 | incs: HashMap, 76 | ) -> Result<(), NodeError> { 77 | while let Some(req) = outgoing.recv().await { 78 | let rules = rules.read().await.clone(); 79 | let mut dst = PeerAddress( 80 | req.body 81 | .uri() 82 | .authority() 83 | .unwrap() 84 | .to_string() 85 | .parse() 86 | .unwrap(), 87 | ); 88 | let rule = rules.iter().find(|r| r.applies(&req.body, src, dst)); 89 | 90 | if let Some(rule) = rule { 91 | match rule.action { 92 | Action::Drop => { 93 | continue; 94 | } 95 | Action::Delay(dur) => { 96 | sleep(dur).await; 97 | } 98 | Action::Redirect(port) => { 99 | dst = PeerAddress(SocketAddr::from(([127, 0, 0, 1], port))); 100 | } 101 | } 102 | } 103 | 104 | let (resp_snd, mut resp_rcv) = mpsc::channel::, NodeError>>(1); 105 | let inc_req = NodeRequest { 106 | limit: Limit::default(), 107 | socket_addr: None, 108 | body: req.body, 109 | resp: resp_snd, 110 | }; 111 | if incs[&dst].sender.chan.send(inc_req).is_ok() { 112 | if let Some(answer) = resp_rcv.recv().await { 113 | let _ = req.resp.send(answer).await; 114 | } 115 | } 116 | } 117 | 118 | Ok(()) 119 | } 120 | 121 | #[derive(Clone)] 122 | pub enum Action { 123 | Drop, 124 | Delay(Duration), 125 | Redirect(u16), 126 | } 127 | 128 | #[derive(Clone)] 129 | pub enum Endpoint { 130 | Any, 131 | Peer(u16), 132 | } 133 | 134 | #[derive(Clone)] 135 | pub struct Rule { 136 | pub from: Endpoint, 137 | pub to: Endpoint, 138 | pub url: String, 139 | pub action: Action, 140 | } 141 | 142 | impl Rule { 143 | pub fn drop_all() -> Self { 144 | Rule { 145 | from: Endpoint::Any, 146 | to: Endpoint::Any, 147 | url: "".into(), 148 | action: Action::Drop, 149 | } 150 | } 151 | pub fn drop_url(url: &str) -> Self { 152 | Rule { 153 | from: Endpoint::Any, 154 | to: Endpoint::Any, 155 | url: url.into(), 156 | action: Action::Drop, 157 | } 158 | } 159 | } 160 | 161 | impl Rule { 162 | fn applies(&self, req: &Request, req_from: PeerAddress, req_to: PeerAddress) -> bool { 163 | req.uri().to_string().contains(&self.url) 164 | && match self.from { 165 | Endpoint::Any => true, 166 | Endpoint::Peer(port) => req_from.0.port() == port, 167 | } 168 | && match self.to { 169 | Endpoint::Any => true, 170 | Endpoint::Peer(port) => req_to.0.port() == port, 171 | } 172 | } 173 | } 174 | 175 | pub fn test_network( 176 | rules: Arc>>, 177 | node_opts: Vec, 178 | ) -> ( 179 | impl futures::Future, NodeError>>, 180 | impl futures::Future, NodeError>>, 181 | Vec, 182 | ) { 183 | let (node_futs, nodes): (Vec<_>, Vec) = node_opts 184 | .into_iter() 185 | .map(|node_opts| create_test_node(node_opts)) 186 | .unzip(); 187 | let incs: HashMap<_, _> = nodes.iter().map(|n| (n.addr, n.incoming.clone())).collect(); 188 | let route_futs = nodes 189 | .into_iter() 190 | .map(|n| route(Arc::clone(&rules), n.addr, n.outgoing, incs.clone())) 191 | .collect::>(); 192 | 193 | ( 194 | futures::future::try_join_all(node_futs), 195 | futures::future::try_join_all(route_futs), 196 | incs.into_values().collect(), 197 | ) 198 | } 199 | -------------------------------------------------------------------------------- /src/core/money.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{SYMBOL, UNIT, UNIT_ZEROS}; 2 | use std::ops::{Add, AddAssign, Div, Sub, SubAssign}; 3 | use std::str::FromStr; 4 | use thiserror::Error; 5 | 6 | #[derive( 7 | serde::Serialize, 8 | serde::Deserialize, 9 | Debug, 10 | Clone, 11 | Copy, 12 | PartialEq, 13 | PartialOrd, 14 | Ord, 15 | Eq, 16 | Default, 17 | )] 18 | pub struct Money(pub u64); 19 | 20 | #[derive(Error, Debug)] 21 | pub enum ParseMoneyError { 22 | #[error("money invalid")] 23 | Invalid, 24 | } 25 | 26 | impl std::fmt::Display for Money { 27 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 | let mut s = self.0.to_string(); 29 | while s.len() <= UNIT_ZEROS as usize { 30 | s.insert(0, '0'); 31 | } 32 | s.insert(s.len() - UNIT_ZEROS as usize, '.'); 33 | while let Some(last) = s.chars().last() { 34 | if last == '0' { 35 | s.pop(); 36 | } else { 37 | break; 38 | } 39 | } 40 | if let Some(last) = s.chars().last() { 41 | if last == '.' { 42 | s.push('0'); 43 | } 44 | } 45 | write!(f, "{}{}", s, SYMBOL) 46 | } 47 | } 48 | 49 | impl FromStr for Money { 50 | type Err = ParseMoneyError; 51 | fn from_str(s: &str) -> Result { 52 | let mut s = s.trim().to_string(); 53 | if let Some(dot_pos) = s.find('.') { 54 | if s == "." { 55 | return Err(ParseMoneyError::Invalid); 56 | } 57 | let dot_rpos = s.len() - 1 - dot_pos; 58 | if dot_rpos > UNIT_ZEROS as usize { 59 | return Err(ParseMoneyError::Invalid); 60 | } 61 | for _ in 0..UNIT_ZEROS as usize - dot_rpos { 62 | s.push('0'); 63 | } 64 | s.remove(dot_pos); 65 | Ok(Self(s.parse().map_err(|_| ParseMoneyError::Invalid)?)) 66 | } else { 67 | let as_u64: u64 = s.parse().map_err(|_| ParseMoneyError::Invalid)?; 68 | Ok(Self(as_u64 * UNIT)) 69 | } 70 | } 71 | } 72 | 73 | impl Into for Money { 74 | fn into(self) -> u64 { 75 | self.0 76 | } 77 | } 78 | 79 | impl From for Money { 80 | fn from(val: u64) -> Self { 81 | Self(val) 82 | } 83 | } 84 | 85 | impl AddAssign for Money { 86 | fn add_assign(&mut self, other: Self) { 87 | self.0 += other.0; 88 | } 89 | } 90 | 91 | impl SubAssign for Money { 92 | fn sub_assign(&mut self, other: Self) { 93 | self.0 -= other.0; 94 | } 95 | } 96 | 97 | impl Add for Money { 98 | type Output = Self; 99 | 100 | fn add(self, other: Self) -> Self { 101 | Self(self.0 + other.0) 102 | } 103 | } 104 | 105 | impl Sub for Money { 106 | type Output = Self; 107 | 108 | fn sub(self, other: Self) -> Self { 109 | Self(self.0 - other.0) 110 | } 111 | } 112 | 113 | impl Div for Money { 114 | type Output = Self; 115 | 116 | fn div(self, other: u64) -> Self { 117 | Self(self.0 / other) 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod tests { 123 | use super::*; 124 | 125 | #[test] 126 | fn test_money_to_str() { 127 | assert_eq!(format!("{}", Money(0)), format!("0.0{}", SYMBOL)); 128 | assert_eq!(format!("{}", Money(1)), format!("0.000000001{}", SYMBOL)); 129 | assert_eq!(format!("{}", Money(12)), format!("0.000000012{}", SYMBOL)); 130 | assert_eq!(format!("{}", Money(1234)), format!("0.000001234{}", SYMBOL)); 131 | assert_eq!( 132 | format!("{}", Money(123000000000)), 133 | format!("123.0{}", SYMBOL) 134 | ); 135 | assert_eq!( 136 | format!("{}", Money(123456789)), 137 | format!("0.123456789{}", SYMBOL) 138 | ); 139 | assert_eq!( 140 | format!("{}", Money(1234567898)), 141 | format!("1.234567898{}", SYMBOL) 142 | ); 143 | assert_eq!( 144 | format!("{}", Money(123456789987654321)), 145 | format!("123456789.987654321{}", SYMBOL) 146 | ); 147 | } 148 | 149 | #[test] 150 | fn test_str_to_money() { 151 | assert_eq!("0".parse::().unwrap(), Money(0)); 152 | assert_eq!("0.".parse::().unwrap(), Money(0)); 153 | assert_eq!("0.0".parse::().unwrap(), Money(0)); 154 | assert_eq!("1".parse::().unwrap(), Money(1000000000)); 155 | assert_eq!("1.".parse::().unwrap(), Money(1000000000)); 156 | assert_eq!("1.0".parse::().unwrap(), Money(1000000000)); 157 | assert_eq!("123".parse::().unwrap(), Money(123000000000)); 158 | assert_eq!("123.".parse::().unwrap(), Money(123000000000)); 159 | assert_eq!("123.0".parse::().unwrap(), Money(123000000000)); 160 | assert_eq!("123.1".parse::().unwrap(), Money(123100000000)); 161 | assert_eq!("123.100".parse::().unwrap(), Money(123100000000)); 162 | assert_eq!( 163 | "123.100000000".parse::().unwrap(), 164 | Money(123100000000) 165 | ); 166 | assert_eq!("123.123456".parse::().unwrap(), Money(123123456000)); 167 | assert_eq!( 168 | "123.123456000".parse::().unwrap(), 169 | Money(123123456000) 170 | ); 171 | assert_eq!( 172 | "123.123456789".parse::().unwrap(), 173 | Money(123123456789) 174 | ); 175 | assert_eq!("123.0001".parse::().unwrap(), Money(123000100000)); 176 | assert_eq!( 177 | "123.000000001".parse::().unwrap(), 178 | Money(123000000001) 179 | ); 180 | assert_eq!("0.0001".parse::().unwrap(), Money(100000)); 181 | assert_eq!("0.000000001".parse::().unwrap(), Money(1)); 182 | assert_eq!(".0001".parse::().unwrap(), Money(100000)); 183 | assert_eq!(".000000001".parse::().unwrap(), Money(1)); 184 | assert_eq!(".123456789".parse::().unwrap(), Money(123456789)); 185 | assert_eq!(" 123 ".parse::().unwrap(), Money(123000000000)); 186 | assert_eq!(" 123.456 ".parse::().unwrap(), Money(123456000000)); 187 | assert!("123.234.123".parse::().is_err()); 188 | assert!("k123".parse::().is_err()); 189 | assert!("12 34".parse::().is_err()); 190 | assert!(".".parse::().is_err()); 191 | assert!(" . ".parse::().is_err()); 192 | assert!("12 .".parse::().is_err()); 193 | assert!(". 12".parse::().is_err()); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/crypto/jubjub/curve.rs: -------------------------------------------------------------------------------- 1 | use std::ops::*; 2 | use std::str::FromStr; 3 | 4 | use ff::{Field, PrimeField, PrimeFieldBits}; 5 | use num_bigint::BigUint; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::zk::ZkScalar; 9 | 10 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)] 11 | pub struct PointCompressed(pub ZkScalar, pub bool); 12 | 13 | #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)] 14 | pub struct PointAffine(pub ZkScalar, pub ZkScalar); 15 | 16 | #[derive(Debug, Clone, Copy, PartialEq)] 17 | pub struct PointProjective(pub ZkScalar, pub ZkScalar, pub ZkScalar); 18 | 19 | impl AddAssign<&PointAffine> for PointAffine { 20 | fn add_assign(&mut self, other: &PointAffine) { 21 | if *self == *other { 22 | *self = self.double(); 23 | return; 24 | } 25 | let xx = (ZkScalar::one() + *D * self.0 * other.0 * self.1 * other.1) 26 | .invert() 27 | .unwrap(); 28 | let yy = (ZkScalar::one() - *D * self.0 * other.0 * self.1 * other.1) 29 | .invert() 30 | .unwrap(); 31 | *self = Self( 32 | (self.0 * other.1 + self.1 * other.0) * xx, 33 | (self.1 * other.1 - *A * self.0 * other.0) * yy, 34 | ); 35 | } 36 | } 37 | 38 | impl PointAffine { 39 | pub fn is_on_curve(&self) -> bool { 40 | self.1 * self.1 - self.0 * self.0 41 | == ZkScalar::one() + *D * self.0 * self.0 * self.1 * self.1 42 | } 43 | pub fn is_infinity(&self) -> bool { 44 | self.0.is_zero().into() && (self.1 == ZkScalar::one() || self.1 == -ZkScalar::one()) 45 | } 46 | pub fn zero() -> Self { 47 | Self(ZkScalar::zero(), ZkScalar::one()) 48 | } 49 | pub fn double(&self) -> Self { 50 | let xx = (*A * self.0 * self.0 + self.1 * self.1).invert().unwrap(); 51 | let yy = (ZkScalar::one() + ZkScalar::one() - *A * self.0 * self.0 - self.1 * self.1) 52 | .invert() 53 | .unwrap(); 54 | Self( 55 | ((self.0 * self.1) * xx).double(), 56 | (self.1 * self.1 - *A * self.0 * self.0) * yy, 57 | ) 58 | } 59 | pub fn multiply(&self, scalar: &ZkScalar) -> Self { 60 | let mut result = PointProjective::zero(); 61 | let self_proj = self.to_projective(); 62 | for bit in scalar.to_le_bits().iter().rev() { 63 | result = result.double(); 64 | if *bit { 65 | result.add_assign(&self_proj); 66 | } 67 | } 68 | result.to_affine() 69 | } 70 | pub fn to_projective(&self) -> PointProjective { 71 | PointProjective(self.0, self.1, ZkScalar::one()) 72 | } 73 | pub fn compress(&self) -> PointCompressed { 74 | PointCompressed(self.0, self.1.is_odd().into()) 75 | } 76 | } 77 | 78 | impl PointCompressed { 79 | pub fn decompress(&self) -> PointAffine { 80 | let mut y = ((ZkScalar::one() - *D * self.0.square()).invert().unwrap() 81 | * (ZkScalar::one() - *A * self.0.square())) 82 | .sqrt() 83 | .unwrap(); 84 | let is_odd: bool = y.is_odd().into(); 85 | if self.1 != is_odd { 86 | y = y.neg(); 87 | } 88 | PointAffine(self.0, y) 89 | } 90 | } 91 | 92 | impl AddAssign<&PointProjective> for PointProjective { 93 | fn add_assign(&mut self, other: &PointProjective) { 94 | if self.is_zero() { 95 | *self = *other; 96 | return; 97 | } 98 | if other.is_zero() { 99 | return; 100 | } 101 | if self.to_affine() == other.to_affine() { 102 | *self = self.double(); 103 | return; 104 | } 105 | let a = self.2 * other.2; // A = Z1 * Z2 106 | let b = a.square(); // B = A^2 107 | let c = self.0 * other.0; // C = X1 * X2 108 | let d = self.1 * other.1; // D = Y1 * Y2 109 | let e = *D * c * d; // E = dC · D 110 | let f = b - e; // F = B − E 111 | let g = b + e; // G = B + E 112 | self.0 = a * f * ((self.0 + self.1) * (other.0 + other.1) - c - d); 113 | self.1 = a * g * (d - *A * c); 114 | self.2 = f * g; 115 | } 116 | } 117 | 118 | impl PointProjective { 119 | pub fn zero() -> Self { 120 | PointProjective(ZkScalar::zero(), ZkScalar::one(), ZkScalar::zero()) 121 | } 122 | pub fn is_zero(&self) -> bool { 123 | self.2.is_zero().into() 124 | } 125 | pub fn double(&self) -> PointProjective { 126 | if self.is_zero() { 127 | return PointProjective::zero(); 128 | } 129 | let b = (self.0 + self.1).square(); 130 | let c = self.0.square(); 131 | let d = self.1.square(); 132 | let e = *A * c; 133 | let f = e + d; 134 | let h = self.2.square(); 135 | let j = f - h.double(); 136 | PointProjective((b - c - d) * j, f * (e - d), f * j) 137 | } 138 | pub fn to_affine(&self) -> PointAffine { 139 | if self.is_zero() { 140 | return PointAffine::zero(); 141 | } 142 | let zinv = self.2.invert().unwrap(); 143 | PointAffine(self.0 * zinv, self.1 * zinv) 144 | } 145 | } 146 | 147 | lazy_static! { 148 | pub static ref A: ZkScalar = ZkScalar::one().neg(); 149 | pub static ref D: ZkScalar = ZkScalar::from_str_vartime( 150 | "19257038036680949359750312669786877991949435402254120286184196891950884077233" 151 | ) 152 | .unwrap(); 153 | pub static ref BASE: PointAffine = PointAffine( 154 | ZkScalar::from_str_vartime( 155 | "28867639725710769449342053336011988556061781325688749245863888315629457631946" 156 | ) 157 | .unwrap(), 158 | ZkScalar::from_str_vartime("18").unwrap() 159 | ); 160 | pub static ref BASE_COFACTOR: PointAffine = BASE.multiply(&ZkScalar::from(8)); 161 | pub static ref ORDER: BigUint = BigUint::from_str( 162 | "6554484396890773809930967563523245729705921265872317281365359162392183254199" 163 | ) 164 | .unwrap(); 165 | } 166 | 167 | #[cfg(test)] 168 | mod tests { 169 | use super::*; 170 | 171 | #[test] 172 | fn test_twisted_edwards_curve_ops() { 173 | // ((2G) + G) + G 174 | let mut a = BASE.double(); 175 | a.add_assign(&BASE); 176 | a.add_assign(&BASE); 177 | 178 | // 2(2G) 179 | let b = BASE.double().double(); 180 | 181 | assert_eq!(a, b); 182 | 183 | // G + G + G + G 184 | let mut c = BASE.clone(); 185 | c.add_assign(&BASE); 186 | c.add_assign(&BASE); 187 | c.add_assign(&BASE); 188 | 189 | assert_eq!(b, c); 190 | 191 | // Check if projective points are working 192 | let mut pnt1 = BASE.to_projective().double().double(); 193 | pnt1.add_assign(&BASE.to_projective()); 194 | let mut pnt2 = BASE.double().double(); 195 | pnt2.add_assign(&BASE); 196 | 197 | assert_eq!(pnt1.to_affine(), pnt2); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod keys; 2 | 3 | use crate::blockchain::{ZkBlockchainPatch, ZkCompressedStateChange}; 4 | use crate::core::{hash::Hash, Account, Block, ContractAccount, ContractId, Hasher, Header}; 5 | use crate::crypto::merkle::MerkleTree; 6 | use crate::zk::{ 7 | ZkCompressedState, ZkContract, ZkDataPairs, ZkDeltaPairs, ZkScalar, ZkState, ZkStateModel, 8 | }; 9 | use db_key::Key; 10 | use serde::{Deserialize, Serialize}; 11 | use std::collections::HashMap; 12 | use thiserror::Error; 13 | 14 | #[derive(Error, Debug)] 15 | pub enum KvStoreError { 16 | #[error("kvstore failure")] 17 | Failure, 18 | #[error("kvstore data corrupted: {0}")] 19 | Corrupted(#[from] bincode::Error), 20 | #[error("io error: {0}")] 21 | IO(#[from] std::io::Error), 22 | #[cfg(feature = "db")] 23 | #[error("leveldb error: {0}")] 24 | LevelDb(#[from] leveldb::error::Error), 25 | } 26 | 27 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, std::hash::Hash)] 28 | pub struct StringKey(pub String); 29 | 30 | impl PartialOrd for StringKey { 31 | fn partial_cmp(&self, other: &Self) -> Option { 32 | Some(self.cmp(other)) 33 | } 34 | } 35 | 36 | impl Ord for StringKey { 37 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 38 | self.0.cmp(&other.0) 39 | } 40 | } 41 | 42 | impl StringKey { 43 | pub fn new(s: &str) -> StringKey { 44 | StringKey(s.to_string()) 45 | } 46 | } 47 | 48 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 49 | pub struct Blob(Vec); 50 | 51 | macro_rules! gen_try_into { 52 | ( $( $x:ty ),* ) => { 53 | $( 54 | impl TryInto<$x> for Blob { 55 | type Error = KvStoreError; 56 | fn try_into(self) -> Result<$x, Self::Error> { 57 | Ok(bincode::deserialize(&self.0)?) 58 | } 59 | } 60 | )* 61 | }; 62 | } 63 | 64 | macro_rules! gen_from { 65 | ( $( $x:ty ),* ) => { 66 | $( 67 | impl From<$x> for Blob { 68 | fn from(n: $x) -> Self { 69 | Self(bincode::serialize(&n).unwrap()) 70 | } 71 | } 72 | )* 73 | }; 74 | } 75 | 76 | gen_try_into!( 77 | u32, 78 | u64, 79 | u128, 80 | usize, 81 | Account, 82 | ContractAccount, 83 | Header, 84 | Block, 85 | Vec, 86 | MerkleTree, 87 | ZkContract, 88 | ZkCompressedState, 89 | Vec, 90 | HashMap, 91 | HashMap, 92 | ZkState, 93 | ZkBlockchainPatch, 94 | ZkStateModel, 95 | ZkScalar, 96 | ZkDataPairs, 97 | ZkDeltaPairs 98 | ); 99 | gen_from!( 100 | u32, 101 | u64, 102 | u128, 103 | usize, 104 | Account, 105 | ContractAccount, 106 | Header, 107 | &Block, 108 | Vec, 109 | MerkleTree, 110 | ZkContract, 111 | ZkCompressedState, 112 | Vec, 113 | HashMap, 114 | HashMap, 115 | &ZkState, 116 | &ZkBlockchainPatch, 117 | ZkStateModel, 118 | ZkScalar, 119 | &ZkDataPairs, 120 | &ZkDeltaPairs 121 | ); 122 | 123 | impl Key for StringKey { 124 | fn from_u8(key: &[u8]) -> StringKey { 125 | StringKey(std::str::from_utf8(key).unwrap().to_string()) 126 | } 127 | 128 | fn as_slice T>(&self, f: F) -> T { 129 | f(self.0.as_bytes()) 130 | } 131 | } 132 | 133 | impl From for StringKey { 134 | fn from(s: String) -> Self { 135 | Self::new(&s) 136 | } 137 | } 138 | impl From<&str> for StringKey { 139 | fn from(s: &str) -> Self { 140 | Self::new(s) 141 | } 142 | } 143 | 144 | #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] 145 | pub enum WriteOp { 146 | Remove(StringKey), 147 | Put(StringKey, Blob), 148 | } 149 | 150 | pub trait KvStore { 151 | fn get(&self, k: StringKey) -> Result, KvStoreError>; 152 | fn update(&mut self, ops: &[WriteOp]) -> Result<(), KvStoreError>; 153 | fn pairs(&self, prefix: StringKey) -> Result, KvStoreError>; 154 | fn checksum(&self) -> Result { 155 | let mut kvs: Vec<_> = self.pairs("".into())?.into_iter().collect(); 156 | kvs.sort_by_key(|(k, _)| k.clone()); 157 | Ok(H::hash(&bincode::serialize(&kvs).unwrap())) 158 | } 159 | fn mirror(&self) -> RamMirrorKvStore<'_, Self> 160 | where 161 | Self: Sized, 162 | { 163 | RamMirrorKvStore::new(self) 164 | } 165 | } 166 | 167 | pub struct RamMirrorKvStore<'a, K: KvStore> { 168 | store: &'a K, 169 | overwrite: HashMap>, 170 | } 171 | impl<'a, K: KvStore> RamMirrorKvStore<'a, K> { 172 | pub fn new(store: &'a K) -> Self { 173 | Self { 174 | store, 175 | overwrite: HashMap::new(), 176 | } 177 | } 178 | pub fn rollback(&self) -> Result, KvStoreError> { 179 | self.overwrite 180 | .iter() 181 | .map(|(k, _)| { 182 | self.store.get(k.clone()).map(|v| match v { 183 | Some(v) => WriteOp::Put(k.clone(), v), 184 | None => WriteOp::Remove(k.clone()), 185 | }) 186 | }) 187 | .collect() 188 | } 189 | pub fn to_ops(&self) -> Vec { 190 | self.overwrite 191 | .iter() 192 | .map(|(k, v)| match v { 193 | Some(b) => WriteOp::Put(k.clone(), b.clone()), 194 | None => WriteOp::Remove(k.clone()), 195 | }) 196 | .collect() 197 | } 198 | } 199 | 200 | impl<'a, K: KvStore> KvStore for RamMirrorKvStore<'a, K> { 201 | fn get(&self, k: StringKey) -> Result, KvStoreError> { 202 | if self.overwrite.contains_key(&k) { 203 | Ok(self.overwrite.get(&k).cloned().unwrap()) 204 | } else { 205 | self.store.get(k) 206 | } 207 | } 208 | fn update(&mut self, ops: &[WriteOp]) -> Result<(), KvStoreError> { 209 | for op in ops.iter() { 210 | match op { 211 | WriteOp::Remove(k) => self.overwrite.insert(k.clone(), None), 212 | WriteOp::Put(k, v) => self.overwrite.insert(k.clone(), Some(v.clone())), 213 | }; 214 | } 215 | Ok(()) 216 | } 217 | fn pairs(&self, prefix: StringKey) -> Result, KvStoreError> { 218 | let mut res = self.store.pairs(prefix.clone())?; 219 | for (k, v) in self.overwrite.iter() { 220 | if let Some(v) = v { 221 | if k.0.starts_with(&prefix.0) { 222 | res.insert(k.clone(), v.clone()); 223 | } 224 | } else { 225 | res.remove(k); 226 | } 227 | } 228 | Ok(res) 229 | } 230 | } 231 | 232 | mod ram; 233 | pub use ram::*; 234 | 235 | #[cfg(feature = "db")] 236 | mod disk; 237 | #[cfg(feature = "db")] 238 | pub use disk::*; 239 | 240 | #[cfg(test)] 241 | mod test; 242 | -------------------------------------------------------------------------------- /src/core/transaction.rs: -------------------------------------------------------------------------------- 1 | use super::address::{Address, Signature}; 2 | use super::hash::Hash; 3 | use super::Money; 4 | use crate::crypto::{SignatureScheme, ZkSignatureScheme}; 5 | use crate::zk::{ZkCompressedState, ZkContract, ZkDeltaPairs, ZkProof, ZkScalar}; 6 | 7 | use std::str::FromStr; 8 | use thiserror::Error; 9 | 10 | #[derive( 11 | serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone, Copy, Eq, std::hash::Hash, 12 | )] 13 | pub struct ContractId(H::Output); 14 | 15 | #[derive(Error, Debug)] 16 | pub enum ParseContractIdError { 17 | #[error("contract-id invalid")] 18 | Invalid, 19 | } 20 | 21 | impl ContractId { 22 | pub fn new(tx: &Transaction) -> Self { 23 | Self(tx.hash()) 24 | } 25 | } 26 | 27 | impl std::fmt::Display for ContractId { 28 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 29 | write!(f, "{}", hex::encode(self.0)) 30 | } 31 | } 32 | 33 | impl FromStr for ContractId { 34 | type Err = ParseContractIdError; 35 | fn from_str(s: &str) -> Result { 36 | let bytes = hex::decode(s).map_err(|_| ParseContractIdError::Invalid)?; 37 | let hash_output = H::Output::try_from(bytes).map_err(|_| ParseContractIdError::Invalid)?; 38 | Ok(Self(hash_output)) 39 | } 40 | } 41 | 42 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 43 | pub enum PaymentDirection { 44 | Deposit(Option), 45 | Withdraw(Option), 46 | } 47 | 48 | #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] 49 | pub struct ContractPayment { 50 | pub address: S::Pub, 51 | pub zk_address: ZS::Pub, 52 | pub zk_address_index: u32, // Help locating the zk_address in a a huge database 53 | pub contract_id: ContractId, // Makes sure the payment can only run on this contract. 54 | pub nonce: u32, // Makes sure a contract payment cannot be replayed on this contract. 55 | pub amount: Money, 56 | pub fee: Money, // Executor fee 57 | pub direction: PaymentDirection, 58 | } 59 | 60 | impl PartialEq for ContractPayment { 61 | fn eq(&self, other: &Self) -> bool { 62 | bincode::serialize(self).unwrap() == bincode::serialize(other).unwrap() 63 | } 64 | } 65 | 66 | impl Eq 67 | for ContractPayment 68 | { 69 | } 70 | impl std::hash::Hash 71 | for ContractPayment 72 | { 73 | fn hash(&self, state: &mut Hasher) 74 | where 75 | Hasher: std::hash::Hasher, 76 | { 77 | state.write(&bincode::serialize(self).unwrap()); 78 | state.finish(); 79 | } 80 | } 81 | 82 | impl ContractPayment { 83 | pub fn verify_signature(&self) -> bool { 84 | let mut unsigned = self.clone(); 85 | unsigned.direction = match &unsigned.direction { 86 | PaymentDirection::::Deposit(_) => PaymentDirection::::Deposit(None), 87 | PaymentDirection::::Withdraw(_) => PaymentDirection::::Withdraw(None), 88 | }; 89 | let unsigned_bin = bincode::serialize(&unsigned).unwrap(); 90 | match &self.direction { 91 | PaymentDirection::::Deposit(Some(sig)) => { 92 | S::verify(&self.address, &unsigned_bin, sig) 93 | } 94 | PaymentDirection::::Withdraw(Some(sig)) => { 95 | let scalar = ZkScalar::new(H::hash(&unsigned_bin).as_ref()); 96 | ZS::verify(&self.zk_address, scalar, sig) 97 | } 98 | _ => false, 99 | } 100 | } 101 | } 102 | 103 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 104 | pub struct ContractAccount { 105 | pub height: u64, 106 | pub balance: Money, 107 | pub compressed_state: ZkCompressedState, 108 | } 109 | 110 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 111 | pub enum ContractUpdate { 112 | // Proof for PaymentCircuit(curr_state, next_state, hash(entries)) 113 | Payment { 114 | circuit_id: u32, 115 | payments: Vec>, 116 | next_state: ZkCompressedState, 117 | proof: ZkProof, 118 | }, 119 | // Proof for FunctionCallCircuits[function_id](curr_state, next_state) 120 | FunctionCall { 121 | function_id: u32, 122 | next_state: ZkCompressedState, 123 | proof: ZkProof, 124 | fee: Money, // Executor fee 125 | }, 126 | } 127 | 128 | // A transaction could be as simple as sending some funds, or as complicated as 129 | // creating a smart-contract. 130 | #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug, Clone)] 131 | pub enum TransactionData { 132 | RegularSend { 133 | dst: Address, 134 | amount: Money, 135 | }, 136 | // Create a Zero-Contract. The creator can consider multiple ways (Circuits) of updating 137 | // the state. But there should be only one circuit for entering and exiting the contract. 138 | CreateContract { 139 | contract: ZkContract, 140 | }, 141 | // Collection of contract updates 142 | UpdateContract { 143 | contract_id: ContractId, 144 | updates: Vec>, 145 | }, 146 | } 147 | 148 | #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)] 149 | pub struct Transaction { 150 | pub src: Address, 151 | pub nonce: u32, 152 | pub data: TransactionData, 153 | pub fee: Money, 154 | pub sig: Signature, 155 | } 156 | 157 | #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] 158 | pub struct TransactionAndDelta { 159 | pub tx: Transaction, 160 | pub state_delta: Option, 161 | } 162 | 163 | impl PartialEq> 164 | for TransactionAndDelta 165 | { 166 | fn eq(&self, other: &Self) -> bool { 167 | bincode::serialize(self).unwrap() == bincode::serialize(other).unwrap() 168 | } 169 | } 170 | 171 | impl Transaction { 172 | pub fn size(&self) -> usize { 173 | bincode::serialize(self).unwrap().len() 174 | } 175 | pub fn hash(&self) -> H::Output { 176 | H::hash(&bincode::serialize(self).unwrap()) 177 | } 178 | pub fn verify_signature(&self) -> bool { 179 | match &self.src { 180 | Address::::Treasury => true, 181 | Address::::PublicKey(pk) => match &self.sig { 182 | Signature::Unsigned => false, 183 | Signature::Signed(sig) => { 184 | let mut unsigned = self.clone(); 185 | unsigned.sig = Signature::Unsigned; 186 | let bytes = bincode::serialize(&unsigned).unwrap(); 187 | S::verify(pk, &bytes, sig) 188 | } 189 | }, 190 | } 191 | } 192 | } 193 | 194 | impl Eq 195 | for TransactionAndDelta 196 | { 197 | } 198 | impl std::hash::Hash 199 | for TransactionAndDelta 200 | { 201 | fn hash(&self, state: &mut Hasher) 202 | where 203 | Hasher: std::hash::Hasher, 204 | { 205 | state.write(&bincode::serialize(self).unwrap()); 206 | state.finish(); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/zk/poseidon/params/poseidon_params_n255_t2_alpha5_M128.txt: -------------------------------------------------------------------------------- 1 | Params: n=255, t=2, alpha=5, M=128, R_F=8, R_P=56 2 | Modulus = 52435875175126190479447740508185965837690552500527637822603658699938581184513 3 | Number of round constants: 128Round constants for GF(p): 4 | ['0x6267f5556c88257324c1c8b00d5871b2eba13cc39d72aa10dde6b69bc44c41c7', '0x30347723511438a085118166c68bf0c4f4ab5c10a2c55adb5cf87cc9e030f60f', '0x10db856965e40038eb6427303181e7b7439f1a051aa4630c26cf86d0a0451a4b', '0x5a3d2dcd541e4faaae7eb143eec847a0f652b6dc1b92e3f39ec23c808b3a5d63', '0x3b07f0ff7edcf93b1dd0487bc9fab1c6905f9ceee38dcce83efeb3a320398526', '0x40c73c524b9fd0fab63128175befe07b5c63ccdde9ca10e1a37205c9607fdf8a', '0x3a933861cf23752376d94dbb24b0f3c61630787928875c07672b68abfb9191e0', '0x71cc165e208570b2d5ef81db84e3c5e714ea4edfb36fc7fb11ef65a64b2d9755', '0x6c0dc9eb332b5d968bec8ad68fe24ce34087ea54093f153618434475bce402f8', '0x0af5bafd335dae5c86967b11d5dcefb986a54c9d60d35eb06dc7a3fd779b3906', '0x6e12847918f030f2626c150ab69e4be0f13d202ae1f8bc87ea74323e93372e3b', '0x5565d40e21d059a26db241ca125d9316283eadf144b1318e604e253eeae1fe9a', '0x608e01b42d3dca09fed9b54eadaaba3e4ce6aefe92b0dc954a0fa4683a9678f2', '0x16bbe434b24f94e2c40ed1f4f9bd7d17e5be96c3aec15579b35fd80f0f80de9e', '0x0d1be811a8e73220cab01ce981d475522c3d7dd9e2716c3a2cf4ddd541546890', '0x5997a3affb18f942868b86f8ee10a68966e90bac7bbd8c65ede7e6e5ef1f6320', '0x4d92e86d270041061eec80278079fca771499dea5ccdc99682a953bb3a038b8e', '0x616c8c5ce232b9314f694fc6a968446ea9daf7a4079ce1a75fcc950741d680bb', '0x677e31e7846d9131bdc350eaf11a8ff918dd258ddd800444424afab34dfdfe3d', '0x4e7d7f85aefc110b233525ee3e53851aee7d3241e2a132585e0e25005eee0b0e', '0x06a8b4539488b7dddc48c3a226dbda313f906e106f844196d55013d321244f13', '0x5091517b6a85783108999f8e6bda3c793bef3f2e9589641d260bdfde8bdef00d', '0x0d2703e5b30f54d7f414e901802d54f8c14cd6355415df6e0f063d16bef9c43a', '0x56f69096811148eb38eec143d32565c077b3d1a4a4351f2b458f43b1659d4495', '0x622d94d38d1ded428afd062008c5709b43a678f6ba518ec56383e8ffba473504', '0x2730c607bba7333723a4a44577819b7db82a24574f6d13eee4c856c1ca3de9c7', '0x01ac5f59256c5004dc1043c53b23800a3fbab53eb1a83f551056f227b514b9f6', '0x0790b92523c973f1c95b94937afbb5796d89481e7a56328b44bab5ba81ae42f3', '0x1d63b59d97bc269d13964fb3e8771d0acc749bc83eb2f0372484e266142bb8c0', '0x1a52d04e5f14a3a05f7a01262df9e68c77fdf7e2bfb56c8b252d2140efdf0914', '0x5aa9b3b808812b284857e8622843a8717fa5cb49b217017f31d79e8d0f963fc0', '0x6a3d18fdbeb1d77ec1304539b00e6188786dbbc4435269b4c6281367f42656e3', '0x4743e860df269a85dd76fb99dbe9d840eb669dc859754b3f74805e57ba288b00', '0x6c32cac3946825f80a434c5ab397fc1a1c6a9bdfaab53175d4cf3d29ddb6cbc6', '0x333b0eea5da7ed1e3959d16280a361aa77dd24ecbfb28e1b2583ac4e9894305c', '0x3b503fc333b795ccc0c5bb3ae26b077dc3742cb745ec8821648c5ce7ebd9df18', '0x4fa5853188d9f728a17532d94bee6fb28fee510380a5d50927c6c5b1ce283444', '0x5d2ed8a6603a905bac490ebfb9e6c18f0bc9da1bbc2173291b18de6b6186118f', '0x2d830a53584c5556264852f075c78f7f9eb068016ae88af9cda933d6ae52eca7', '0x0250f4d6780ad29ae60e55f135b9ac80ccc7c81e3add37db276c26f1a2b1b86e', '0x6e3e9595f59220599e23e830728d4a0c4d62515ec1ed10b72446cf4df5b4c308', '0x2cd3314555d6faf23ee90cdb884f1c4697ebe98e3a450a624c4d896233b93cd5', '0x584a408d0f370543b8413fee70a060a394e561f504d8679f7bece4bf222e4108', '0x499cd53437b9fcbf7479c00fcc21295759074ce9bd1bb1fbd3460237aef4759e', '0x56a9b567bd0646effd0608d74d537991136098d9a06af6cb3ff8f010efb57578', '0x6a5fae2b00d968b931441b374e27ba4d03b306bd602d48731677169e75a67e8c', '0x2e1cc28e390e64aa1d60edb99c0aeda7c8c32bdb01ba11abbad5026b46eccb27', '0x2d4820000675df7c276beac408fe2e851e734a7008ae09bbcb3c96c70024f71b', '0x0c2fe101a2b52b538b902c6b2dc992cb266f7636e05b0c068385b5fa19e97142', '0x209b790b78c0e7927c6a178ef2f00b8687fc7bd4f21a9e02578551535002bc95', '0x2dd0926cf56bbaaec6491513d08a9983f94a910852a7b4ea4bd4222b93e14c10', '0x4316b39dd7d65b1bb575198104d409b169236a7ade371f7ab176fcbae75a5f0d', '0x540276d61041b91f6ea3068ec260a9338b6e3da15d934e648c24f35aee04e535', '0x37af612900b839977b146324c84772c58a4ccc0f6494cc054571827e74bfd2d3', '0x2af00c93d59ed14c9911e5cb3781d772371e83228e4267bbce11d065c1955338', '0x62b48779b0cf7ff2c10fd9b91a6ff7b7a99f935e961a5a94aa38f9d4f71c8b4c', '0x540bf5bbe01f28563bcbe11a2ce346d8231a2cdd0fe07641f9fa89e5c21978e3', '0x232b6c847a6d23912cb10ecbe50b53491f67f71e9b87a4a30446f2218017874b', '0x0ab34adbe77b8f1e57a370e4fd626071eea74b3f0b66644a629efaa0e96456c0', '0x1a83e43ef118c90046b1bdbeab8dd5cdcab632807c2cd0dc9147cbc5b7084be8', '0x1ec6fa41b41b672d9005468720918130b642567462a3d557a595d4dc6c56f2f9', '0x01f81a153199a751a111b8f5212cfc5bf82aacf0287d03e1864f8e5713fe4a17', '0x2617307587a675f4ecd73a54a7b206162d751cabf3d9fd007bcca4de2c6f0649', '0x1647be94c515178c7974a245624b642bb1ae6e2d4e1682087e362d7f98bc953f', '0x6e690b956e00b9e339dec49d675586f661f9b081ee3fa7696d73977658aa6fea', '0x660b85bc22de06d476c47bf084ad436f59874f1d630c0f5c91fbef51d5e738c5', '0x32bf3d451b69dde075fc370eaa8c1b77b5c0bc2aab1c7b46da7ef9d1840b0419', '0x73924b40beaa9c1ce4074c2154d1af4d658c09395a568b99b2fbcc3b5685e810', '0x17cbb3ee0adcb9d977e96e9152b36042925244fdd0aa184c7a89a58a2dc40097', '0x29d76a821e3220775c552f6b5977ab94956e52b8dac36ef88ace050d553766a3', '0x62b1a6c06ab26881a1fe57eceac56b5aec0b96da7211557f4e27ec24296d7db6', '0x0dfc474151e5c605a693a51ae8227cc0a99fdc4524fc2810c6eda9035d04334d', '0x3e287088506bb38d2ed465cdab37a7b2eedafb47557446a9aec9d366a91b1d98', '0x2641d2919d0bed24673c9effc24578798c56f3cd39940030aff2c913cf58974a', '0x286c1f841a05bb667c84d93ba9359da901a545ecb1c6a37403c5e74d0a663cf9', '0x68159ff06a0aff28d53ecaae5654db9f3017f564378fccb68af2a47f10788d17', '0x432ff6c037e9ce6e61172862cc076d925d00f4414fa4f4d8722c4b1460c2f04b', '0x5e1a35f47c87b33cd37544c51d7dda1d7e262af52b11133df163d2c94e9f0674', '0x0b8d4686183872494b4416fc82317a4deae10e7c312aeb8a44697b49b1025df5', '0x6291c6d458499cffd8915196bde5fdcdb7eb5ef6b1e6d15aab7d8e95cda2c564', '0x0e2f4df2de6504553f057c1046bb04decf779baff7457cce028c4f55d4f4f0ed', '0x1694268ccf0cf400d1c77259d9766437e5a35810b854022829ead0b3e70aeba4', '0x3c96c51a30dc76af38c9c08c36ee90b626c28910a0994e0854b5375e7f46698b', '0x5a99402864789e9d2bd2930a70a54adb66865923b4a17ef36a107020e44a73dc', '0x5a22c7703f1ab4a55031fc34aab0732018e931ba48c8c38c65bfaf5e610ab7af', '0x686fea106123473955c3e9365afa2238a5e35390360884ed7555a8c5f8d66a20', '0x6a68607546ff48f9d154b924ccf0fd85b608ad351e4bcffd6097d4d175ea53bc', '0x21077e81fe5a0bde7f6f2631f93cb33d07d1928befec6a434bc0616ef5b3dddb', '0x11aeb10c549bc49d3ea21f84ea42ba3374a80c7504186d3c9c9476e7a380fb90', '0x4dfbc6f6a54a316d331770308f53534a01f4b9a50210cb081fdee076e3fd6871', '0x0e9ba2efbd177a7c3e3ba0716b5de50a24937f94bd97491941e064704a024904', '0x1dda541f2f744aaee3c50c2d9abc405d8fbd01e3e5c22f4192d893f45788e9d6', '0x2ec1165caf7fd1589fbd8cf29049439a0c266d9c002c5d77b656a2e28db097b3', '0x1f8cc6e561bde673e0bbbfb882419c31575e4b7ae66cde549443f95dc23566ec', '0x57d97d4b02423d7738690fbea9e0b7ba8eb1ad651986f869ff96a932fedbe6d3', '0x1205704bf8e95ba26bca37a88295daeba494546ea8efe7189efb000e0bcfbdd0', '0x016cdeebdb6f8b37fbbd4ef80645d2473d21df81eee935ec540836f06eb49d38', '0x19b8a2f4a6403c98add6f220ff4938fe7b5ca43c78095f14184923bdfa0cae33', '0x19358aab5822facc694ee0a27a4ec9fb6bf931d5efb8ce59c25d67bb017e26d5', '0x0c7a80ba8f7372a2bee14572b6967720c9853f811188cd44fe24e764a2e7affe', '0x3894fe113139129d6f42058e3f871ac109b87f7fcffc317321cc1e428f9b48c4', '0x1aefc0785946d65ef0e62be601da8cad8e94f5e3f96a177c235740d5fb406ab9', '0x0af1c4fc0b49030fe545ef543ac1b16640288c36554ee106e7d1e06a4e1626b3', '0x5848a8b1f2645e1c36b603a083689b50b760744f96bdf07a2df821e584023884', '0x1318aac0f0ef72e5e0233ca168ea6c21a4e162e00d3ba70d79087df4660154f4', '0x4179cc529ed5ddd4aa1acf77e50d12bb9a754df8bfcb0ff6c6e1073e8cbfe8ea', '0x005983d5e4d5d48c2c80a4dd83083db023217247ece5e0514e49b90cd73e3d05', '0x5c198603562a2e473d5486f461f4fc776051ab0bfc00526df55541b87e47c11b', '0x39c93d11962620d7c5396532137173d6746356c6821ff03734d59af52a6ad067', '0x2475bc5acc012b8083fcc7d2ccb15127a2bc7bec09dbce1319e5f680bed75fa7', '0x66729d8d26c113573bcb9dcd84bdbca5fb0aede246e1f5659762ce88a38abc7a', '0x0bb442eba5baf8e728df8eb9c2cb17ed8ac2909427f0a608ddbede534265c995', '0x4a432bd0cf7b12c3f31ff6ef7033982cfb702a89df14ad1457e9e108e0093e0f', '0x3d120149595e0c7fa884fe33f287ea54841a94be027702ea9881ad9227204ecd', '0x35e47349a427ba8b1df4bf4f5f1da532f25c44be406a90def22e870301d051e0', '0x19caf5fbbc0a1424525971b573841109f36dc3e07eb1df7fd69d4e1fd34abcec', '0x4d94360247806752a4c1e622c2964069456601b666c900aa8686ae74c00d33be', '0x515cce538dcf5280c18d4d2973a2fd4eda2304d6f0bb0b45d01bf9a2b1fcae38', '0x5d157e3dbf20d63c3e0783a96c763b96f28ad0548a44fabba29463d5fb759390', '0x50c6439188ea7d8153949b6f5f64c10df7bf884d31b9680323781406fc84d4c8', '0x06ab7adf0f997badc0b95360e3d992b0d5dafe9fce2c807f5de11ea819123651', '0x6212d6f6db15bcc7f3b1637efbfa8d08c5faebeab89d265bb7a0585b96306900', '0x5f674e12cce1d53f2d01b06ea1874335f8c7634443b29b300ff011a4e4822783', '0x01865aba8ec3d89aca45cc1da7d8ca5e2b31a8a44178a0d6e665fbe83d63cdd2', '0x221d9df33836d262a34e9566471db96efbe80c8e0602d8ea7af1c2560afc4ed9', '0x0f12e59c448a18bae09585d74c637be4ef24ec3d5ba000964cf76c6032d159af', '0x10376fa0adb3a50540dfd9aa95fa8b5c120f4e6843590ed156b5fcb93ecb7a27', '0x031597a49ea890a50ed8381799fa51e27e540902ada5f8d8a7529a980458eac8'] 5 | n: 255 6 | t: 2 7 | N: 510 8 | Result Algorithm 1: 9 | [True, 0] 10 | Result Algorithm 2: 11 | [True, None] 12 | Result Algorithm 3: 13 | [True, None] 14 | Prime number: 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 15 | MDS matrix: 16 | [['0x1e6d0cd936714f2124fc4c78321266174fe2855e689c6511a36ecadc3cccc268', '0x1240406fed29618b5515b2170437e1cbe9dbf814d630e4c71109f74a157a9bcd'],['0x70720066b0150aa415f3fff26ea0b231e657f63112a36a97f0833f3c18dfa4c5', '0x6b07f55f01bb144fece2d7068241cef3eda1aba1730dab73367f3d06e994a23e']] -------------------------------------------------------------------------------- /src/node/heartbeat/sync_blocks.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::common::*; 3 | 4 | pub async fn sync_blocks( 5 | context: &Arc>>, 6 | ) -> Result<(), NodeError> { 7 | let ctx = context.read().await; 8 | let net = ctx.outgoing.clone(); 9 | let opts = ctx.opts.clone(); 10 | let max_block_size = ctx.blockchain.config().max_block_size; 11 | let mut sorted_peers = ctx.active_peers(); 12 | drop(ctx); 13 | 14 | sorted_peers.sort_by_key(|p| p.info.as_ref().map(|i| i.power).unwrap_or(0)); 15 | 16 | for peer in sorted_peers.iter().rev() { 17 | if let Some(inf) = peer.info.as_ref() { 18 | loop { 19 | let mut failed = false; 20 | let ctx = context.read().await; 21 | let min_target = ctx.blockchain.config().minimum_pow_difficulty; 22 | if inf.power <= ctx.blockchain.get_power()? { 23 | return Ok(()); 24 | } 25 | 26 | log::info!( 27 | "Syncing blocks with: {} (Peer power: {})", 28 | peer.address, 29 | inf.power 30 | ); 31 | 32 | let local_height = ctx.blockchain.get_height()?; 33 | let start_height = std::cmp::min(local_height, inf.height); 34 | drop(ctx); 35 | 36 | // WARN: Chain might change when getting responses from users, maybe get all data needed before dropping ctx 37 | 38 | // Get all headers starting from the indices that we don't have. 39 | let resp = if let Ok(resp) = net 40 | .bincode_get::( 41 | format!("{}/bincode/headers", peer.address), 42 | GetHeadersRequest { 43 | since: start_height, 44 | count: opts.max_blocks_fetch, 45 | }, 46 | Limit::default() 47 | .size(opts.max_blocks_fetch * KB) 48 | .time(5 * SECOND), 49 | ) 50 | .await 51 | { 52 | resp 53 | } else { 54 | break; 55 | }; 56 | 57 | let (mut headers, pow_keys) = (resp.headers, resp.pow_keys); 58 | 59 | // TODO: Check parent hashes 60 | let ctx = context.read().await; 61 | for (i, (head, pow_key)) in headers.iter().zip(pow_keys.into_iter()).enumerate() { 62 | if head.proof_of_work.target < min_target || !head.meets_target(&pow_key) { 63 | log::warn!("Header doesn't meet min target!"); 64 | failed = true; 65 | break; 66 | } 67 | if head.number != start_height + i as u64 { 68 | log::warn!("Bad header number returned!"); 69 | failed = true; 70 | break; 71 | } 72 | if head.number < local_height 73 | && head == &ctx.blockchain.get_header(head.number)? 74 | { 75 | log::warn!("Duplicate header given!"); 76 | failed = true; 77 | break; 78 | } 79 | } 80 | drop(ctx); 81 | 82 | if failed { 83 | break; 84 | } 85 | 86 | log::info!( 87 | "Got headers {}-{}...", 88 | start_height, 89 | start_height + headers.len() as u64 90 | ); 91 | 92 | // The local blockchain and the peer blockchain both have all blocks 93 | // from 0 to height-1, though, the blocks might not be equal. Find 94 | // the header from which the fork has happened. 95 | for index in (0..start_height).rev() { 96 | let peer_resp = if let Ok(resp) = net 97 | .bincode_get::( 98 | format!("{}/bincode/headers", peer.address), 99 | GetHeadersRequest { 100 | since: index, 101 | count: 1, 102 | }, 103 | Limit::default().size(1 * KB).time(3 * SECOND), 104 | ) 105 | .await 106 | { 107 | resp 108 | } else { 109 | failed = true; 110 | break; 111 | }; 112 | if peer_resp.headers.is_empty() || peer_resp.pow_keys.is_empty() { 113 | failed = true; 114 | break; 115 | } 116 | 117 | let (peer_header, peer_pow_key) = 118 | (peer_resp.headers[0].clone(), peer_resp.pow_keys[0].clone()); 119 | 120 | if peer_header.number > 0 121 | && (peer_header.proof_of_work.target < min_target 122 | || !peer_header.meets_target(&peer_pow_key)) 123 | { 124 | log::warn!("Header doesn't meet min target!"); 125 | failed = true; 126 | break; 127 | } 128 | if peer_header.number != index as u64 { 129 | log::warn!("Bad header number!"); 130 | failed = true; 131 | break; 132 | } 133 | if peer_header.hash() != headers[0].parent_hash { 134 | log::warn!("Bad header hash!"); 135 | failed = true; 136 | break; 137 | } 138 | 139 | log::info!("Got header {}...", index); 140 | 141 | let ctx = context.read().await; 142 | let local_header = ctx.blockchain.get_headers(index, 1)?[0].clone(); 143 | drop(ctx); 144 | 145 | if local_header.hash() != peer_header.hash() { 146 | headers.insert(0, peer_header); 147 | } else { 148 | break; 149 | } 150 | } 151 | 152 | if failed { 153 | break; 154 | } 155 | 156 | let ctx = context.read().await; 157 | if headers.iter().any(|h| ctx.banned_headers.contains_key(h)) { 158 | log::warn!("Chain has banned headers!"); 159 | break; 160 | } 161 | 162 | let will_extend = 163 | match ctx 164 | .blockchain 165 | .will_extend(headers[0].number, &headers, true) 166 | { 167 | Ok(result) => { 168 | if !result { 169 | log::warn!("Chain is not powerful enough!"); 170 | } 171 | result 172 | } 173 | Err(e) => { 174 | log::warn!("Chain is invalid! Error: {}", e); 175 | false 176 | } 177 | }; 178 | 179 | if !will_extend { 180 | break; 181 | } 182 | 183 | drop(ctx); 184 | 185 | if let Ok(resp) = net 186 | .bincode_get::( 187 | format!("{}/bincode/blocks", peer.address).to_string(), 188 | GetBlocksRequest { 189 | since: headers[0].number, 190 | count: opts.max_blocks_fetch, 191 | }, 192 | Limit::default() 193 | .size(opts.max_blocks_fetch as u64 * max_block_size as u64 * 2) 194 | .time(opts.max_blocks_fetch as u32 * 30 * SECOND), 195 | ) 196 | .await 197 | { 198 | let mut ctx = context.write().await; 199 | 200 | match ctx.blockchain.extend(headers[0].number, &resp.blocks) { 201 | Ok(_) => { 202 | ctx.outdated_since = None; 203 | } 204 | Err(e) => { 205 | log::warn!("Cannot extend the blockchain. Error: {}", e); 206 | break; 207 | } 208 | } 209 | } else { 210 | log::warn!("Network error! Cannot fetch blocks..."); 211 | break; 212 | } 213 | } 214 | context.write().await.punish_bad_behavior( 215 | peer.address, 216 | opts.incorrect_power_punish, 217 | "Cannot sync blocks!", 218 | ); 219 | } 220 | } 221 | 222 | Ok(()) 223 | } 224 | -------------------------------------------------------------------------------- /src/blockchain/test/contract.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::str::FromStr; 3 | 4 | #[test] 5 | fn test_contract_create_patch() -> Result<(), BlockchainError> { 6 | let miner = Wallet::new(Vec::from("MINER")); 7 | let alice = Wallet::new(Vec::from("ABC")); 8 | let mut chain = KvStoreChain::new(db::RamKvStore::new(), easy_config())?; 9 | 10 | let state_model = zk::ZkStateModel::List { 11 | item_type: Box::new(zk::ZkStateModel::Scalar), 12 | log4_size: 5, 13 | }; 14 | let full_state = zk::ZkState { 15 | rollbacks: vec![], 16 | data: Default::default(), 17 | }; 18 | 19 | let tx = alice.create_contract( 20 | zk::ZkContract { 21 | state_model: state_model.clone(), 22 | initial_state: state_model.compress::(&full_state.data)?, 23 | payment_functions: Vec::new(), 24 | functions: Vec::new(), 25 | }, 26 | full_state.data.clone(), 27 | Money(0), 28 | 1, 29 | ); 30 | 31 | let draft = chain 32 | .draft_block(1, &with_dummy_stats(&[tx.clone()]), &miner, true)? 33 | .unwrap(); 34 | chain.apply_block(&draft.block, true)?; 35 | 36 | assert_eq!(chain.get_height()?, 2); 37 | assert_eq!(chain.get_outdated_contracts()?.len(), 1); 38 | 39 | chain.update_states(&draft.patch)?; 40 | 41 | assert_eq!(chain.get_height()?, 2); 42 | assert_eq!(chain.get_outdated_contracts()?.len(), 0); 43 | 44 | rollback_till_empty(&mut chain)?; 45 | 46 | Ok(()) 47 | } 48 | 49 | #[test] 50 | fn test_contract_update() -> Result<(), BlockchainError> { 51 | let miner = Wallet::new(Vec::from("MINER")); 52 | let alice = Wallet::new(Vec::from("ABC")); 53 | let cid = 54 | ContractId::from_str("94f768758eebc1e0a1fc806726db01aaff5331763ce7c93b253770abfa7a53ee") 55 | .unwrap(); 56 | let mut chain = KvStoreChain::new(db::RamKvStore::new(), easy_config())?; 57 | 58 | let state_model = zk::ZkStateModel::List { 59 | item_type: Box::new(zk::ZkStateModel::Scalar), 60 | log4_size: 5, 61 | }; 62 | let mut full_state = zk::ZkState { 63 | rollbacks: vec![], 64 | data: zk::ZkDataPairs( 65 | [(zk::ZkDataLocator(vec![100]), zk::ZkScalar::from(200))] 66 | .into_iter() 67 | .collect(), 68 | ), 69 | }; 70 | let mut full_state_with_delta = full_state.clone(); 71 | 72 | let state_delta = zk::ZkDeltaPairs( 73 | [(zk::ZkDataLocator(vec![123]), Some(zk::ZkScalar::from(234)))] 74 | .into_iter() 75 | .collect(), 76 | ); 77 | full_state.apply_delta(&state_delta); 78 | full_state_with_delta.push_delta(&state_delta); 79 | 80 | let tx = alice.call_function( 81 | cid, 82 | 0, 83 | state_delta.clone(), 84 | state_model.compress::(&full_state.data)?, 85 | zk::ZkProof::Dummy(true), 86 | Money(0), 87 | Money(0), 88 | 1, 89 | ); 90 | 91 | let draft = chain 92 | .draft_block(1, &with_dummy_stats(&[tx.clone()]), &miner, false)? 93 | .unwrap(); 94 | 95 | chain.apply_block(&draft.block, true)?; 96 | 97 | assert!(matches!( 98 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 99 | patches: HashMap::new() 100 | }), 101 | Err(BlockchainError::FullStateNotFound) 102 | )); 103 | assert!(matches!( 104 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 105 | patches: [( 106 | cid, 107 | zk::ZkStatePatch::Delta(zk::ZkDeltaPairs( 108 | [(zk::ZkDataLocator(vec![123]), Some(zk::ZkScalar::from(321)))] 109 | .into_iter() 110 | .collect() 111 | )) 112 | )] 113 | .into_iter() 114 | .collect() 115 | }), 116 | Err(BlockchainError::FullStateNotValid) 117 | )); 118 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 119 | patches: [(cid, zk::ZkStatePatch::Delta(state_delta.clone()))] 120 | .into_iter() 121 | .collect(), 122 | })?; 123 | assert!(matches!( 124 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 125 | patches: HashMap::new() 126 | }), 127 | Err(BlockchainError::FullStateNotFound) 128 | )); 129 | assert!(matches!( 130 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 131 | patches: [( 132 | cid, 133 | zk::ZkStatePatch::Full(zk::ZkState { 134 | rollbacks: vec![], 135 | data: zk::ZkDataPairs( 136 | [(zk::ZkDataLocator(vec![100]), zk::ZkScalar::from(200))] 137 | .into_iter() 138 | .collect() 139 | ) 140 | }) 141 | )] 142 | .into_iter() 143 | .collect() 144 | }), 145 | Err(BlockchainError::FullStateNotValid) 146 | )); 147 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 148 | patches: [( 149 | cid, 150 | zk::ZkStatePatch::Full(zk::ZkState { 151 | rollbacks: vec![], 152 | data: zk::ZkDataPairs( 153 | [ 154 | (zk::ZkDataLocator(vec![100]), zk::ZkScalar::from(200)), 155 | (zk::ZkDataLocator(vec![123]), zk::ZkScalar::from(234)), 156 | ] 157 | .into_iter() 158 | .collect(), 159 | ), 160 | }), 161 | )] 162 | .into_iter() 163 | .collect(), 164 | })?; 165 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 166 | patches: [( 167 | cid, 168 | zk::ZkStatePatch::Full(zk::ZkState { 169 | rollbacks: vec![], 170 | data: zk::ZkDataPairs( 171 | [ 172 | (zk::ZkDataLocator(vec![100]), zk::ZkScalar::from(200)), 173 | (zk::ZkDataLocator(vec![123]), zk::ZkScalar::from(234)), 174 | ] 175 | .into_iter() 176 | .collect(), 177 | ), 178 | }), 179 | )] 180 | .into_iter() 181 | .collect(), 182 | })?; 183 | chain.fork_on_ram().update_states(&ZkBlockchainPatch { 184 | patches: [(cid, zk::ZkStatePatch::Full(full_state_with_delta))] 185 | .into_iter() 186 | .collect(), 187 | })?; 188 | let mut unupdated_fork = chain.fork_on_ram(); 189 | let mut updated_fork = chain.fork_on_ram(); 190 | updated_fork.update_states(&ZkBlockchainPatch { 191 | patches: [( 192 | cid, 193 | zk::ZkStatePatch::Delta(zk::ZkDeltaPairs( 194 | [(zk::ZkDataLocator(vec![123]), Some(zk::ZkScalar::from(234)))] 195 | .into_iter() 196 | .collect(), 197 | )), 198 | )] 199 | .into_iter() 200 | .collect(), 201 | })?; 202 | assert_eq!(updated_fork.get_outdated_contracts()?.len(), 0); 203 | let updated_tip_hash = updated_fork.get_tip()?.hash(); 204 | 205 | let outdated_heights = unupdated_fork.get_outdated_heights()?; 206 | assert_eq!(outdated_heights.len(), 1); 207 | 208 | let gen_state_patch = updated_fork.generate_state_patch(outdated_heights, updated_tip_hash)?; 209 | unupdated_fork.update_states(&gen_state_patch)?; 210 | assert_eq!(unupdated_fork.get_outdated_contracts()?.len(), 0); 211 | chain.update_states(&draft.patch)?; 212 | 213 | assert_eq!(chain.get_height()?, 2); 214 | assert_eq!(chain.get_outdated_contracts()?.len(), 0); 215 | 216 | assert!(matches!( 217 | chain.apply_tx( 218 | &alice 219 | .call_function( 220 | cid, 221 | 0, 222 | state_delta.clone(), 223 | state_model.compress::(&full_state.data)?, 224 | zk::ZkProof::Dummy(true), 225 | Money(0), 226 | Money(0), 227 | 1, 228 | ) 229 | .tx, 230 | false 231 | ), 232 | Err(BlockchainError::InvalidTransactionNonce) 233 | )); 234 | 235 | assert!(matches!( 236 | chain.apply_tx( 237 | &alice 238 | .call_function( 239 | ContractId::from_str( 240 | "0000000000000000000000000000000000000000000000000000000000000000" 241 | ) 242 | .unwrap(), 243 | 0, 244 | state_delta.clone(), 245 | state_model.compress::(&full_state.data)?, 246 | zk::ZkProof::Dummy(true), 247 | Money(0), 248 | Money(0), 249 | 2, 250 | ) 251 | .tx, 252 | false 253 | ), 254 | Err(BlockchainError::ContractNotFound) 255 | )); 256 | 257 | assert!(matches!( 258 | chain.apply_tx( 259 | &alice 260 | .call_function( 261 | cid, 262 | 1, 263 | state_delta.clone(), 264 | state_model.compress::(&full_state.data)?, 265 | zk::ZkProof::Dummy(true), 266 | Money(0), 267 | Money(0), 268 | 2, 269 | ) 270 | .tx, 271 | false 272 | ), 273 | Err(BlockchainError::ContractFunctionNotFound) 274 | )); 275 | 276 | assert!(matches!( 277 | chain.apply_tx( 278 | &alice 279 | .call_function( 280 | cid, 281 | 0, 282 | state_delta, 283 | state_model.compress::(&full_state.data)?, 284 | zk::ZkProof::Dummy(false), 285 | Money(0), 286 | Money(0), 287 | 2, 288 | ) 289 | .tx, 290 | false 291 | ), 292 | Err(BlockchainError::IncorrectZkProof) 293 | )); 294 | 295 | chain.rollback()?; 296 | 297 | assert_eq!(chain.get_height()?, 1); 298 | assert_eq!(chain.get_outdated_contracts()?.len(), 0); 299 | 300 | chain.rollback()?; 301 | 302 | assert_eq!(chain.get_height()?, 0); 303 | assert_eq!(chain.get_outdated_contracts()?.len(), 0); 304 | 305 | Ok(()) 306 | } 307 | -------------------------------------------------------------------------------- /src/zk/test/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::core::ContractId; 3 | use crate::db::{KvStore, RamKvStore, WriteOp}; 4 | use std::ops::*; 5 | use std::str::FromStr; 6 | 7 | #[derive(Clone)] 8 | struct SumHasher; 9 | impl ZkHasher for SumHasher { 10 | const MAX_ARITY: usize = 16; 11 | fn hash(vals: &[ZkScalar]) -> ZkScalar { 12 | let mut sum = ZkScalar::from(0); 13 | for v in vals.iter() { 14 | sum.add_assign(v); 15 | } 16 | sum 17 | } 18 | } 19 | 20 | fn empty_contract(state_model: ZkStateModel) -> ZkContract { 21 | ZkContract { 22 | initial_state: ZkCompressedState::empty::(state_model.clone()).into(), 23 | state_model: state_model, 24 | payment_functions: vec![], 25 | functions: vec![], 26 | } 27 | } 28 | 29 | #[test] 30 | fn test_u64_conversion() { 31 | let zero: u64 = ZkScalar::from(0).try_into().unwrap(); 32 | let num123: u64 = ZkScalar::from(123).try_into().unwrap(); 33 | let u64max: u64 = ZkScalar::from(u64::MAX).try_into().unwrap(); 34 | assert_eq!(zero, 0); 35 | assert_eq!(num123, 123); 36 | assert_eq!(u64max, u64::MAX); 37 | let u64max_plus_1: Result = (ZkScalar::from(u64::MAX) + ZkScalar::from(1)).try_into(); 38 | assert!(u64max_plus_1.is_err()); 39 | } 40 | 41 | #[test] 42 | fn test_zk_list_membership_proof() { 43 | let model = ZkStateModel::Struct { 44 | field_types: vec![ 45 | ZkStateModel::Scalar, 46 | ZkStateModel::List { 47 | log4_size: 4, 48 | item_type: Box::new(ZkStateModel::Scalar), 49 | }, 50 | ], 51 | }; 52 | let mut builder = ZkStateBuilder::::new(model); 53 | for i in 0..256 { 54 | builder 55 | .batch_set(&ZkDeltaPairs( 56 | [(ZkDataLocator(vec![1, i]), Some(ZkScalar::from(i as u64)))].into(), 57 | )) 58 | .unwrap(); 59 | } 60 | for i in 0..256 { 61 | let mut accum = ZkScalar::from(i as u64); 62 | for part in builder.prove(ZkDataLocator(vec![1]), i).unwrap() { 63 | for val in part.iter() { 64 | accum.add_assign(val); 65 | } 66 | } 67 | assert_eq!(accum, ZkScalar::from(32640)); // sum(0..255) 68 | } 69 | } 70 | 71 | #[test] 72 | fn test_state_manager_scalar() -> Result<(), StateManagerError> { 73 | let mut db = RamKvStore::new(); 74 | 75 | let c0 = 76 | ContractId::from_str("0000000000000000000000000000000000000000000000000000000000000000") 77 | .unwrap(); 78 | 79 | db.update(&[WriteOp::Put( 80 | format!("contract_{}", c0).into(), 81 | empty_contract::(ZkStateModel::Scalar).into(), 82 | )])?; 83 | 84 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 85 | 86 | KvStoreStateManager::::update_contract( 87 | &mut db, 88 | c0, 89 | &ZkDeltaPairs( 90 | [(ZkDataLocator(vec![]), Some(ZkScalar::from(0xf)))] 91 | .into_iter() 92 | .collect(), 93 | ), 94 | )?; 95 | 96 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 97 | 98 | Ok(()) 99 | } 100 | 101 | #[test] 102 | fn test_state_manager_struct() -> Result<(), StateManagerError> { 103 | let mut db = RamKvStore::new(); 104 | 105 | let c0 = 106 | ContractId::from_str("0000000000000000000000000000000000000000000000000000000000000000") 107 | .unwrap(); 108 | 109 | db.update(&[WriteOp::Put( 110 | format!("contract_{}", c0).into(), 111 | empty_contract::(ZkStateModel::Struct { 112 | field_types: vec![ZkStateModel::Scalar, ZkStateModel::Scalar], 113 | }) 114 | .into(), 115 | )])?; 116 | 117 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 118 | 119 | KvStoreStateManager::::update_contract( 120 | &mut db, 121 | c0, 122 | &ZkDeltaPairs( 123 | [(ZkDataLocator(vec![0]), Some(ZkScalar::from(0xf)))] 124 | .into_iter() 125 | .collect(), 126 | ), 127 | )?; 128 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 129 | 130 | KvStoreStateManager::::update_contract( 131 | &mut db, 132 | c0, 133 | &ZkDeltaPairs( 134 | [(ZkDataLocator(vec![1]), Some(ZkScalar::from(0xf0)))] 135 | .into_iter() 136 | .collect(), 137 | ), 138 | )?; 139 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 140 | 141 | KvStoreStateManager::::update_contract( 142 | &mut db, 143 | c0, 144 | &ZkDeltaPairs( 145 | [(ZkDataLocator(vec![0]), Some(ZkScalar::from(0xf00)))] 146 | .into_iter() 147 | .collect(), 148 | ), 149 | )?; 150 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 151 | 152 | KvStoreStateManager::::update_contract( 153 | &mut db, 154 | c0, 155 | &ZkDeltaPairs( 156 | [(ZkDataLocator(vec![0]), Some(ZkScalar::from(0xf)))] 157 | .into_iter() 158 | .collect(), 159 | ), 160 | )?; 161 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 162 | 163 | KvStoreStateManager::::update_contract( 164 | &mut db, 165 | c0, 166 | &ZkDeltaPairs( 167 | [ 168 | (ZkDataLocator(vec![0]), Some(ZkScalar::from(0x0))), 169 | (ZkDataLocator(vec![1]), Some(ZkScalar::from(0x0))), 170 | ] 171 | .into_iter() 172 | .collect(), 173 | ), 174 | )?; 175 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 176 | 177 | Ok(()) 178 | } 179 | 180 | #[test] 181 | fn test_state_manager_list() -> Result<(), StateManagerError> { 182 | let mut db = RamKvStore::new(); 183 | 184 | let c0 = 185 | ContractId::from_str("0000000000000000000000000000000000000000000000000000000000000000") 186 | .unwrap(); 187 | 188 | let mut roots = Vec::new(); 189 | 190 | db.update(&[WriteOp::Put( 191 | format!("contract_{}", c0).into(), 192 | empty_contract::(ZkStateModel::List { 193 | log4_size: 3, 194 | item_type: Box::new(ZkStateModel::Struct { 195 | field_types: vec![ZkStateModel::Scalar, ZkStateModel::Scalar], 196 | }), 197 | }) 198 | .into(), 199 | )])?; 200 | 201 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 202 | roots.push(KvStoreStateManager::::root(&db, c0)?); 203 | 204 | KvStoreStateManager::::update_contract( 205 | &mut db, 206 | c0, 207 | &ZkDeltaPairs( 208 | [(ZkDataLocator(vec![62, 0]), Some(ZkScalar::from(0xf00000)))] 209 | .into_iter() 210 | .collect(), 211 | ), 212 | )?; 213 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 214 | roots.push(KvStoreStateManager::::root(&db, c0)?); 215 | 216 | KvStoreStateManager::::update_contract( 217 | &mut db, 218 | c0, 219 | &ZkDeltaPairs( 220 | [(ZkDataLocator(vec![33, 0]), Some(ZkScalar::from(0xf)))] 221 | .into_iter() 222 | .collect(), 223 | ), 224 | )?; 225 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 226 | roots.push(KvStoreStateManager::::root(&db, c0)?); 227 | 228 | KvStoreStateManager::::update_contract( 229 | &mut db, 230 | c0, 231 | &ZkDeltaPairs( 232 | [(ZkDataLocator(vec![33, 1]), Some(ZkScalar::from(0xf0)))] 233 | .into_iter() 234 | .collect(), 235 | ), 236 | )?; 237 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 238 | roots.push(KvStoreStateManager::::root(&db, c0)?); 239 | 240 | KvStoreStateManager::::update_contract( 241 | &mut db, 242 | c0, 243 | &ZkDeltaPairs( 244 | [(ZkDataLocator(vec![33, 0]), Some(ZkScalar::from(0xf00)))] 245 | .into_iter() 246 | .collect(), 247 | ), 248 | )?; 249 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 250 | roots.push(KvStoreStateManager::::root(&db, c0)?); 251 | 252 | KvStoreStateManager::::update_contract( 253 | &mut db, 254 | c0, 255 | &ZkDeltaPairs( 256 | [(ZkDataLocator(vec![33, 0]), Some(ZkScalar::from(0xf)))] 257 | .into_iter() 258 | .collect(), 259 | ), 260 | )?; 261 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 262 | roots.push(KvStoreStateManager::::root(&db, c0)?); 263 | 264 | println!( 265 | "Full: {:?}", 266 | KvStoreStateManager::::get_full_state(&db, c0)?.data 267 | ); 268 | 269 | KvStoreStateManager::::update_contract( 270 | &mut db, 271 | c0, 272 | &ZkDeltaPairs( 273 | [ 274 | (ZkDataLocator(vec![33, 0]), Some(ZkScalar::from(0x0))), 275 | (ZkDataLocator(vec![33, 1]), Some(ZkScalar::from(0x0))), 276 | ] 277 | .into_iter() 278 | .collect(), 279 | ), 280 | )?; 281 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 282 | roots.push(KvStoreStateManager::::root(&db, c0)?); 283 | 284 | KvStoreStateManager::::update_contract( 285 | &mut db, 286 | c0, 287 | &ZkDeltaPairs( 288 | [(ZkDataLocator(vec![62, 0]), Some(ZkScalar::from(0x0)))] 289 | .into_iter() 290 | .collect(), 291 | ), 292 | )?; 293 | println!("{:?}", KvStoreStateManager::::root(&db, c0)); 294 | 295 | // KvStoreStateManager::::reset_contract(c0, ZkDeltaPairs(Default::default()), Default::default())?; 296 | 297 | while KvStoreStateManager::::height_of(&db, c0)? > 2 { 298 | if let Some(expected_root) = roots.pop() { 299 | assert_eq!( 300 | Some(expected_root), 301 | KvStoreStateManager::::rollback_contract(&mut db, c0)? 302 | ); 303 | println!( 304 | "{:?} == {:?}", 305 | KvStoreStateManager::::root(&db, c0), 306 | expected_root 307 | ); 308 | } 309 | } 310 | 311 | Ok(()) 312 | } 313 | -------------------------------------------------------------------------------- /src/config/blockchain.rs: -------------------------------------------------------------------------------- 1 | use super::UNIT; 2 | 3 | use crate::blockchain::{BlockAndPatch, BlockchainConfig, ZkBlockchainPatch}; 4 | use crate::common::*; 5 | use crate::consensus::pow::Difficulty; 6 | use crate::core::{ 7 | Address, Block, ContractId, Header, Money, ProofOfWork, Signature, Transaction, 8 | TransactionAndDelta, TransactionData, ZkHasher, 9 | }; 10 | use crate::zk; 11 | 12 | #[cfg(test)] 13 | use crate::wallet::Wallet; 14 | 15 | const MPN_LOG4_ACCOUNT_CAPACITY: u8 = 15; 16 | const MPN_LOG4_PAYMENT_CAPACITY: u8 = 3; 17 | 18 | lazy_static! { 19 | pub static ref MPN_UPDATE_VK: zk::groth16::Groth16VerifyingKey = 20 | bincode::deserialize(&hex::decode("3f199befdafa0452eb41dbafe5e0a36381fe1ba7d698dc16e620591540c991969eb32ee78cd011b62eb96b503ba4b317a4c8552ff5e308fad0c0b0ef17aaaf65f53fc4b9dbdb89ba6f0c8199dd92dff453e33eede0c0845117ca822f6e6a8811007bc4cbb2b740f7ebc412a231b7183fadcb003ce07f10e8d5133c6d2588a835305ca2330692c028056f543d366575f90c066b813b3a2266b13cb19ad010c5909265ff799aec091f409cf04effcc5743645effb04d0019796e0755fcb7865b86060037e0d10bd2768642a9363085d4b323b4e0cc51e3d8da429bcd985e46f0fb32395674724e91f418e29c9020de45f6780641ad530d672dcb6f7eba18f93347221a89dba2b6914539eabaa57830668668ade353de22645e83300e82ca3c5b0f0719ff4e34ce1062331a9578aa4e392bd57baec7b6b264d37f59b6063fce47b38da04e23bc4a96b769c4c9cd58290f86a30b7ce7e0f8c0d5da07841f4369b6a4f5f376c34629dcfc05760493918d99bd99b211cb0bfdcecaa4f3ccf243d29a0c03120043a0bdc61aad71aeed05b55b5ba8cd6ba27e42d6d02f3886f8ba505977033909aedc65aa437e5ddc9827d6f81f867f1924de0b5d7557aa566a088af9f402bb760bac45c375bf77cbffd4d9ccb4ff12b2ead07f54428c79bced24573261912f15a5f16118dc0ce771342afdabe7b37cd137a0ecefe0e22a1daa171fa3e3e2c72eb746ac0b1359faa42a0151ab6940570f5c87743f4b8ab7efe9f2149d8cc94756fe35912b14ed162e571c54ba40c36ce730ee588321458a7f40f23888a9e02d1900391fe965a04a337425cba7c7237f450b94ef74f146025ff7fd9ce4ba8cbb75283e54d5a3c95598a32c234ad05b1ec703706f129ea17a5ac43aab60f009119b9c1e1de864312238df3a4ba94c502c3262582d6a2251b27c147275d31acf056218000749440a67da1be23a2c257a6b1ae68cd22981ea90f275f5f16822029053d21bb6423df3ed1767b1f42eb16c87dbb3116295970ca234fc46ea9e8e85a80c8d40d3212e4fb9929561e7f3fd229200d453bbc0165791853786fb8b1df54ca7bd10bd9d9f636ddcb8222b9509f735092a6610e9cdc90c6a71974a8a8452285a86ce29e7096243a88bdd0e8bf3bd7632d213816301f20f04bbf13060d3b4029dc218e146c55b641c5c3f8d9f145985f5fd83023c92f2b86f816deb22bce75668660e0004000000000000007a38f72f670e288976916f2e184ce1eaae4a37e9403c6ac1f2f380afe8cacdae847a68c2efa31033bb259351333b7c1180b67f5944285563825e4e8efa5609d7241a3e30371db73e42822d551ade46401b41ef3219fe07caf9e75fdb8ea00f18000bba41dcca0685de6cc4bbd05d1e3b2b7f7cac5139273bf3ff06f84af51505acc363230f63d8e283f17bbc2ac4c7cc15de6fc610be3f23b50ef441f7cfee68472586c315f84e0dd5a42d5c1ca434e419acd304e53f16fbafca3dbfa48b9253070093e2b6c99569701dac13767d047068d357afabd51313913963376d8f8699a85fd8f0efbd33ba59217aed553bce86880069679224036986cb435f6e5956b72058ad930e76eadfe6f94a39ddda623e8bfbe20e8e5fe8b81c9f381acb8226e28306000a6a7319bac9abda9b0be3257fe4dbc5c4b4e51de643f0a268dc95b7beb8fa32f52405f4c4b81296553ceeb4aee2a3163d4336b0bdcee33d13b72604078943e746e9299872cea6287e799fc21cf576619b2e953fc352937436ad23673f5b140200").unwrap()).unwrap(); 21 | pub static ref MPN_PAYMENT_VK: zk::groth16::Groth16VerifyingKey = 22 | bincode::deserialize(&hex::decode("5b13098f03bd2176afed05ef5b5e8e4af9c45dde7fcd8b5cfa35b82216e6f3c53cb9ea16cee040b4fe897a6cc85965037a59ef5080e34918d89d1fa48b017c46621965ec48d43c9a6a458344ab2d57cef3cee164d0ad963cfaeca878814e7e030067262dce13114a1a5746b33398b45cb7ce6a058496c54e3e35b0e5c499f5282e42cddb5e2a3e10042ba3109e556d3a152aed6a63251792884fb25fae55af429c558fe5c9d859e4beedf246d774bf71dbf545a6fe040015171a0cd06fbeea031300d23f696e716e31ab49a737323043e1d346f273020cc03b35dc29802793ef9fe3ead5cef00d2554786d19039954aa2603bae47eae410304d4881617bbe899fda942d0bdea0804ba112aecf41ae5c215fbcb2562e900db5c4e9bf1443ae91bf11263100c1a71e1a21f64c68cffd8367fb419ea402afd6beb391161318a1508e9b7c83d0444c9ee946f7e001f1f809b9a09f42ea8035ca3de8e6463ecf28379042354fe0ac72f1ad8e808a376b2d87a22456fb9176e7aa4a9a3e6568930d9ff621100379f0bb2838c775b508642e27a419c97dc84e62e5fced4c203fe624c3c8c802dc37da9f8cf9d15b860a213350e296b054a2bf3fa14cc3b82f0bb5ef376864292fdc2a96e81d24c71e52c1abbdb6708dadc4ea55059284293abd6a194b07a8613eb969386846265c37861b77cb09b43651b1be78410dfc9d42300dcc488c38f6985ff4bca42bc8701d94e5a8a17463e0a42fef67dec88c6e4a95f381b11643d975ea9588bc95703d07795b60dff923cd8a50217360b75147394cfd5cfab982d0c00091b61c195b1ea129ab2aa06893d2ebcf4e6f6eed1940bf59c88b8e892152a96ac852ca3ffc66a24977c2d429ad9501419d2b2c803137b7bedf09c9b6e86d1ce3b3e4c04c66f2d192913a30532e4de77e6c1c12e858bbb69ce3c928f7e166900002ac279436e4b5c593d2a142db175f64dd5b70b4fd2a10c3931b8c2b1be5f70506d6b32fb71aab99e06aab537df893403a6c2393a03513a6a077b1c42f431c27271cdabf25e2b9d8749e115b9f55a7a8fb5341b9d98f0a549cde6e47480c94c10772da0dd837611f5859785239d45bda10b1a6d07af8525d7dcb2dd7347e4c840d8aa1087a7ac6c0e6629079f308138019734578f716328296ac013aa273481971c86bd5203127459cacd5bdd32ea62a463722daa2305ea4b12e70a0585c70719000400000000000000dfb7739170e202f885129928f96224e304beb0a021f343c0e8c4287e21b1adeea3007dadd7bb8d47f0b376dbe299191769e156bb006f9fc074c20ed3568562c8d6a67189badd8d792f98d98faf10be478742f04f77d021da086661ea104bbe0b0010d7d1de5a487ba9d5781a7c5426e0a4f038d91ebd9696b7150491acad38d7b5789bedde564dc07ee2b57a89cecf7503e97e54a525eca5e0ae2b0032fe159154445265f96edc885b5d6c13115b468a4e954b48cb1b3a48beeee464e1d05dba12002e471db0eee21d0c8df4137a60cd116fbf6e712a005e633b3c0511a005be19ce2dc7e09b7f309941898bbd54554f2e028a012474c1f1ac22b24ad561f3d39cd724cb12a4f7af03218cbcc69385a30977ac564e873204cbeb62e9bf8160e5430c0027b4ba4c614a70b8ad429bcd0f944b6df4998f4b2d7df6b5fa57d205f845e726e44fb5d759d909d9f45404dacd1e0d03d67ef6d66f54bf78caf3c28372eee5ccb756bee34c0a343d49762d3f24f681ff52ea405d67a16a9ae541d0923576270900").unwrap()).unwrap(); 23 | } 24 | 25 | fn get_mpn_contract() -> TransactionAndDelta { 26 | let mpn_state_model = zk::ZkStateModel::List { 27 | log4_size: MPN_LOG4_ACCOUNT_CAPACITY, 28 | item_type: Box::new(zk::ZkStateModel::Struct { 29 | field_types: vec![ 30 | zk::ZkStateModel::Scalar, // Nonce 31 | zk::ZkStateModel::Scalar, // Pub-key X 32 | zk::ZkStateModel::Scalar, // Pub-key Y 33 | zk::ZkStateModel::Scalar, // Balance 34 | ], 35 | }), 36 | }; 37 | let mpn_contract = zk::ZkContract { 38 | state_model: mpn_state_model.clone(), 39 | initial_state: zk::ZkCompressedState::empty::(mpn_state_model), 40 | payment_functions: vec![zk::ZkPaymentVerifierKey { 41 | verifier_key: zk::ZkVerifierKey::Groth16(Box::new(MPN_PAYMENT_VK.clone())), 42 | log4_payment_capacity: MPN_LOG4_PAYMENT_CAPACITY, 43 | }], 44 | functions: vec![zk::ZkVerifierKey::Groth16(Box::new(MPN_UPDATE_VK.clone()))], 45 | }; 46 | let mpn_contract_create_tx = Transaction { 47 | src: Address::Treasury, 48 | data: TransactionData::CreateContract { 49 | contract: mpn_contract, 50 | }, 51 | nonce: 1, 52 | fee: Money(0), 53 | sig: Signature::Unsigned, 54 | }; 55 | TransactionAndDelta { 56 | tx: mpn_contract_create_tx, 57 | state_delta: Some(zk::ZkDeltaPairs::default()), 58 | } 59 | } 60 | 61 | #[cfg(test)] 62 | fn get_test_mpn_contract() -> TransactionAndDelta { 63 | let mut mpn_tx_delta = get_mpn_contract(); 64 | let init_state = zk::ZkDataPairs( 65 | [(zk::ZkDataLocator(vec![100]), zk::ZkScalar::from(200))] 66 | .into_iter() 67 | .collect(), 68 | ); 69 | match &mut mpn_tx_delta.tx.data { 70 | TransactionData::CreateContract { contract } => { 71 | contract.state_model = zk::ZkStateModel::List { 72 | log4_size: 5, 73 | item_type: Box::new(zk::ZkStateModel::Scalar), 74 | }; 75 | contract.initial_state = contract 76 | .state_model 77 | .compress::(&init_state) 78 | .unwrap(); 79 | contract.payment_functions = vec![zk::ZkPaymentVerifierKey { 80 | verifier_key: zk::ZkVerifierKey::Dummy, 81 | log4_payment_capacity: 1, 82 | }]; 83 | contract.functions = vec![zk::ZkVerifierKey::Dummy]; 84 | } 85 | _ => panic!(), 86 | } 87 | mpn_tx_delta.state_delta = Some(init_state.as_delta()); 88 | mpn_tx_delta 89 | } 90 | 91 | pub fn get_blockchain_config() -> BlockchainConfig { 92 | let mpn_tx_delta = get_mpn_contract(); 93 | let mpn_contract_id = ContractId::new(&mpn_tx_delta.tx); 94 | 95 | let min_diff = Difficulty(0x020fffff); 96 | 97 | let blk = Block { 98 | header: Header { 99 | parent_hash: Default::default(), 100 | number: 0, 101 | block_root: Default::default(), 102 | proof_of_work: ProofOfWork { 103 | timestamp: 0, 104 | target: min_diff, 105 | nonce: 0, 106 | }, 107 | }, 108 | body: vec![mpn_tx_delta.tx], 109 | }; 110 | 111 | BlockchainConfig { 112 | mpn_contract_id, 113 | genesis: BlockAndPatch { 114 | block: blk, 115 | patch: ZkBlockchainPatch { 116 | patches: [( 117 | mpn_contract_id, 118 | zk::ZkStatePatch::Delta(mpn_tx_delta.state_delta.unwrap()), 119 | )] 120 | .into_iter() 121 | .collect(), 122 | }, 123 | }, 124 | total_supply: Money(2_000_000_000_u64 * UNIT), // 2 Billion ZIK 125 | reward_ratio: 100_000, // 1/100_000 -> 0.01% of Treasury Supply per block 126 | max_block_size: (1 * MB) as usize, 127 | max_delta_count: 1024, // Only allow max of 1024 ZkScalar cells to be added per block 128 | block_time: 60, // Seconds 129 | difficulty_calc_interval: 128, // Blocks 130 | 131 | // 0 63 -> BAZUKA BASE KEY 132 | // 64 2111 -> hash(blk#0) 133 | // 2112 4159 -> hash(blk#2048) 134 | // 4160 6207 -> hash(blk#4096) 135 | // ... 136 | pow_base_key: b"BAZUKA BASE KEY", 137 | pow_key_change_delay: 64, // Blocks 138 | pow_key_change_interval: 2048, // Blocks 139 | 140 | // New block's timestamp should be higher than median 141 | // timestamp of 10 previous blocks 142 | median_timestamp_count: 10, 143 | 144 | // We expect a minimum number of MPN contract updates 145 | // in a block to consider it valid 146 | mpn_num_function_calls: 1, 147 | mpn_num_contract_payments: 1, 148 | 149 | minimum_pow_difficulty: min_diff, 150 | } 151 | } 152 | 153 | #[cfg(test)] 154 | pub fn get_test_blockchain_config() -> BlockchainConfig { 155 | let mpn_tx_delta = get_test_mpn_contract(); 156 | let mpn_contract_id = ContractId::new(&mpn_tx_delta.tx); 157 | println!("{}", mpn_contract_id); 158 | 159 | let min_diff = Difficulty(0x007fffff); 160 | 161 | let mut conf = get_blockchain_config(); 162 | conf.mpn_num_contract_payments = 0; 163 | conf.mpn_num_function_calls = 0; 164 | conf.mpn_contract_id = mpn_contract_id; 165 | conf.minimum_pow_difficulty = min_diff; 166 | conf.genesis.block.header.proof_of_work.target = min_diff; 167 | 168 | conf.genesis.block.body[0] = get_test_mpn_contract().tx; 169 | let abc = Wallet::new(Vec::from("ABC")); 170 | conf.genesis.block.body.push(Transaction { 171 | src: Address::Treasury, 172 | data: TransactionData::RegularSend { 173 | dst: abc.get_address(), 174 | amount: Money(10000), 175 | }, 176 | nonce: 2, 177 | fee: Money(0), 178 | sig: Signature::Unsigned, 179 | }); 180 | conf.genesis.patch = ZkBlockchainPatch { 181 | patches: [( 182 | mpn_contract_id, 183 | zk::ZkStatePatch::Delta(mpn_tx_delta.state_delta.unwrap()), 184 | )] 185 | .into_iter() 186 | .collect(), 187 | }; 188 | conf 189 | } 190 | --------------------------------------------------------------------------------