├── xelis_wallet ├── src │ ├── storage │ │ ├── backend │ │ │ ├── native.rs │ │ │ └── mod.rs │ │ └── types.rs │ ├── mnemonics │ │ └── languages │ │ │ └── mod.rs │ ├── lib.rs │ ├── api │ │ ├── mod.rs │ │ ├── server │ │ │ └── mod.rs │ │ └── xswd │ │ │ └── error.rs │ └── precomputed_tables │ │ ├── mod.rs │ │ └── native.rs └── Cargo.toml ├── .dockerignore ├── xelis_daemon ├── src │ ├── core │ │ ├── storage │ │ │ ├── types │ │ │ │ ├── mod.rs │ │ │ │ └── topoheight_metadata.rs │ │ │ ├── rocksdb │ │ │ │ ├── types │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── contract.rs │ │ │ │ │ ├── asset.rs │ │ │ │ │ ├── block_metadata.rs │ │ │ │ │ └── account.rs │ │ │ │ └── providers │ │ │ │ │ ├── versioned │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── contract │ │ │ │ │ │ ├── data.rs │ │ │ │ │ │ └── balance.rs │ │ │ │ │ ├── balance.rs │ │ │ │ │ └── dag_order.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── tips.rs │ │ │ │ │ ├── network.rs │ │ │ │ │ ├── merkle.rs │ │ │ │ │ ├── pruned_topoheight.rs │ │ │ │ │ ├── contract │ │ │ │ │ └── contract_logs.rs │ │ │ │ │ └── snapshot.rs │ │ │ ├── providers │ │ │ │ ├── versioned │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── dag_order.rs │ │ │ │ │ ├── registrations.rs │ │ │ │ │ ├── contract │ │ │ │ │ │ ├── scheduled_execution.rs │ │ │ │ │ │ ├── data.rs │ │ │ │ │ │ ├── balance.rs │ │ │ │ │ │ └── mod.rs │ │ │ │ │ ├── asset_supply.rs │ │ │ │ │ ├── asset.rs │ │ │ │ │ ├── nonce.rs │ │ │ │ │ ├── balance.rs │ │ │ │ │ └── multisig.rs │ │ │ │ ├── tips.rs │ │ │ │ ├── cache.rs │ │ │ │ ├── pruned_topoheight.rs │ │ │ │ ├── network.rs │ │ │ │ ├── snapshot.rs │ │ │ │ ├── contract │ │ │ │ │ ├── contract_logs.rs │ │ │ │ │ ├── balance.rs │ │ │ │ │ ├── scheduled_execution.rs │ │ │ │ │ └── data.rs │ │ │ │ ├── merkle.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── blockdag.rs │ │ │ │ ├── state.rs │ │ │ │ ├── block_execution_order.rs │ │ │ │ ├── dag_order.rs │ │ │ │ ├── transaction.rs │ │ │ │ ├── nonce.rs │ │ │ │ ├── asset_supply.rs │ │ │ │ ├── difficulty.rs │ │ │ │ ├── client_protocol.rs │ │ │ │ ├── account.rs │ │ │ │ ├── block.rs │ │ │ │ ├── multisig.rs │ │ │ │ └── blocks_at_height.rs │ │ │ ├── sled │ │ │ │ ├── migrations.rs │ │ │ │ └── providers │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── versioned │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── nonce.rs │ │ │ │ │ ├── asset.rs │ │ │ │ │ ├── multisig.rs │ │ │ │ │ ├── contract │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ ├── data.rs │ │ │ │ │ │ └── balance.rs │ │ │ │ │ └── asset_supply.rs │ │ │ │ │ ├── tips_provider.rs │ │ │ │ │ ├── cache.rs │ │ │ │ │ ├── network.rs │ │ │ │ │ ├── pruned_topoheight.rs │ │ │ │ │ ├── merkle.rs │ │ │ │ │ ├── contract │ │ │ │ │ └── contract_logs.rs │ │ │ │ │ ├── snapshot.rs │ │ │ │ │ ├── blockdag.rs │ │ │ │ │ └── block_execution_order.rs │ │ │ └── snapshot │ │ │ │ ├── iterator_mode.rs │ │ │ │ ├── bytes_view.rs │ │ │ │ └── changes.rs │ │ ├── mod.rs │ │ ├── tx_cache.rs │ │ └── difficulty │ │ │ ├── v1.rs │ │ │ └── v2.rs │ ├── p2p │ │ ├── expirable_cache.rs │ │ └── packet │ │ │ ├── peer_disconnected.rs │ │ │ └── bootstrap │ │ │ └── mod.rs │ └── rpc │ │ └── getwork │ │ └── miner.rs └── Cargo.toml ├── xelis_miner ├── src │ └── config.rs └── Cargo.toml ├── xelis_common ├── src │ ├── transaction │ │ ├── payload │ │ │ ├── mod.rs │ │ │ ├── burn.rs │ │ │ ├── contract │ │ │ │ ├── deposits.rs │ │ │ │ └── deploy.rs │ │ │ └── multisig.rs │ │ ├── verify │ │ │ ├── zkp_cache.rs │ │ │ └── error.rs │ │ ├── extra_data │ │ │ └── shared_key.rs │ │ ├── builder │ │ │ ├── state.rs │ │ │ └── fee.rs │ │ ├── reference.rs │ │ └── source_commitment.rs │ ├── rpc │ │ ├── mod.rs │ │ ├── types.rs │ │ └── server │ │ │ ├── websocket │ │ │ └── http_request.rs │ │ │ └── mod.rs │ ├── contract │ │ ├── opaque │ │ │ ├── crypto │ │ │ │ ├── mod.rs │ │ │ │ └── proofs │ │ │ │ │ └── mod.rs │ │ │ └── storage │ │ │ │ └── btree │ │ │ │ └── record.rs │ │ ├── random.rs │ │ ├── source.rs │ │ ├── module.rs │ │ ├── provider.rs │ │ ├── metadata.rs │ │ └── cache.rs │ ├── serializer │ │ ├── count.rs │ │ ├── raw.rs │ │ ├── mod.rs │ │ ├── dynamic_len.rs │ │ └── writer.rs │ ├── crypto │ │ ├── elgamal │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── proofs │ │ │ └── range_proof.rs │ ├── tokio │ │ ├── sync │ │ │ └── mod.rs │ │ ├── executor.rs │ │ └── thread_pool.rs │ ├── prompt │ │ ├── art.rs │ │ ├── option.rs │ │ └── error.rs │ ├── time.rs │ ├── block │ │ └── mod.rs │ ├── lib.rs │ ├── account │ │ └── nonce.rs │ ├── context.rs │ └── difficulty.rs ├── build.rs └── benches │ ├── address.rs │ └── homomorphic_encryption.rs ├── .cargo └── config.toml ├── .gitignore ├── .github ├── workflows │ ├── rust.yml │ └── docker-publish.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── Dockerfile ├── Cross.toml ├── Cargo.toml └── LICENSE /xelis_wallet/src/storage/backend/native.rs: -------------------------------------------------------------------------------- 1 | pub use sled::{Db, Tree, open}; -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | target/ 3 | API.md 4 | README.md 5 | build_all.sh 6 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod topoheight_metadata; 2 | 3 | pub use topoheight_metadata::TopoHeightMetadata; -------------------------------------------------------------------------------- /xelis_miner/src/config.rs: -------------------------------------------------------------------------------- 1 | // daemon address by default when no specified 2 | pub const DEFAULT_DAEMON_ADDRESS: &str = "127.0.0.1:8080"; -------------------------------------------------------------------------------- /xelis_common/src/transaction/payload/mod.rs: -------------------------------------------------------------------------------- 1 | mod transfer; 2 | mod burn; 3 | mod multisig; 4 | mod contract; 5 | 6 | pub use transfer::*; 7 | pub use burn::*; 8 | pub use multisig::*; 9 | pub use contract::*; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod block_metadata; 2 | mod account; 3 | mod asset; 4 | mod contract; 5 | 6 | pub use block_metadata::*; 7 | pub use account::*; 8 | pub use asset::*; 9 | pub use contract::*; -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # rustflags = ["-C", "target-cpu=native", "--cfg", "tokio_unstable"] 3 | rustflags = ["-C", "target-cpu=native"] 4 | 5 | [target.wasm32-unknown-unknown] 6 | rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] -------------------------------------------------------------------------------- /xelis_common/src/rpc/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rpc-server")] 2 | pub mod server; 3 | 4 | #[cfg(feature = "rpc-client")] 5 | pub mod client; 6 | 7 | mod types; 8 | mod rpc_handler; 9 | mod error; 10 | 11 | pub use types::*; 12 | pub use error::*; 13 | pub use rpc_handler::*; -------------------------------------------------------------------------------- /xelis_wallet/src/mnemonics/languages/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod spanish; 2 | pub mod russian; 3 | pub mod portuguese; 4 | pub mod japanese; 5 | pub mod italian; 6 | pub mod german; 7 | pub mod french; 8 | pub mod esperanto; 9 | pub mod english; 10 | pub mod dutch; 11 | pub mod chinese_simplified; -------------------------------------------------------------------------------- /xelis_common/src/contract/opaque/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | mod hash; 2 | mod signature; 3 | mod ciphertext; 4 | mod proofs; 5 | mod ristretto; 6 | mod scalar; 7 | 8 | pub use hash::*; 9 | pub use signature::*; 10 | pub use ciphertext::*; 11 | pub use proofs::*; 12 | pub use ristretto::*; 13 | pub use scalar::*; -------------------------------------------------------------------------------- /xelis_common/src/serializer/count.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | pub struct Count(pub usize); 4 | 5 | impl Serializer for Count { 6 | fn write(&self, _: &mut Writer) {} 7 | 8 | fn read(reader: &mut Reader) -> Result { 9 | Ok(Self(reader.total_size())) 10 | } 11 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use crate::core::error::BlockchainError; 3 | 4 | #[async_trait] 5 | pub trait VersionedCacheProvider { 6 | // Clear all the internal caches if any 7 | async fn clear_versioned_data_caches(&mut self) -> Result<(), BlockchainError>; 8 | } -------------------------------------------------------------------------------- /xelis_common/src/crypto/elgamal/mod.rs: -------------------------------------------------------------------------------- 1 | mod compressed; 2 | mod ciphertext; 3 | mod key; 4 | mod signature; 5 | mod pedersen; 6 | 7 | pub use compressed::*; 8 | pub use ciphertext::Ciphertext; 9 | pub use key::*; 10 | pub use pedersen::*; 11 | pub use signature::*; 12 | 13 | pub use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as G; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.log 3 | mainnet/ 4 | testnet/ 5 | dev/ 6 | peerlist-*/ 7 | .vscode/ 8 | wallets/ 9 | peerlist-*.json 10 | build/ 11 | 12 | # ignore all profiling files 13 | flamegraph.svg 14 | perf.data 15 | perf.data.old 16 | 17 | # ignore generated files by XELIS 18 | difficulty_dataset.csv 19 | precomputed_tables_*.bin 20 | mainnet* 21 | testnet* 22 | stagenet* 23 | devnet* -------------------------------------------------------------------------------- /xelis_wallet/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod storage; 2 | pub mod wallet; 3 | pub mod config; 4 | pub mod cipher; 5 | pub mod entry; 6 | pub mod mnemonics; 7 | pub mod transaction_builder; 8 | pub mod error; 9 | 10 | pub mod precomputed_tables; 11 | 12 | #[cfg(feature = "network_handler")] 13 | pub mod daemon_api; 14 | 15 | #[cfg(feature = "network_handler")] 16 | pub mod network_handler; 17 | 18 | pub mod api; -------------------------------------------------------------------------------- /xelis_wallet/src/api/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | #[cfg(any(feature = "xswd", feature = "api_server"))] 3 | mod rpc; 4 | 5 | #[cfg(feature = "api_server")] 6 | mod server; 7 | #[cfg(feature = "xswd")] 8 | mod xswd; 9 | 10 | #[cfg(feature = "api_server")] 11 | pub use server::*; 12 | 13 | #[cfg(feature = "xswd")] 14 | pub use self::{ 15 | xswd::*, 16 | rpc::register_methods as register_rpc_methods 17 | }; 18 | -------------------------------------------------------------------------------- /xelis_common/src/contract/opaque/crypto/proofs/mod.rs: -------------------------------------------------------------------------------- 1 | mod ciphertext_validity; 2 | mod commitment_eq; 3 | mod range_proof; 4 | mod transcript; 5 | mod arbitrary_range; 6 | mod ownership; 7 | mod balance; 8 | 9 | pub use ciphertext_validity::*; 10 | pub use commitment_eq::*; 11 | pub use range_proof::*; 12 | pub use arbitrary_range::*; 13 | pub use ownership::*; 14 | pub use balance::*; 15 | pub use transcript::*; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/tips.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use crate::core::{error::BlockchainError, storage::Tips}; 3 | 4 | #[async_trait] 5 | pub trait TipsProvider { 6 | // Get current chain tips 7 | async fn get_tips(&self) -> Result; 8 | 9 | // Store chain tips 10 | async fn store_tips(&mut self, tips: &Tips) -> Result<(), BlockchainError>; 11 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | mod tx_cache; 2 | mod block_size_ema; 3 | 4 | pub mod config; 5 | pub mod blockchain; 6 | pub mod mempool; 7 | pub mod error; 8 | pub mod blockdag; 9 | pub mod storage; 10 | pub mod difficulty; 11 | pub mod simulator; 12 | pub mod nonce_checker; 13 | pub mod tx_selector; 14 | pub mod state; 15 | pub mod merkle; 16 | 17 | pub mod hard_fork; 18 | 19 | pub use tx_cache::*; 20 | pub use block_size_ema::*; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/versioned/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use crate::core::{error::BlockchainError, storage::{RocksStorage, VersionedCacheProvider}}; 3 | 4 | #[async_trait] 5 | impl VersionedCacheProvider for RocksStorage { 6 | // Clear all the internal caches if any 7 | async fn clear_versioned_data_caches(&mut self) -> Result<(), BlockchainError> { 8 | Ok(()) 9 | } 10 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/migrations.rs: -------------------------------------------------------------------------------- 1 | use log::debug; 2 | use xelis_common::config::VERSION; 3 | use crate::core::error::BlockchainError; 4 | use super::{SledStorage, DB_VERSION}; 5 | 6 | impl SledStorage { 7 | pub(super) fn handle_migrations(&mut self) -> Result<(), BlockchainError> { 8 | debug!("set DB version to {}", VERSION); 9 | self.extra.insert(DB_VERSION, VERSION)?; 10 | 11 | Ok(()) 12 | } 13 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use crate::core::{error::BlockchainError, storage::cache::ChainCache}; 3 | 4 | #[async_trait] 5 | pub trait CacheProvider { 6 | // Clear all the internal caches if any 7 | async fn clear_objects_cache(&mut self) -> Result<(), BlockchainError>; 8 | 9 | async fn chain_cache_mut(&mut self) -> Result<&mut ChainCache, BlockchainError>; 10 | 11 | async fn chain_cache(&self) -> &ChainCache; 12 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/mod.rs: -------------------------------------------------------------------------------- 1 | mod network; 2 | mod difficulty; 3 | mod dag_order; 4 | mod tips; 5 | mod transaction; 6 | mod pruned_topoheight; 7 | mod merkle; 8 | mod client_protocol; 9 | mod cache; 10 | mod blockdag; 11 | mod blocks_at_height; 12 | mod block; 13 | mod snapshot; 14 | mod block_execution_order; 15 | mod asset; 16 | mod asset_supply; 17 | mod account; 18 | mod balance; 19 | mod nonce; 20 | mod state; 21 | mod multisig; 22 | mod contract; 23 | mod versioned; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/mod.rs: -------------------------------------------------------------------------------- 1 | mod asset; 2 | mod asset_supply; 3 | mod blocks_at_height; 4 | mod dag_order; 5 | mod difficulty; 6 | mod pruned_topoheight; 7 | mod nonce; 8 | mod balance; 9 | mod client_protocol; 10 | mod transaction; 11 | mod block; 12 | mod blockdag; 13 | mod merkle; 14 | mod account; 15 | mod block_execution_order; 16 | mod network; 17 | mod multisig; 18 | mod snapshot; 19 | mod tips_provider; 20 | mod contract; 21 | mod versioned; 22 | mod cache; 23 | mod state; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::debug; 3 | 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{SledStorage, VersionedCacheProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedCacheProvider for SledStorage { 11 | async fn clear_versioned_data_caches(&mut self) -> Result<(), BlockchainError> { 12 | debug!("clear versioned data caches"); 13 | 14 | self.clear_caches(); 15 | Ok(()) 16 | } 17 | } -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Install Clang 20 | run: sudo apt-get update && sudo apt-get install -y clang 21 | - name: Build 22 | run: cargo build --verbose 23 | - name: Run tests 24 | run: cargo test --verbose 25 | -------------------------------------------------------------------------------- /xelis_common/src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | mod hash; 2 | mod address; 3 | mod transcript; 4 | mod human_readable_proof; 5 | 6 | pub mod elgamal; 7 | pub mod proofs; 8 | pub mod bech32; 9 | 10 | pub use hash::*; 11 | pub use address::*; 12 | pub use transcript::*; 13 | pub use human_readable_proof::*; 14 | 15 | pub use elgamal::{PrivateKey, KeyPair, Signature, SIGNATURE_SIZE}; 16 | 17 | /// Re-export the curve25519-dalek ecdlp module 18 | pub use curve25519_dalek::ecdlp; 19 | 20 | /// Public Key type used in the system 21 | pub type PublicKey = elgamal::CompressedPublicKey; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/pruned_topoheight.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | // This trait is used for pruning 6 | #[async_trait] 7 | pub trait PrunedTopoheightProvider { 8 | // get the pruned topoheight 9 | async fn get_pruned_topoheight(&self) -> Result, BlockchainError>; 10 | 11 | // set the pruned topoheight on disk 12 | async fn set_pruned_topoheight(&mut self, pruned_topoheight: Option) -> Result<(), BlockchainError>; 13 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/dag_order.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedDagOrderProvider { 7 | // Delete the topoheight for a block hash 8 | async fn delete_dag_order_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // Delete every block hashes <=> topoheight relations 11 | async fn delete_dag_order_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/network.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::network::Network; 2 | use crate::core::error::BlockchainError; 3 | 4 | pub trait NetworkProvider { 5 | // Get the network from cache 6 | fn get_network(&self) -> Result; 7 | 8 | // Is the network mainnet 9 | fn is_mainnet(&self) -> bool; 10 | 11 | // Set the network in the storage 12 | fn set_network(&mut self, network: &Network) -> Result<(), BlockchainError>; 13 | 14 | // Do we have a network stored in DB ? 15 | fn has_network(&self) -> Result; 16 | } -------------------------------------------------------------------------------- /xelis_common/src/transaction/verify/zkp_cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | 3 | use crate::crypto::Hash; 4 | 5 | #[async_trait] 6 | pub trait ZKPCache { 7 | // Did we already verified the ZK Proofs of this transaction? 8 | async fn is_already_verified(&self, hash: &Hash) -> Result; 9 | } 10 | 11 | #[derive(Default)] 12 | pub struct NoZKPCache; 13 | 14 | #[async_trait] 15 | impl ZKPCache for NoZKPCache { 16 | // Did we already verified the ZK Proofs of this transaction? 17 | async fn is_already_verified(&self, _: &Hash) -> Result { 18 | Ok(false) 19 | } 20 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report that should be fixed 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Version used** 14 | Give the exact version you're using (example: 1.12.0-345ef64) 15 | 16 | **To Reproduce** 17 | Give information to help us to reproduce the bug easily. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/registrations.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedRegistrationsProvider { 7 | // delete versioned registrations at topoheight 8 | async fn delete_versioned_registrations_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // delete versioned registrations above topoheight 11 | async fn delete_versioned_registrations_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/contract/scheduled_execution.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedScheduledExecutionsProvider { 7 | async fn delete_scheduled_executions_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 8 | 9 | async fn delete_scheduled_executions_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 10 | 11 | async fn delete_scheduled_executions_below_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/contract/data.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedContractDataProvider { 7 | async fn delete_versioned_contract_data_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 8 | 9 | async fn delete_versioned_contract_data_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 10 | 11 | async fn delete_versioned_contract_data_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /xelis_wallet/src/storage/backend/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any( 2 | all( 3 | target_arch = "wasm32", 4 | target_vendor = "unknown", 5 | target_os = "unknown" 6 | ), 7 | test 8 | ))] 9 | mod web; 10 | 11 | #[cfg(all( 12 | target_arch = "wasm32", 13 | target_vendor = "unknown", 14 | target_os = "unknown" 15 | ))] 16 | pub use web::*; 17 | 18 | #[cfg(not(all( 19 | target_arch = "wasm32", 20 | target_vendor = "unknown", 21 | target_os = "unknown" 22 | )))] 23 | mod native; 24 | 25 | #[cfg(not(all( 26 | target_arch = "wasm32", 27 | target_vendor = "unknown", 28 | target_os = "unknown" 29 | )))] 30 | pub use native::*; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/asset_supply.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedAssetsCirculatingSupplyProvider { 7 | async fn delete_versioned_assets_supply_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 8 | 9 | async fn delete_versioned_assets_supply_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 10 | 11 | async fn delete_versioned_assets_supply_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/contract/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedContractBalanceProvider { 7 | async fn delete_versioned_contract_balances_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 8 | 9 | async fn delete_versioned_contract_balances_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 10 | 11 | async fn delete_versioned_contract_balances_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 12 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /xelis_common/src/tokio/sync/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all( 2 | feature = "tokio", 3 | target_arch = "wasm32", 4 | target_vendor = "unknown", 5 | target_os = "unknown" 6 | ))] 7 | pub use tokio_with_wasm::sync::*; 8 | 9 | #[cfg(all( 10 | feature = "tokio", 11 | not(all( 12 | target_arch = "wasm32", 13 | target_vendor = "unknown", 14 | target_os = "unknown" 15 | )) 16 | ))] 17 | pub use tokio::sync::*; 18 | 19 | #[cfg(any(test, feature = "deadlock-detection"))] 20 | mod rwlock; 21 | #[cfg(feature = "deadlock-detection")] 22 | pub use rwlock::RwLock; 23 | 24 | #[cfg(any(test, feature = "deadlock-detection"))] 25 | mod mutex; 26 | #[cfg(feature = "deadlock-detection")] 27 | pub use mutex::Mutex; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/types/contract.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::{block::TopoHeight, serializer::*}; 2 | 3 | pub type ContractId = u64; 4 | pub type ContractDataId = u64; 5 | 6 | pub struct Contract { 7 | pub id: ContractId, 8 | pub module_pointer: Option, 9 | } 10 | 11 | impl Serializer for Contract { 12 | fn write(&self, writer: &mut Writer) { 13 | self.id.write(writer); 14 | self.module_pointer.write(writer); 15 | } 16 | 17 | fn read(reader: &mut Reader) -> Result { 18 | let id = ContractId::read(reader)?; 19 | let module_pointer = Option::read(reader)?; 20 | 21 | Ok(Self { 22 | id, 23 | module_pointer 24 | }) 25 | } 26 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/asset.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedAssetProvider { 7 | // delete versioned assets at topoheight 8 | async fn delete_versioned_assets_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // delete versioned assets above topoheight 11 | async fn delete_versioned_assets_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | 13 | // delete versioned assets below topoheight 14 | async fn delete_versioned_assets_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 15 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/nonce.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedNonceProvider { 7 | // delete versioned nonces at topoheight 8 | async fn delete_versioned_nonces_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // delete versioned nonces above topoheight 11 | async fn delete_versioned_nonces_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | 13 | // delete versioned nonces below topoheight 14 | async fn delete_versioned_nonces_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 15 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{CacheProvider, ChainCache, RocksStorage} 6 | }; 7 | 8 | #[async_trait] 9 | impl CacheProvider for RocksStorage { 10 | // Clear all the internal caches if any 11 | async fn clear_objects_cache(&mut self) -> Result<(), BlockchainError> { 12 | trace!("clear caches"); 13 | self.cache_mut().clear_caches(); 14 | Ok(()) 15 | } 16 | 17 | async fn chain_cache_mut(&mut self) -> Result<&mut ChainCache, BlockchainError> { 18 | Ok(&mut self.cache_mut().chain) 19 | } 20 | 21 | async fn chain_cache(&self) -> &ChainCache { 22 | &self.cache().chain 23 | } 24 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedBalanceProvider { 7 | // delete versioned balances at topoheight 8 | async fn delete_versioned_balances_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // delete versioned balances above topoheight 11 | async fn delete_versioned_balances_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | 13 | // delete versioned balances below topoheight 14 | async fn delete_versioned_balances_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 15 | } -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the changes and the related issue. 4 | 5 | ## Type of change 6 | 7 | Please select the right one. 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Documentation update 13 | 14 | ## Which part is impacted ? 15 | 16 | - [ ] Daemon 17 | - [ ] Wallet 18 | - [ ] Miner 19 | - [ ] Misc (documentation, comments, text...) 20 | 21 | ## Checklist 22 | 23 | - [ ] I have performed a self-review of my code 24 | - [ ] I have commented my code, particularly in hard-to-understand areas 25 | - [ ] My changes generate no new warnings -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/multisig.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::error::BlockchainError; 4 | 5 | #[async_trait] 6 | pub trait VersionedMultiSigProvider { 7 | // delete versioned multisigs at topoheight 8 | async fn delete_versioned_multisigs_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 9 | 10 | // delete versioned multisigs above topoheight 11 | async fn delete_versioned_multisigs_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | 13 | // delete versioned multisigs below topoheight 14 | async fn delete_versioned_multisigs_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 15 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/tips_provider.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::serializer::Serializer; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{sled::TIPS, SledStorage, Tips, TipsProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl TipsProvider for SledStorage { 11 | async fn get_tips(&self) -> Result { 12 | trace!("get tips"); 13 | Ok(self.cache().chain.tips.clone()) 14 | } 15 | 16 | async fn store_tips(&mut self, tips: &Tips) -> Result<(), BlockchainError> { 17 | trace!("Saving {} Tips", tips.len()); 18 | Self::insert_into_disk(self.snapshot.as_mut(), &self.extra, TIPS, tips.to_bytes())?; 19 | self.cache_mut().chain.tips = tips.clone(); 20 | 21 | Ok(()) 22 | } 23 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/snapshot.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | 3 | use async_trait::async_trait; 4 | use crate::core::{error::BlockchainError, storage::snapshot::Snapshot}; 5 | 6 | #[async_trait] 7 | pub trait SnapshotProvider { 8 | type Column: Hash + Eq; 9 | 10 | // Check if we have a snapshot already set 11 | async fn has_snapshot(&self) -> Result; 12 | 13 | // Start a commit point 14 | // This is useful to do some operations before applying the batch 15 | async fn start_snapshot(&mut self) -> Result<(), BlockchainError>; 16 | 17 | // Apply the batch to the storage 18 | async fn end_snapshot(&mut self, apply: bool) -> Result<(), BlockchainError>; 19 | 20 | async fn swap_snapshot(&mut self, other: Snapshot) -> Result>, BlockchainError>; 21 | } -------------------------------------------------------------------------------- /xelis_miner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xelis_miner" 3 | version = "1.21.0" 4 | edition = "2021" 5 | authors = ["Slixe "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | xelis_common = { path = "../xelis_common", features = ["prompt", "clap"] } 11 | tokio-tungstenite = { version = "0.27", features = ["rustls-tls-native-roots"] } 12 | 13 | # Common dependencies 14 | lazy_static = { workspace = true } 15 | serde = { workspace = true } 16 | serde_json = { workspace = true } 17 | tokio = { workspace = true, features = ["rt"] } 18 | clap = { workspace = true } 19 | anyhow = { workspace = true } 20 | rand = { workspace = true } 21 | log = { workspace = true } 22 | futures-util = { workspace = true } 23 | 24 | [features] 25 | default = ["api_stats"] 26 | api_stats = [] -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::debug; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{CacheProvider, ChainCache, SledStorage} 6 | }; 7 | 8 | #[async_trait] 9 | impl CacheProvider for SledStorage { 10 | async fn clear_objects_cache(&mut self) -> Result<(), BlockchainError> { 11 | debug!("clear caches"); 12 | self.cache_mut().clear_caches(); 13 | 14 | debug!("reload caches from disk"); 15 | // also load the atomic counters from disk 16 | self.load_cache_from_disk(); 17 | 18 | Ok(()) 19 | } 20 | 21 | async fn chain_cache_mut(&mut self) -> Result<&mut ChainCache, BlockchainError> { 22 | Ok(&mut self.cache_mut().chain) 23 | } 24 | 25 | async fn chain_cache(&self) -> &ChainCache { 26 | &self.cache().chain 27 | } 28 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/tips.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | rocksdb::{Column, TIPS}, 8 | Tips, 9 | RocksStorage, 10 | TipsProvider, 11 | } 12 | }; 13 | 14 | #[async_trait] 15 | impl TipsProvider for RocksStorage { 16 | // Get current chain tips 17 | async fn get_tips(&self) -> Result { 18 | trace!("get tips"); 19 | self.load_optional_from_disk(Column::Common, TIPS) 20 | .map(|v| v.unwrap_or_default()) 21 | } 22 | 23 | // Store chain tips 24 | async fn store_tips(&mut self, tips: &Tips) -> Result<(), BlockchainError> { 25 | trace!("store tips"); 26 | self.cache_mut().chain.tips = tips.clone(); 27 | self.insert_into_disk(Column::Common, TIPS, tips) 28 | } 29 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/snapshot/iterator_mode.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug)] 2 | pub enum Direction { 3 | Forward, 4 | Reverse, 5 | } 6 | 7 | #[cfg(feature = "rocksdb")] 8 | impl Into for Direction { 9 | fn into(self) -> rocksdb::Direction { 10 | match self { 11 | Direction::Forward => rocksdb::Direction::Forward, 12 | Direction::Reverse => rocksdb::Direction::Reverse, 13 | } 14 | } 15 | } 16 | 17 | #[derive(Copy, Clone, Debug)] 18 | pub enum IteratorMode<'a> { 19 | Start, 20 | End, 21 | // Allow for range start operations 22 | From(&'a [u8], Direction), 23 | // Strict prefix to all keys 24 | WithPrefix(&'a [u8], Direction), 25 | Range { 26 | lower_bound: &'a [u8], 27 | // NOTE: upper bound is NEVER included 28 | upper_bound: &'a [u8], 29 | direction: Direction, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /xelis_common/src/transaction/payload/burn.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::{crypto::Hash, serializer::*}; 5 | 6 | // Burn is a public payload allowing to use it as a proof of burn 7 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 8 | pub struct BurnPayload { 9 | pub asset: Hash, 10 | pub amount: u64 11 | } 12 | 13 | impl Serializer for BurnPayload { 14 | fn write(&self, writer: &mut Writer) { 15 | self.asset.write(writer); 16 | self.amount.write(writer); 17 | } 18 | 19 | fn read(reader: &mut Reader) -> Result { 20 | let asset = Hash::read(reader)?; 21 | let amount = reader.read_u64()?; 22 | Ok(BurnPayload { 23 | asset, 24 | amount 25 | }) 26 | } 27 | 28 | fn size(&self) -> usize { 29 | self.asset.size() + self.amount.size() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/contract/contract_logs.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | contract::ContractLog, 4 | crypto::Hash 5 | }; 6 | use crate::core::error::BlockchainError; 7 | 8 | #[async_trait] 9 | pub trait ContractLogsProvider { 10 | // Verify if the contract logs for a caller exist 11 | async fn has_contract_logs_for_caller(&self, caller: &Hash) -> Result; 12 | 13 | // Get the contract logs for a caller 14 | async fn get_contract_logs_for_caller(&self, caller: &Hash) -> Result, BlockchainError>; 15 | 16 | // Set the contract logs for a caller 17 | async fn set_contract_logs_for_caller(&mut self, caller: &Hash, logs: &Vec) -> Result<(), BlockchainError>; 18 | 19 | // Delete the contract outputs for a caller 20 | async fn delete_contract_logs_for_caller(&mut self, caller: &Hash) -> Result<(), BlockchainError>; 21 | } -------------------------------------------------------------------------------- /xelis_common/src/rpc/types.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::Value; 5 | 6 | pub const JSON_RPC_VERSION: &str = "2.0"; 7 | 8 | #[derive(Debug, Clone, Serialize, Deserialize)] 9 | #[serde(untagged)] 10 | pub enum Id { 11 | String(String), 12 | Number(usize), 13 | } 14 | 15 | #[derive(Clone, Serialize, Deserialize)] 16 | pub struct RpcRequest { 17 | pub jsonrpc: String, 18 | pub id: Option, 19 | pub method: String, 20 | pub params: Option 21 | } 22 | 23 | #[derive(Serialize)] 24 | pub struct RpcResponse<'a> { 25 | pub jsonrpc: &'a str, 26 | pub id: Cow<'a, Option>, 27 | pub result: Cow<'a, Value> 28 | } 29 | 30 | impl<'a> RpcResponse<'a> { 31 | pub fn new(id: Cow<'a, Option>, result: Cow<'a, Value>) -> Self { 32 | Self { 33 | jsonrpc: JSON_RPC_VERSION, 34 | id, 35 | result 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/network.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::{network::Network, serializer::Serializer}; 2 | use log::trace; 3 | use crate::core::{error::BlockchainError, storage::{sled::NETWORK, NetworkProvider, SledStorage}}; 4 | 5 | impl NetworkProvider for SledStorage { 6 | fn get_network(&self) -> Result { 7 | trace!("get network"); 8 | Ok(self.network) 9 | } 10 | 11 | fn is_mainnet(&self) -> bool { 12 | self.network.is_mainnet() 13 | } 14 | 15 | fn set_network(&mut self, network: &Network) -> Result<(), BlockchainError> { 16 | trace!("set network to {}", network); 17 | Self::insert_into_disk(self.snapshot.as_mut(), &self.extra, NETWORK, network.to_bytes())?; 18 | Ok(()) 19 | } 20 | 21 | fn has_network(&self) -> Result { 22 | trace!("has network"); 23 | self.contains_data(&self.extra, NETWORK) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/network.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::network::Network; 2 | use log::trace; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{ 6 | rocksdb::Column, 7 | NetworkProvider, 8 | RocksStorage 9 | } 10 | }; 11 | 12 | impl NetworkProvider for RocksStorage { 13 | fn get_network(&self) -> Result { 14 | trace!("get network"); 15 | Ok(self.network) 16 | } 17 | 18 | fn is_mainnet(&self) -> bool { 19 | self.network.is_mainnet() 20 | } 21 | 22 | fn set_network(&mut self, network: &Network) -> Result<(), BlockchainError> { 23 | trace!("set network to {}", network); 24 | self.insert_into_disk(Column::Common, b"network", network) 25 | } 26 | 27 | fn has_network(&self) -> Result { 28 | trace!("has network"); 29 | self.contains_data(Column::Common, b"network") 30 | } 31 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/merkle.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | crypto::Hash, 4 | block::TopoHeight 5 | }; 6 | use crate::core::error::BlockchainError; 7 | 8 | // Merkle Hash provider allow to give a Hash at a specific topoheight 9 | // The merkle hash only contains account balances 10 | // Because TXs and block rewards are applied on account balances 11 | // Balances are the only thing that needs to be proven 12 | // NOTE: We are based on the topoheight because of DAG reorgs as it's the main consensus 13 | #[async_trait] 14 | pub trait MerkleHashProvider { 15 | // Get the merkle hash at a specific topoheight 16 | async fn get_balances_merkle_hash_at_topoheight(&self, topoheight: TopoHeight) -> Result; 17 | 18 | // Set the merkle hash at a specific topoheight 19 | async fn set_balances_merkle_hash_at_topoheight(&mut self, topoheight: TopoHeight, merkle_proof: &Hash) -> Result<(), BlockchainError>; 20 | } -------------------------------------------------------------------------------- /xelis_common/src/prompt/art.rs: -------------------------------------------------------------------------------- 1 | pub const LOGO_ASCII: &str =r#" 2 | ^! !^ 3 | ~P@G P@P~ 4 | ~G@@@B B@@@G~ 5 | !G@@#@@#. .#@@&@@G! 6 | .7B@@P^^@@&: :&@@~~G@@B7. 7 | .?#@@P^ G@@^ ^@@B ~P@@#?. 8 | .J#@@5^ ?@@~ ~@@J ^P@@#J. 9 | :Y&@&Y: :&@7 7@@^ ^5@@&Y: 10 | ^Y&@&Y: P@J J@G :Y&@&Y^ 11 | 7@@@B^ 7@5 Y@? ^B@@@7 12 | ~P@@#?. .&P P@: .?#@@P~ 13 | ^5@@&J: PB BP :J&@@5^ 14 | :Y&@&Y: !#.#7 :Y&@&Y: 15 | :J&@@5^ .B~B. ^5@@&J: 16 | .?#@@P~ Y?Y ~P@@#?. 17 | .?B@@P~ ~Y~ ~P@@B?. 18 | 7B@@G!.?.!G@@B7 19 | !G@@BYB@@G! 20 | ~P@@@P~ 21 | ~Y~ 22 | "#; -------------------------------------------------------------------------------- /xelis_daemon/src/core/tx_cache.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | transaction::verify::ZKPCache, 4 | crypto::Hash 5 | }; 6 | 7 | use super::{ 8 | error::BlockchainError, 9 | mempool::Mempool, 10 | storage::Storage 11 | }; 12 | 13 | pub struct TxCache<'a, S: Storage> { 14 | storage: &'a S, 15 | mempool: &'a Mempool, 16 | disabled: bool, 17 | } 18 | 19 | impl<'a, S: Storage> TxCache<'a, S> { 20 | pub fn new(storage: &'a S, mempool: &'a Mempool, disabled: bool) -> Self { 21 | Self { 22 | storage, 23 | mempool, 24 | disabled 25 | } 26 | } 27 | } 28 | 29 | #[async_trait] 30 | impl<'a, S: Storage> ZKPCache for TxCache<'a, S> { 31 | async fn is_already_verified(&self, hash: &Hash) -> Result { 32 | if self.disabled { 33 | Ok(false) 34 | } else { 35 | Ok(self.mempool.contains_tx(hash) || self.storage.has_transaction(hash).await?) 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /xelis_common/src/prompt/option.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | pin::Pin, 4 | task::{Context, Poll} 5 | }; 6 | use crate::tokio::sync::mpsc::UnboundedReceiver; 7 | 8 | pub struct OptionReader { 9 | reader: Option> 10 | } 11 | 12 | impl OptionReader { 13 | pub fn new(reader: Option>) -> Self { 14 | Self { 15 | reader 16 | } 17 | } 18 | } 19 | 20 | impl Future for OptionReader { 21 | type Output = Option; 22 | 23 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 24 | match self.reader.as_mut() { 25 | Some(reader) => { 26 | match Pin::new(reader).poll_recv(cx) { 27 | Poll::Ready(Some(value)) => Poll::Ready(Some(value)), 28 | Poll::Ready(None) => Poll::Ready(None), 29 | Poll::Pending => Poll::Pending 30 | } 31 | }, 32 | None => Poll::Ready(None) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/pruned_topoheight.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::block::TopoHeight; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{sled::PRUNED_TOPOHEIGHT, PrunedTopoheightProvider, SledStorage}, 6 | }; 7 | 8 | #[async_trait] 9 | impl PrunedTopoheightProvider for SledStorage { 10 | async fn set_pruned_topoheight(&mut self, pruned_topoheight: Option) -> Result<(), BlockchainError> { 11 | self.cache_mut().pruned_topoheight = pruned_topoheight; 12 | 13 | if let Some(pruned_topoheight) = pruned_topoheight { 14 | Self::insert_into_disk(self.snapshot.as_mut(), &self.extra, PRUNED_TOPOHEIGHT, &pruned_topoheight.to_be_bytes())?; 15 | } else { 16 | Self::remove_from_disk_without_reading(self.snapshot.as_mut(), &self.extra, PRUNED_TOPOHEIGHT)?; 17 | } 18 | Ok(()) 19 | } 20 | 21 | async fn get_pruned_topoheight(&self) -> Result, BlockchainError> { 22 | Ok(self.cache.pruned_topoheight) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/mod.rs: -------------------------------------------------------------------------------- 1 | mod asset; 2 | mod blocks_at_height; 3 | mod dag_order; 4 | mod difficulty; 5 | mod pruned_topoheight; 6 | mod nonce; 7 | mod balance; 8 | mod client_protocol; 9 | mod transaction; 10 | mod block; 11 | mod blockdag; 12 | mod merkle; 13 | mod account; 14 | mod block_execution_order; 15 | mod network; 16 | mod multisig; 17 | mod snapshot; 18 | mod tips; 19 | mod contract; 20 | mod versioned; 21 | mod cache; 22 | mod state; 23 | mod asset_supply; 24 | 25 | pub use asset::*; 26 | pub use blocks_at_height::*; 27 | pub use dag_order::*; 28 | pub use difficulty::*; 29 | pub use pruned_topoheight::*; 30 | pub use nonce::*; 31 | pub use balance::*; 32 | pub use client_protocol::*; 33 | pub use transaction::*; 34 | pub use block::*; 35 | pub use blockdag::*; 36 | pub use merkle::*; 37 | pub use account::*; 38 | pub use block_execution_order::*; 39 | pub use network::*; 40 | pub use multisig::*; 41 | pub use snapshot::*; 42 | pub use tips::*; 43 | pub use contract::*; 44 | pub use versioned::*; 45 | pub use cache::*; 46 | pub use state::*; 47 | pub use asset_supply::*; -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/versioned/contract/mod.rs: -------------------------------------------------------------------------------- 1 | mod data; 2 | mod balance; 3 | mod scheduled_execution; 4 | 5 | use async_trait::async_trait; 6 | use xelis_common::block::TopoHeight; 7 | use crate::core::error::BlockchainError; 8 | 9 | pub use data::*; 10 | pub use balance::*; 11 | pub use scheduled_execution::VersionedScheduledExecutionsProvider; 12 | 13 | #[async_trait] 14 | pub trait VersionedContractProvider: VersionedContractDataProvider + VersionedContractBalanceProvider + VersionedScheduledExecutionsProvider { 15 | // delete versioned contracts at topoheight 16 | async fn delete_versioned_contracts_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 17 | 18 | // delete versioned contracts above topoheight 19 | async fn delete_versioned_contracts_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 20 | 21 | // delete versioned contracts below topoheight 22 | async fn delete_versioned_contracts_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError>; 23 | } -------------------------------------------------------------------------------- /xelis_common/build.rs: -------------------------------------------------------------------------------- 1 | // This file is executed before the build and fetch the commit hash from git 2 | // we create the build version and set it as an environment variable for the build. 3 | 4 | use std::process::Command; 5 | 6 | fn main() { 7 | let commit_hash = if let Some(hash) = option_env!("XELIS_COMMIT_HASH") { 8 | (&hash[0..8]).to_string() 9 | } else { 10 | // Run git command to get the commit hash 11 | let output = Command::new("git") 12 | .args(&["rev-parse", "--short", "HEAD"]) 13 | .output() 14 | .expect("Failed to execute git command"); 15 | 16 | // Convert the commit hash to a string 17 | String::from_utf8_lossy(&output.stdout).trim().to_string() 18 | }; 19 | 20 | // Set the result as an environment variable for the build 21 | let build_version = format!("{}-{}", env!("CARGO_PKG_VERSION"), commit_hash); 22 | println!("cargo:rerun-if-env-changed=BUILD_VERSION"); 23 | println!("cargo:BUILD_VERSION={}", build_version); 24 | println!("cargo:rustc-env=BUILD_VERSION={}", build_version); 25 | } 26 | -------------------------------------------------------------------------------- /xelis_wallet/src/precomputed_tables/mod.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, RwLock}; 2 | use xelis_common::crypto::ecdlp; 3 | 4 | #[cfg(all( 5 | target_arch = "wasm32", 6 | target_vendor = "unknown", 7 | target_os = "unknown" 8 | ))] 9 | mod web; 10 | 11 | #[cfg(all( 12 | target_arch = "wasm32", 13 | target_vendor = "unknown", 14 | target_os = "unknown" 15 | ))] 16 | pub use web::*; 17 | 18 | #[cfg(not(all( 19 | target_arch = "wasm32", 20 | target_vendor = "unknown", 21 | target_os = "unknown" 22 | )))] 23 | mod native; 24 | 25 | #[cfg(not(all( 26 | target_arch = "wasm32", 27 | target_vendor = "unknown", 28 | target_os = "unknown" 29 | )))] 30 | pub use native::*; 31 | 32 | // ECDLP Tables L1 size 33 | pub const L1_LOW: usize = 13; 34 | 35 | // ECDLP Tables L1 size 36 | pub const L1_MEDIUM: usize = 18; 37 | 38 | // ECDLP Tables L1 size 39 | // L1 at 26 is around ~330 MB of RAM and support up to 2^48 values 40 | pub const L1_FULL: usize = 26; 41 | 42 | 43 | // Allows to be used in several wallets at the same time 44 | pub type PrecomputedTablesShared = Arc>; 45 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/merkle.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::{ 4 | crypto::Hash, 5 | serializer::Serializer, 6 | block::TopoHeight 7 | }; 8 | use crate::core::{ 9 | error::{BlockchainError, DiskContext}, 10 | storage::{MerkleHashProvider, SledStorage} 11 | }; 12 | 13 | #[async_trait] 14 | impl MerkleHashProvider for SledStorage { 15 | async fn get_balances_merkle_hash_at_topoheight(&self, topoheight: TopoHeight) -> Result { 16 | trace!("get merkle hash at topoheight {}", topoheight); 17 | self.load_from_disk(&self.merkle_hashes, &topoheight.to_bytes(), DiskContext::BalancesMerkleHashAtTopoHeight(topoheight)) 18 | } 19 | 20 | async fn set_balances_merkle_hash_at_topoheight(&mut self, topoheight: TopoHeight, merkle_proof: &Hash) -> Result<(), BlockchainError> { 21 | trace!("set merkle hash {} at topoheight {}", merkle_proof, topoheight); 22 | Self::insert_into_disk(self.snapshot.as_mut(), &self.merkle_hashes, &topoheight.to_bytes(), merkle_proof.as_bytes())?; 23 | Ok(()) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_daemon/src/p2p/expirable_cache.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | time::{Duration, Instant} 4 | }; 5 | use xelis_common::{ 6 | crypto::Hash, 7 | tokio::sync::Mutex 8 | }; 9 | 10 | // Expirable cache used for requested objects (blocks, transactions) 11 | // Used to keep tracking hashes requested to prevent false unrequested errors 12 | pub struct ExpirableCache { 13 | cache: Mutex> 14 | } 15 | 16 | impl ExpirableCache { 17 | pub fn new() -> Self { 18 | Self { 19 | cache: Mutex::new(HashMap::new()) 20 | } 21 | } 22 | 23 | pub async fn insert(&self, hash: Hash) { 24 | let mut cache = self.cache.lock().await; 25 | cache.insert(hash, Instant::now()); 26 | } 27 | 28 | pub async fn remove(&self, hash: &Hash) -> bool { 29 | let mut cache = self.cache.lock().await; 30 | cache.remove(hash).is_some() 31 | } 32 | 33 | pub async fn clean(&self, timeout: Duration) { 34 | let mut cache = self.cache.lock().await; 35 | cache.retain(|_, v| { 36 | v.elapsed() < timeout 37 | }); 38 | } 39 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/merkle.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | crypto::Hash, 4 | block::TopoHeight 5 | }; 6 | use crate::core::{ 7 | error::BlockchainError, 8 | storage::{MerkleHashProvider, RocksStorage} 9 | }; 10 | 11 | // Merkle Hash provider allow to give a Hash at a specific topoheight 12 | // The merkle hash only contains account balances 13 | // Because TXs and block rewards are applied on account balances 14 | // Balances are the only thing that needs to be proven 15 | // NOTE: We are based on the topoheight because of DAG reorgs as it's the main consensus 16 | #[async_trait] 17 | impl MerkleHashProvider for RocksStorage { 18 | // Get the merkle hash at a specific topoheight 19 | async fn get_balances_merkle_hash_at_topoheight(&self, _: TopoHeight) -> Result { 20 | Err(BlockchainError::UnsupportedOperation) 21 | } 22 | 23 | // Set the merkle hash at a specific topoheight 24 | async fn set_balances_merkle_hash_at_topoheight(&mut self, _: TopoHeight, _: &Hash) -> Result<(), BlockchainError> { 25 | Err(BlockchainError::UnsupportedOperation) 26 | } 27 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lukemathwalker/cargo-chef:0.1.71-rust-1.86-slim-bookworm AS chef 2 | 3 | ENV BUILD_DIR=/tmp/xelis-build 4 | 5 | RUN apt-get update && apt-get install -y clang cmake libclang-dev 6 | RUN mkdir -p $BUILD_DIR 7 | WORKDIR $BUILD_DIR 8 | 9 | # --- 10 | 11 | FROM chef AS planner 12 | 13 | ARG app 14 | 15 | COPY . . 16 | RUN cargo chef prepare --recipe-path recipe.json --bin $app 17 | 18 | # --- 19 | 20 | FROM chef AS builder 21 | 22 | ARG app 23 | ARG commit_hash 24 | 25 | COPY --from=planner /tmp/xelis-build/recipe.json recipe.json 26 | RUN cargo chef cook --release --recipe-path recipe.json --bin $app 27 | 28 | COPY Cargo.toml Cargo.lock ./ 29 | COPY xelis_common ./xelis_common 30 | COPY $app ./$app 31 | 32 | RUN XELIS_COMMIT_HASH=${commit_hash} cargo build --release --bin $app 33 | 34 | # --- 35 | 36 | FROM gcr.io/distroless/cc-debian12 37 | 38 | ARG app 39 | 40 | ENV APP_DIR=/var/run/xelis 41 | ENV DATA_DIR=$APP_DIR/data 42 | ENV BINARY=$APP_DIR/xelis 43 | 44 | LABEL org.opencontainers.image.authors="Slixe " 45 | 46 | COPY --from=builder /tmp/xelis-build/target/release/$app $BINARY 47 | 48 | WORKDIR $DATA_DIR 49 | 50 | ENTRYPOINT ["/var/run/xelis/xelis"] 51 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/types/asset.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::{block::TopoHeight, serializer::*}; 2 | 3 | pub type AssetId = u64; 4 | 5 | pub struct Asset { 6 | // id used to prevent duplicated raw key 7 | // and save some space 8 | pub id: AssetId, 9 | // pointer to the last VersionedAssetData 10 | pub data_pointer: Option, 11 | // pointer to the last versioned supply 12 | pub supply_pointer: Option, 13 | } 14 | 15 | impl Serializer for Asset { 16 | fn read(reader: &mut Reader) -> Result { 17 | let id = AssetId::read(reader)?; 18 | let data_pointer = Option::read(reader)?; 19 | let supply_pointer = Option::read(reader)?; 20 | 21 | Ok(Self { 22 | id, 23 | data_pointer, 24 | supply_pointer 25 | }) 26 | } 27 | 28 | fn write(&self, writer: &mut Writer) { 29 | self.id.write(writer); 30 | self.data_pointer.write(writer); 31 | self.supply_pointer.write(writer); 32 | } 33 | 34 | fn size(&self) -> usize { 35 | self.id.size() 36 | + self.data_pointer.size() 37 | + self.supply_pointer.size() 38 | } 39 | } -------------------------------------------------------------------------------- /xelis_common/src/time.rs: -------------------------------------------------------------------------------- 1 | // A simple module to define the time types used in the project 2 | 3 | use std::time::{SystemTime, UNIX_EPOCH, Duration}; 4 | 5 | // Millis timestamps used to determine it using its type 6 | pub type TimestampMillis = u64; 7 | 8 | // Seconds timestamps used to determine it using its type 9 | pub type TimestampSeconds = u64; 10 | 11 | #[cfg(not(target_arch = "wasm32"))] 12 | pub type Instant = std::time::Instant; 13 | #[cfg(target_arch = "wasm32")] 14 | pub type Instant = web_time::Instant; 15 | 16 | 17 | #[inline] 18 | pub fn get_current_time() -> Duration { 19 | let start = SystemTime::now(); 20 | let time = start.duration_since(UNIX_EPOCH).expect("Incorrect time returned from get_current_time"); 21 | time 22 | } 23 | 24 | // return timestamp in seconds 25 | pub fn get_current_time_in_seconds() -> TimestampSeconds { 26 | get_current_time().as_secs() 27 | } 28 | 29 | // return timestamp in milliseconds 30 | // We cast it to u64 as we have plenty of time before it overflows 31 | // See more: https://github.com/xelis-project/xelis-blockchain/issues/18 32 | pub fn get_current_time_in_millis() -> TimestampMillis { 33 | get_current_time().as_millis() as TimestampMillis 34 | } 35 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [target.armv7-unknown-linux-gnueabihf] 2 | image = "ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:main" 3 | 4 | [target.aarch64-unknown-linux-gnu] 5 | image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main" 6 | 7 | [target.x86_64-unknown-linux-gnu] 8 | image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" 9 | # Use clang instead of GCC / G++ 10 | # See more about the bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189 11 | pre-build = [ 12 | "apt-get update", 13 | "DEBIAN_FRONTEND=noninteractive apt-get install -y wget gnupg software-properties-common", 14 | "wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor -o /usr/share/keyrings/llvm.gpg", 15 | "echo \"deb [signed-by=/usr/share/keyrings/llvm.gpg] http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main\" > /etc/apt/sources.list.d/llvm.list", 16 | "apt-get update", 17 | "apt-get install -y clang-18 libclang-18-dev", 18 | "update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100", 19 | "update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100", 20 | ] 21 | 22 | [target.x86_64-unknown-linux-gnu.env] 23 | passthrough = [ 24 | "CC=clang", 25 | "CXX=clang++", 26 | "AWS_LC_SYS_CC=clang", 27 | ] -------------------------------------------------------------------------------- /xelis_common/src/transaction/extra_data/shared_key.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | use crate::serializer::*; 4 | 5 | /// 32-bytes shared key used to encrypt/decrypt the extra data 6 | #[derive(Debug, Clone, JsonSchema)] 7 | #[schemars(with = "String")] 8 | pub struct SharedKey(pub [u8; 32]); 9 | 10 | impl Serializer for SharedKey { 11 | fn write(&self, writer: &mut Writer) { 12 | self.0.write(writer); 13 | } 14 | 15 | fn read(reader: &mut Reader) -> Result { 16 | Ok(Self(reader.read()?)) 17 | } 18 | 19 | fn size(&self) -> usize { 20 | self.0.len() 21 | } 22 | } 23 | 24 | impl Serialize for SharedKey { 25 | fn serialize(&self, serializer: S) -> Result 26 | where 27 | S: serde::Serializer, 28 | { 29 | self.0.to_hex().serialize(serializer) 30 | } 31 | } 32 | 33 | impl<'a> Deserialize<'a> for SharedKey { 34 | fn deserialize(deserializer: D) -> Result 35 | where 36 | D: serde::Deserializer<'a>, 37 | { 38 | let hex = String::deserialize(deserializer)?; 39 | SharedKey::from_hex(&hex) 40 | .map_err(serde::de::Error::custom) 41 | } 42 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/pruned_topoheight.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | rocksdb::{Column, PRUNED_TOPOHEIGHT}, 8 | PrunedTopoheightProvider, 9 | RocksStorage, 10 | } 11 | }; 12 | 13 | // This trait is used for pruning 14 | #[async_trait] 15 | impl PrunedTopoheightProvider for RocksStorage { 16 | // get the pruned topoheight 17 | async fn get_pruned_topoheight(&self) -> Result, BlockchainError> { 18 | trace!("get pruned topoheight"); 19 | self.load_optional_from_disk(Column::Common, PRUNED_TOPOHEIGHT) 20 | } 21 | 22 | // set the pruned topoheight on disk 23 | async fn set_pruned_topoheight(&mut self, pruned_topoheight: Option) -> Result<(), BlockchainError> { 24 | trace!("set pruned topoheight {:?}", pruned_topoheight); 25 | if let Some(pruned_topoheight) = pruned_topoheight { 26 | self.insert_into_disk(Column::Common, PRUNED_TOPOHEIGHT, &pruned_topoheight) 27 | } else { 28 | self.remove_from_disk(Column::Common, PRUNED_TOPOHEIGHT) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /xelis_common/src/rpc/server/websocket/http_request.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{dev::RequestHead, http::{Uri, header::HeaderMap}}; 2 | use reqwest::{Method, Version}; 3 | use actix_web::HttpRequest as ActixHttpRequest; 4 | 5 | // Copy of actix_web::HttpRequest 6 | // Its done to Copy it & save it in WebSocketSession 7 | pub struct HttpRequest { 8 | head: RequestHead 9 | } 10 | 11 | impl HttpRequest { 12 | #[inline] 13 | pub fn head(&self) -> &RequestHead { 14 | &self.head 15 | } 16 | 17 | /// Request's uri. 18 | #[inline] 19 | pub fn uri(&self) -> &Uri { 20 | &self.head().uri 21 | } 22 | 23 | /// Read the Request method. 24 | #[inline] 25 | pub fn method(&self) -> &Method { 26 | &self.head().method 27 | } 28 | 29 | /// Read the Request Version. 30 | #[inline] 31 | pub fn version(&self) -> Version { 32 | self.head().version 33 | } 34 | 35 | #[inline] 36 | /// Returns request's headers. 37 | pub fn headers(&self) -> &HeaderMap { 38 | &self.head().headers 39 | } 40 | } 41 | 42 | impl From for HttpRequest { 43 | fn from(req: ActixHttpRequest) -> Self { 44 | Self { 45 | head: req.head().clone() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /xelis_daemon/src/p2p/packet/peer_disconnected.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use xelis_common::serializer::{Serializer, Reader, ReaderError, Writer}; 4 | 5 | // this packet is sent when a peer disconnects from one of our peer 6 | // it is used to continue to track common peers between us and our peers 7 | // This is used to avoid the problem of not broadcasting a Block propagation 8 | // when we are broadcasting blocks and that we have him in common but that we 9 | // are not connected anymore to it. 10 | #[derive(Debug)] 11 | pub struct PacketPeerDisconnected { 12 | addr: SocketAddr // outgoing address 13 | } 14 | 15 | impl PacketPeerDisconnected { 16 | pub fn new(addr: SocketAddr) -> Self { 17 | Self { 18 | addr 19 | } 20 | } 21 | 22 | pub fn to_addr(self) -> SocketAddr { 23 | self.addr 24 | } 25 | } 26 | 27 | impl Serializer for PacketPeerDisconnected { 28 | fn read(reader: &mut Reader) -> Result { 29 | let addr = SocketAddr::read(reader)?; 30 | Ok(Self::new(addr)) 31 | } 32 | 33 | fn write(&self, writer: &mut Writer) { 34 | self.addr.write(writer); 35 | } 36 | 37 | fn size(&self) -> usize { 38 | self.addr.size() 39 | } 40 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "xelis_common", 6 | "xelis_wallet", 7 | "xelis_miner", 8 | "xelis_daemon" 9 | ] 10 | 11 | [workspace.dependencies] 12 | metrics = "0.24.2" 13 | anyhow = "1" 14 | thiserror = "2" 15 | log = "0.4" 16 | lru = "0.15" 17 | hex = "0.4.3" 18 | async-trait = "0.1.77" 19 | chrono = "0.4.38" 20 | chacha20poly1305 = "0.11.0-rc.0" 21 | futures = "0.3.31" 22 | lazy_static = "1.4.0" 23 | rand = "0.8.5" 24 | serde_json = "1" 25 | tokio = "1" 26 | actix-web = "4" 27 | actix-cors = "0.7" 28 | futures-util = "0.3.30" 29 | clap = { version = "4.5.2", features = ["derive"] } 30 | indexmap = { version = "2.10.0", features = ["serde"] } 31 | serde = { version = "1", features = ["derive", "rc"] } 32 | strum = { version = "0.27.1", features = ["derive"] } 33 | cfg-if = "1" 34 | # TODO: replace with new release when available 35 | tokio-tungstenite-wasm = { git = "https://github.com/TannerRogalsky/tokio-tungstenite-wasm.git", features = ["rustls-tls-webpki-roots"] } 36 | schemars = { version = "1.0.4", features = ["indexmap2"] } 37 | 38 | # cargo run --profile release-with-lto 39 | [profile.release-with-lto] 40 | inherits = "release" 41 | opt-level = 3 42 | debug-assertions = false 43 | overflow-checks = false 44 | lto = true 45 | strip = true -------------------------------------------------------------------------------- /xelis_common/src/transaction/builder/state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | account::{Nonce, CiphertextCache}, 3 | crypto::{elgamal::Ciphertext, Hash}, 4 | transaction::Reference 5 | }; 6 | 7 | use super::FeeHelper; 8 | 9 | /// If the returned balance and ct do not match, the build function will panic and/or 10 | /// the proof will be invalid. 11 | pub trait AccountState: FeeHelper { 12 | 13 | /// Used to verify if the address is on the same chain 14 | fn is_mainnet(&self) -> bool; 15 | 16 | /// Get the balance from the source 17 | fn get_account_balance(&self, asset: &Hash) -> Result; 18 | 19 | /// Block topoheight at which the transaction is being built 20 | fn get_reference(&self) -> Reference; 21 | 22 | /// Get the balance ciphertext from the source 23 | fn get_account_ciphertext(&self, asset: &Hash) -> Result; 24 | 25 | /// Update the balance and the ciphertext 26 | fn update_account_balance(&mut self, asset: &Hash, new_balance: u64, ciphertext: Ciphertext) -> Result<(), Self::Error>; 27 | 28 | /// Get the nonce of the account 29 | fn get_nonce(&self) -> Result; 30 | 31 | /// Update account nonce 32 | fn update_nonce(&mut self, new_nonce: Nonce) -> Result<(), Self::Error>; 33 | } 34 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/blockdag.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | block::{BlockHeader, TopoHeight}, 4 | crypto::Hash, 5 | immutable::Immutable 6 | }; 7 | use crate::core::{error::BlockchainError, storage::types::TopoHeightMetadata}; 8 | use super::{BlockProvider, DagOrderProvider}; 9 | 10 | #[async_trait] 11 | pub trait BlockDagProvider: DagOrderProvider + BlockProvider { 12 | // Get a block header & hash from its topoheight 13 | async fn get_block_header_at_topoheight(&self, topoheight: TopoHeight) -> Result<(Hash, Immutable), BlockchainError>; 14 | 15 | // Get the block reward from using topoheight 16 | async fn get_block_reward_at_topo_height(&self, topoheight: TopoHeight) -> Result; 17 | 18 | // Get the supply from topoheight 19 | async fn get_emitted_supply_at_topo_height(&self, topoheight: TopoHeight) -> Result; 20 | 21 | // Set the metadata for topoheight 22 | async fn get_metadata_at_topoheight(&self, topoheight: TopoHeight) -> Result; 23 | 24 | // Set the metadata for topoheight 25 | async fn set_metadata_at_topoheight(&mut self, topoheight: TopoHeight, metadata: TopoHeightMetadata) -> Result<(), BlockchainError>; 26 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/state.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | block::{Block, BlockHeader, TopoHeight}, 4 | crypto::Hash, 5 | immutable::Immutable 6 | }; 7 | 8 | use crate::core::error::BlockchainError; 9 | 10 | #[async_trait] 11 | pub trait StateProvider { 12 | // Get the top block hash of the chain 13 | async fn get_top_block_hash(&self) -> Result; 14 | 15 | // Get the top block of the chain, based on top block hash 16 | async fn get_top_block(&self) -> Result; 17 | 18 | // Get the top block header of the chain, based on top block hash 19 | async fn get_top_block_header(&self) -> Result<(Immutable, Hash), BlockchainError>; 20 | 21 | // Get the top topoheight of the chain 22 | async fn get_top_topoheight(&self) -> Result; 23 | 24 | // Set the top topoheight of the chain 25 | async fn set_top_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError>; 26 | 27 | // Get the top height of the chain 28 | async fn get_top_height(&self) -> Result; 29 | 30 | // Set the top height of the chain 31 | async fn set_top_height(&mut self, height: u64) -> Result<(), BlockchainError>; 32 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/types/block_metadata.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::{difficulty::{CumulativeDifficulty, Difficulty}, serializer::*, varuint::VarUint}; 2 | 3 | // All needed difficulty for a block 4 | pub struct BlockMetadata { 5 | pub difficulty: Difficulty, 6 | pub cumulative_difficulty: CumulativeDifficulty, 7 | pub covariance: VarUint, 8 | pub size_ema: u32, 9 | } 10 | 11 | impl Serializer for BlockMetadata { 12 | fn read(reader: &mut Reader) -> Result { 13 | let difficulty = Difficulty::read(reader)?; 14 | let cumulative_difficulty = CumulativeDifficulty::read(reader)?; 15 | let covariance = VarUint::read(reader)?; 16 | let size_ema = u32::read(reader)?; 17 | 18 | Ok(Self { 19 | difficulty, 20 | cumulative_difficulty, 21 | covariance, 22 | size_ema, 23 | }) 24 | } 25 | 26 | fn write(&self, writer: &mut Writer) { 27 | self.difficulty.write(writer); 28 | self.cumulative_difficulty.write(writer); 29 | self.covariance.write(writer); 30 | self.size_ema.write(writer); 31 | } 32 | 33 | fn size(&self) -> usize { 34 | self.difficulty.size() 35 | + self.cumulative_difficulty.size() 36 | + self.covariance.size() 37 | + self.size_ema.size() 38 | } 39 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/block_execution_order.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::crypto::Hash; 3 | use crate::core::error::BlockchainError; 4 | 5 | // This provider tracks the order in which blocks are added in the chain. 6 | // This is independant of the DAG order and is used for debug purposes. 7 | #[async_trait] 8 | pub trait BlockExecutionOrderProvider { 9 | // Get the blocks execution order 10 | async fn get_blocks_execution_order<'a>(&'a self) -> Result> + 'a, BlockchainError>; 11 | 12 | // Get the position of a block in the execution order 13 | async fn get_block_position_in_order(&self, hash: &Hash) -> Result; 14 | 15 | // Check if a block is in the execution order 16 | async fn has_block_position_in_order(&self, hash: &Hash) -> Result; 17 | 18 | // Add a block to the execution order 19 | async fn add_block_execution_to_order(&mut self, hash: &Hash) -> Result<(), BlockchainError>; 20 | 21 | // Get the number of blocks executed 22 | async fn get_blocks_execution_count(&self) -> Result; 23 | 24 | // Swap the position of two blocks in the execution order 25 | async fn swap_blocks_executions_positions(&mut self, left: &Hash, right: &Hash) -> Result<(), BlockchainError>; 26 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/dag_order.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | crypto::Hash, 4 | block::TopoHeight, 5 | }; 6 | use crate::core::error::BlockchainError; 7 | 8 | // This trait is used for find_tip_work_score to provide topoheight of each blocks 9 | #[async_trait] 10 | pub trait DagOrderProvider { 11 | // Get the topoheight from a block hash 12 | async fn get_topo_height_for_hash(&self, hash: &Hash) -> Result; 13 | 14 | // Set the topoheight for a block hash 15 | async fn set_topo_height_for_block(&mut self, hash: &Hash, topoheight: TopoHeight) -> Result<(), BlockchainError>; 16 | 17 | // Is block hash ordered in DAG? 18 | // (block hash must be assigned a topoheight) 19 | async fn is_block_topological_ordered(&self, hash: &Hash) -> Result; 20 | 21 | // Get the current block hash at specified topoheight 22 | async fn get_hash_at_topo_height(&self, topoheight: TopoHeight) -> Result; 23 | 24 | // Is topoheight available 25 | async fn has_hash_at_topoheight(&self, topoheight: TopoHeight) -> Result; 26 | 27 | // Fetch all the blocks orphaned in the DB 28 | async fn get_orphaned_blocks<'a>(&'a self) -> Result> + 'a, BlockchainError>; 29 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/types/topoheight_metadata.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::serializer::*; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | pub struct TopoHeightMetadata { 5 | // block reward 6 | pub block_reward: u64, 7 | pub emitted_supply: u64, 8 | // total fee paid to the miner 9 | pub total_fees: u64, 10 | // total fee burned at this topoheight 11 | pub total_fees_burned: u64, 12 | } 13 | 14 | impl Serializer for TopoHeightMetadata { 15 | fn read(reader: &mut Reader) -> Result { 16 | let rewards = reader.read_u64()?; 17 | let emitted_supply = reader.read_u64()?; 18 | let total_fees = reader.read_u64()?; 19 | let total_fees_burned = reader.read_u64()?; 20 | 21 | Ok(Self { 22 | block_reward: rewards, 23 | emitted_supply, 24 | total_fees, 25 | total_fees_burned, 26 | }) 27 | } 28 | 29 | fn write(&self, writer: &mut Writer) { 30 | self.block_reward.write(writer); 31 | self.emitted_supply.write(writer); 32 | self.total_fees.write(writer); 33 | self.total_fees_burned.write(writer); 34 | } 35 | 36 | fn size(&self) -> usize { 37 | self.block_reward.size() 38 | + self.emitted_supply.size() 39 | + self.total_fees.size() 40 | + self.total_fees_burned.size() 41 | } 42 | } -------------------------------------------------------------------------------- /xelis_common/src/block/mod.rs: -------------------------------------------------------------------------------- 1 | mod header; 2 | mod block; 3 | mod miner; 4 | mod version; 5 | 6 | pub use header::BlockHeader; 7 | pub use block::Block; 8 | pub use miner::{MinerWork, Worker, Algorithm}; 9 | pub use version::BlockVersion; 10 | 11 | use crate::crypto::{Hash, HASH_SIZE}; 12 | 13 | // Topoheight is the height of the block in the blockdag 14 | // It is a unique identifier for a block and can be changed during the unstable height 15 | // due to a DAG reorganization 16 | pub type TopoHeight = u64; 17 | 18 | pub const EXTRA_NONCE_SIZE: usize = 32; 19 | pub const HEADER_WORK_SIZE: usize = 73; 20 | pub const BLOCK_WORK_SIZE: usize = 112; // 32 + 8 + 8 + 32 + 32 = 112 21 | 22 | // Get combined hash for tips 23 | // This is used to get a hash that is unique for a set of tips 24 | pub fn get_combined_hash_for_tips<'a, H: AsRef, I: Iterator>(tips: I) -> Hash { 25 | let mut bytes = [0u8; HASH_SIZE]; 26 | for tip in tips { 27 | for i in 0..HASH_SIZE { 28 | bytes[i] ^= tip.as_ref().as_bytes()[i]; 29 | } 30 | } 31 | Hash::new(bytes) 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use crate::crypto::Hash; 37 | 38 | #[test] 39 | fn test_one_hash() { 40 | let hash = Hash::new([255u8; 32]); 41 | let combined_hash = super::get_combined_hash_for_tips(std::iter::once(&hash)); 42 | assert_eq!(combined_hash, hash); 43 | } 44 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/transaction.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use futures::Stream; 3 | use xelis_common::{ 4 | crypto::Hash, 5 | immutable::Immutable, 6 | transaction::Transaction 7 | }; 8 | use crate::core::error::BlockchainError; 9 | 10 | #[async_trait] 11 | pub trait TransactionProvider { 12 | // Get the transaction using its hash 13 | async fn get_transaction(&self, hash: &Hash) -> Result, BlockchainError>; 14 | 15 | // Get the transaction size using its hash 16 | async fn get_transaction_size(&self, hash: &Hash) -> Result; 17 | 18 | // Count the number of transactions stored 19 | async fn count_transactions(&self) -> Result; 20 | 21 | // Get all the unexecuted transactions 22 | async fn get_unexecuted_transactions<'a>(&'a self) -> Result> + 'a, BlockchainError>; 23 | 24 | // Check if the transaction exists 25 | async fn has_transaction(&self, hash: &Hash) -> Result; 26 | 27 | // Store a new transaction 28 | async fn add_transaction(&mut self, hash: &Hash, transaction: &Transaction) -> Result<(), BlockchainError>; 29 | 30 | // Delete a transaction from the storage using its hash 31 | async fn delete_transaction(&mut self, hash: &Hash) -> Result, BlockchainError>; 32 | } -------------------------------------------------------------------------------- /xelis_common/src/rpc/server/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod websocket; 2 | 3 | use actix_web::{ 4 | HttpResponse, 5 | web::{self, Data, Payload}, 6 | Responder, 7 | HttpRequest 8 | }; 9 | 10 | use super::{RPCHandler, RpcResponseError}; 11 | use self::websocket::{WebSocketServerShared, WebSocketHandler}; 12 | 13 | // trait to retrieve easily a JSON RPC handler for registered route 14 | pub trait RPCServerHandler { 15 | fn get_rpc_handler(&self) -> &RPCHandler; 16 | } 17 | 18 | // JSON RPC handler endpoint 19 | pub async fn json_rpc(server: Data, body: web::Bytes) -> Result 20 | where 21 | T: Send + Sync + Clone + 'static, 22 | H: RPCServerHandler 23 | { 24 | let result = server.get_rpc_handler().handle_request(&body).await?; 25 | Ok(HttpResponse::Ok().json(result)) 26 | } 27 | 28 | // trait to retrieve easily a websocket handler for registered route 29 | pub trait WebSocketServerHandler { 30 | fn get_websocket(&self) -> &WebSocketServerShared; 31 | } 32 | 33 | // WebSocket JSON RPC handler endpoint 34 | pub async fn websocket(server: Data, request: HttpRequest, body: Payload) -> Result 35 | where 36 | H: WebSocketHandler + 'static, 37 | S: WebSocketServerHandler 38 | { 39 | let response = server.get_websocket().handle_connection(request, body).await?; 40 | Ok(response) 41 | } -------------------------------------------------------------------------------- /xelis_common/src/transaction/reference.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use schemars::JsonSchema; 3 | use serde::{Serialize, Deserialize}; 4 | use crate::{ 5 | crypto::Hash, 6 | block::TopoHeight, 7 | serializer::{ 8 | Reader, 9 | ReaderError, 10 | Serializer, 11 | Writer 12 | } 13 | }; 14 | 15 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 16 | pub struct Reference { 17 | pub hash: Hash, 18 | pub topoheight: TopoHeight, 19 | } 20 | 21 | impl fmt::Display for Reference { 22 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | write!(f, "Reference[hash: {}, topoheight: {}]", self.hash, self.topoheight) 24 | } 25 | } 26 | 27 | impl PartialEq for Reference { 28 | fn eq(&self, other: &Self) -> bool { 29 | self.hash == other.hash && self.topoheight == other.topoheight 30 | } 31 | } 32 | 33 | impl Serializer for Reference { 34 | fn write(&self, writer: &mut Writer) { 35 | self.hash.write(writer); 36 | self.topoheight.write(writer); 37 | } 38 | 39 | fn read(reader: &mut Reader) -> Result { 40 | let hash = Hash::read(reader)?; 41 | let topoheight = Reader::read(reader)?; 42 | Ok(Reference { 43 | hash, 44 | topoheight 45 | }) 46 | } 47 | 48 | fn size(&self) -> usize { 49 | self.hash.size() + self.topoheight.size() 50 | } 51 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/nonce.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{SledStorage, VersionedNonceProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedNonceProvider for SledStorage { 11 | async fn delete_versioned_nonces_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 12 | trace!("delete versioned nonces at topoheight {}", topoheight); 13 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.nonces, &self.versioned_nonces, topoheight) 14 | } 15 | 16 | async fn delete_versioned_nonces_above_topoheight(&mut self, topoheight: u64) -> Result<(), BlockchainError> { 17 | trace!("delete versioned nonces above topoheight {}!", topoheight); 18 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.nonces, &self.versioned_nonces, topoheight, DiskContext::VersionedNonce) 19 | } 20 | 21 | async fn delete_versioned_nonces_below_topoheight(&mut self, topoheight: u64, keep_last: bool) -> Result<(), BlockchainError> { 22 | trace!("delete versioned nonces below topoheight {}!", topoheight); 23 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.nonces, &self.versioned_nonces, topoheight, keep_last, DiskContext::VersionedNonce) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_common/src/serializer/raw.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use super::{Reader, Serializer}; 4 | 5 | pub struct RawBytes(Vec); 6 | 7 | impl RawBytes { 8 | pub fn new(bytes: Vec) -> Self { 9 | Self(bytes) 10 | } 11 | 12 | pub fn as_bytes(&self) -> &[u8] { 13 | &self.0 14 | } 15 | } 16 | 17 | impl Serializer for RawBytes { 18 | fn write(&self, writer: &mut super::Writer) { 19 | writer.write_bytes(&self.0); 20 | } 21 | 22 | fn read(reader: &mut Reader) -> Result { 23 | let bytes = reader.bytes(); 24 | Ok(Self(bytes.to_vec())) 25 | } 26 | 27 | fn to_bytes(&self) -> Vec { 28 | self.0.clone() 29 | } 30 | 31 | fn size(&self) -> usize { 32 | self.0.len() 33 | } 34 | } 35 | 36 | impl Deref for RawBytes { 37 | type Target = [u8]; 38 | 39 | fn deref(&self) -> &Self::Target { 40 | &self.0 41 | } 42 | } 43 | 44 | impl AsRef<[u8]> for RawBytes { 45 | fn as_ref(&self) -> &[u8] { 46 | &self.0 47 | } 48 | } 49 | 50 | impl DerefMut for RawBytes { 51 | fn deref_mut(&mut self) -> &mut Self::Target { 52 | &mut self.0 53 | } 54 | } 55 | 56 | impl From> for RawBytes { 57 | fn from(bytes: Vec) -> Self { 58 | Self::new(bytes) 59 | } 60 | } 61 | 62 | impl From for Vec { 63 | fn from(raw: RawBytes) -> Self { 64 | raw.0 65 | } 66 | } -------------------------------------------------------------------------------- /xelis_common/src/contract/random.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use blake3::OutputReader; 3 | 4 | use crate::{block::TopoHeight, crypto::Hash}; 5 | 6 | // Deterministic random number generator 7 | // This is used to generate random numbers in a deterministic way 8 | // It is based on the blake3 hash function 9 | #[derive(Debug, Clone)] 10 | pub struct DeterministicRandom { 11 | // Key for the random number generator 12 | // Blake3 support up to 2^64 - 1 bytes 13 | // We need to check pos is less than 2^64 - 1 14 | reader: OutputReader, 15 | } 16 | 17 | impl DeterministicRandom { 18 | pub fn new(contract: &Hash, block: &Hash, topoheight: TopoHeight, transaction: &Hash) -> Self { 19 | let mut hasher = blake3::Hasher::new(); 20 | 21 | hasher 22 | .update(contract.as_bytes()) 23 | .update(block.as_bytes()) 24 | .update(&topoheight.to_be_bytes()) 25 | .update(transaction.as_bytes()); 26 | 27 | Self { 28 | reader: hasher.finalize_xof(), 29 | } 30 | } 31 | 32 | pub fn fill(&mut self, buffer: &mut [u8]) -> Result<(), anyhow::Error> { 33 | let pos = self.reader.position() 34 | .checked_add(buffer.len() as u64) 35 | .context("overflow")?; 36 | 37 | if pos >= u64::MAX - 1 { 38 | return Err(anyhow::anyhow!("2^64 - 1 bytes reached")); 39 | } 40 | 41 | self.reader.fill(buffer); 42 | 43 | Ok(()) 44 | } 45 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/asset.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{SledStorage, VersionedAssetProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedAssetProvider for SledStorage { 11 | async fn delete_versioned_assets_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 12 | trace!("delete versioned assets at topoheight {}", topoheight); 13 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.assets, &self.versioned_assets, topoheight) 14 | } 15 | 16 | async fn delete_versioned_assets_above_topoheight(&mut self, topoheight: u64) -> Result<(), BlockchainError> { 17 | trace!("delete versioned assets above topoheight {}", topoheight); 18 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.assets, &self.versioned_assets, topoheight, DiskContext::Asset) 19 | } 20 | 21 | // delete versioned assets below topoheight 22 | async fn delete_versioned_assets_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 23 | trace!("delete versioned assets below topoheight {}", topoheight); 24 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.assets, &self.versioned_assets, topoheight, keep_last, DiskContext::Asset) 25 | } 26 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2025, XELIS 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/multisig.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{SledStorage, VersionedMultiSigProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedMultiSigProvider for SledStorage { 11 | async fn delete_versioned_multisigs_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 12 | trace!("delete versioned nonces at topoheight {}", topoheight); 13 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.multisig, &self.versioned_multisigs, topoheight) 14 | } 15 | 16 | async fn delete_versioned_multisigs_above_topoheight(&mut self, topoheight: u64) -> Result<(), BlockchainError> { 17 | trace!("delete versioned multisigs above topoheight {}!", topoheight); 18 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.multisig, &self.versioned_multisigs, topoheight, DiskContext::VersionedMultisig) 19 | } 20 | 21 | async fn delete_versioned_multisigs_below_topoheight(&mut self, topoheight: u64, keep_last: bool) -> Result<(), BlockchainError> { 22 | trace!("delete versioned multisigs below topoheight {}!", topoheight); 23 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.multisig, &self.versioned_multisigs, topoheight, keep_last, DiskContext::VersionedMultisig) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/versioned/contract/data.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | rocksdb::Column, 8 | RocksStorage, 9 | VersionedContractDataProvider 10 | } 11 | }; 12 | 13 | #[async_trait] 14 | impl VersionedContractDataProvider for RocksStorage { 15 | async fn delete_versioned_contract_data_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 16 | trace!("delete versioned contract data at topoheight {}", topoheight); 17 | self.delete_versioned_at_topoheight(Column::ContractsData, Column::VersionedContractsData, topoheight) 18 | } 19 | 20 | async fn delete_versioned_contract_data_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 21 | trace!("delete versioned contract data above topoheight {}", topoheight); 22 | self.delete_versioned_above_topoheight(Column::ContractsData, Column::VersionedContractsData, topoheight) 23 | } 24 | 25 | async fn delete_versioned_contract_data_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 26 | trace!("delete versioned contract data below topoheight {}", topoheight); 27 | self.delete_versioned_below_topoheight_default(Column::ContractsData, Column::VersionedContractsData, topoheight, keep_last) 28 | } 29 | } -------------------------------------------------------------------------------- /xelis_wallet/src/api/server/mod.rs: -------------------------------------------------------------------------------- 1 | mod rpc_server; 2 | mod xswd_server; 3 | 4 | pub use rpc_server::{ 5 | WalletRpcServer, 6 | WalletRpcServerShared, 7 | AuthConfig 8 | }; 9 | 10 | use serde::Serialize; 11 | use serde_json::json; 12 | use xelis_common::{ 13 | api::wallet::NotifyEvent, 14 | rpc::server::WebSocketServerHandler 15 | }; 16 | pub use xswd_server::{ 17 | XSWDServer, 18 | XSWDWebSocketHandler 19 | }; 20 | 21 | use crate::api::XSWDHandler; 22 | 23 | 24 | pub enum APIServer 25 | where 26 | W: Clone + Send + Sync + XSWDHandler + 'static 27 | { 28 | RPCServer(WalletRpcServerShared), 29 | XSWD(XSWDServer) 30 | } 31 | 32 | impl APIServer 33 | where 34 | W: Clone + Send + Sync + XSWDHandler + 'static 35 | { 36 | pub async fn notify_event(&self, event: &NotifyEvent, value: &V) { 37 | let json = json!(value); 38 | match self { 39 | APIServer::RPCServer(server) => { 40 | server.get_websocket().get_handler().notify(event, json).await; 41 | }, 42 | APIServer::XSWD(xswd) => { 43 | xswd.get_handler().notify(event, json).await; 44 | } 45 | } 46 | } 47 | 48 | pub async fn stop(self) { 49 | match self { 50 | APIServer::RPCServer(server) => { 51 | server.stop().await; 52 | }, 53 | APIServer::XSWD(xswd) => { 54 | xswd.stop().await; 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /xelis_common/src/tokio/executor.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::VecDeque, future::Future, pin::Pin, task::{Context, Poll}}; 2 | 3 | use futures::Stream; 4 | use pin_project_lite::pin_project; 5 | 6 | pin_project! { 7 | pub struct Executor { 8 | futures: VecDeque>> 9 | } 10 | } 11 | 12 | impl Executor { 13 | pub fn new() -> Self { 14 | Self { 15 | futures: VecDeque::new() 16 | } 17 | } 18 | 19 | pub fn push_front(&mut self, future: F) { 20 | self.futures.push_front(Box::pin(future)); 21 | } 22 | 23 | pub fn push_back(&mut self, future: F) { 24 | self.futures.push_back(Box::pin(future)); 25 | } 26 | 27 | pub fn len(&self) -> usize { 28 | self.futures.len() 29 | } 30 | 31 | pub fn is_empty(&self) -> bool { 32 | self.futures.is_empty() 33 | } 34 | } 35 | 36 | impl Stream for Executor { 37 | type Item = F::Output; 38 | 39 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 40 | let this = self.project(); 41 | 42 | if let Some(fut) = this.futures.front_mut() { 43 | match fut.as_mut().poll(cx) { 44 | Poll::Ready(output) => { 45 | this.futures.pop_front(); 46 | Poll::Ready(Some(output)) 47 | }, 48 | Poll::Pending => Poll::Pending, 49 | } 50 | } else { 51 | Poll::Ready(None) 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/types/account.rs: -------------------------------------------------------------------------------- 1 | use xelis_common::{block::TopoHeight, serializer::*}; 2 | 3 | pub type AccountId = u64; 4 | 5 | pub struct Account { 6 | // id used to prevent duplicated raw key 7 | // and save some space 8 | pub id: AccountId, 9 | // At which topoheight the account has been seen 10 | // for the first time 11 | pub registered_at: Option, 12 | // pointer to the last versioned nonce 13 | pub nonce_pointer: Option, 14 | // pointer to the last versioned multisig 15 | pub multisig_pointer: Option, 16 | } 17 | 18 | impl Serializer for Account { 19 | fn read(reader: &mut Reader) -> Result { 20 | let id = AccountId::read(reader)?; 21 | let registered_at = Option::read(reader)?; 22 | let nonce_pointer = Option::read(reader)?; 23 | let multisig_pointer = Option::read(reader)?; 24 | 25 | Ok(Self { 26 | id, 27 | registered_at, 28 | nonce_pointer, 29 | multisig_pointer 30 | }) 31 | } 32 | 33 | fn write(&self, writer: &mut Writer) { 34 | self.id.write(writer); 35 | self.registered_at.write(writer); 36 | self.nonce_pointer.write(writer); 37 | self.multisig_pointer.write(writer); 38 | } 39 | 40 | fn size(&self) -> usize { 41 | self.id.size() 42 | + self.registered_at.size() 43 | + self.nonce_pointer.size() 44 | + self.multisig_pointer.size() 45 | } 46 | } -------------------------------------------------------------------------------- /xelis_common/src/contract/source.rs: -------------------------------------------------------------------------------- 1 | 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{crypto::{Hash, PublicKey}, serializer::*}; 6 | 7 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] 8 | #[serde(rename_all = "snake_case", tag = "type", content = "value")] 9 | pub enum Source { 10 | Contract(Hash), 11 | Account(PublicKey), 12 | } 13 | 14 | impl Serializer for Source { 15 | fn write(&self, writer: &mut Writer) { 16 | match self { 17 | Source::Contract(hash) => { 18 | writer.write_u8(0); 19 | hash.write(writer); 20 | } 21 | Source::Account(account) => { 22 | writer.write_u8(1); 23 | account.write(writer); 24 | } 25 | } 26 | } 27 | 28 | fn read(reader: &mut Reader) -> Result { 29 | let tag = reader.read_u8()?; 30 | match tag { 31 | 0 => { 32 | let hash = Hash::read(reader)?; 33 | Ok(Source::Contract(hash)) 34 | } 35 | 1 => { 36 | let account = PublicKey::read(reader)?; 37 | Ok(Source::Account(account)) 38 | }, 39 | _ => Err(ReaderError::InvalidValue), 40 | } 41 | } 42 | 43 | fn size(&self) -> usize { 44 | match self { 45 | Source::Contract(hash) => 1 + hash.size(), 46 | Source::Account(account) => 1 + account.size(), 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/versioned/contract/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | rocksdb::Column, 8 | RocksStorage, 9 | VersionedContractBalanceProvider 10 | } 11 | }; 12 | 13 | #[async_trait] 14 | impl VersionedContractBalanceProvider for RocksStorage { 15 | async fn delete_versioned_contract_balances_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 16 | trace!("delete versioned contract balances at topoheight {}", topoheight); 17 | self.delete_versioned_at_topoheight(Column::ContractsBalances, Column::VersionedContractsBalances, topoheight) 18 | } 19 | 20 | async fn delete_versioned_contract_balances_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 21 | trace!("delete versioned contract balances above topoheight {}", topoheight); 22 | self.delete_versioned_above_topoheight(Column::ContractsBalances, Column::VersionedContractsBalances, topoheight) 23 | } 24 | 25 | async fn delete_versioned_contract_balances_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 26 | trace!("delete versioned contract balances below topoheight {}", topoheight); 27 | self.delete_versioned_below_topoheight_default(Column::ContractsBalances, Column::VersionedContractsBalances, topoheight, keep_last) 28 | } 29 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/contract/mod.rs: -------------------------------------------------------------------------------- 1 | mod data; 2 | mod balance; 3 | mod scheduled_execution; 4 | 5 | use async_trait::async_trait; 6 | use log::trace; 7 | use xelis_common::block::TopoHeight; 8 | use crate::core::{ 9 | error::{BlockchainError, DiskContext}, 10 | storage::{SledStorage, VersionedContractProvider} 11 | }; 12 | 13 | #[async_trait] 14 | impl VersionedContractProvider for SledStorage { 15 | async fn delete_versioned_contracts_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 16 | trace!("delete versioned contracts at topoheight {}", topoheight); 17 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.contracts, &self.versioned_contracts, topoheight) 18 | } 19 | 20 | async fn delete_versioned_contracts_above_topoheight(&mut self, topoheight: u64) -> Result<(), BlockchainError> { 21 | trace!("delete versioned contracts above topoheight {}!", topoheight); 22 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.contracts, &self.versioned_contracts, topoheight, DiskContext::VersionedContract) 23 | } 24 | 25 | async fn delete_versioned_contracts_below_topoheight(&mut self, topoheight: u64, keep_last: bool) -> Result<(), BlockchainError> { 26 | trace!("delete versioned contracts below topoheight {}!", topoheight); 27 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.contracts, &self.versioned_contracts, topoheight, keep_last, DiskContext::VersionedContract) 28 | } 29 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/asset_supply.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{SledStorage, VersionedAssetsCirculatingSupplyProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedAssetsCirculatingSupplyProvider for SledStorage { 11 | async fn delete_versioned_assets_supply_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 12 | trace!("delete versioned assets supply at topoheight {}", topoheight); 13 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.assets_supply, &self.versioned_assets_supply, topoheight) 14 | } 15 | 16 | async fn delete_versioned_assets_supply_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 17 | trace!("delete versioned assets supply above topoheight {}", topoheight); 18 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.assets_supply, &self.versioned_assets_supply, topoheight, DiskContext::AssetSupply) 19 | } 20 | 21 | async fn delete_versioned_assets_supply_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 22 | trace!("delete versioned assets supply below topoheight {}", topoheight); 23 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.assets_supply, &self.versioned_assets_supply, topoheight, keep_last, DiskContext::AssetSupply) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/contract/data.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{SledStorage, VersionedContractDataProvider} 7 | }; 8 | 9 | #[async_trait] 10 | impl VersionedContractDataProvider for SledStorage { 11 | async fn delete_versioned_contract_data_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 12 | trace!("delete versioned contracts data at topoheight {}", topoheight); 13 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.contracts_data, &self.versioned_contracts_data, topoheight) 14 | } 15 | 16 | async fn delete_versioned_contract_data_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 17 | trace!("delete versioned contracts data above topoheight {}", topoheight); 18 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.contracts_data, &self.versioned_contracts_data, topoheight, DiskContext::VersionedContractData) 19 | } 20 | 21 | async fn delete_versioned_contract_data_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 22 | trace!("delete versioned contracts data below topoheight {}", topoheight); 23 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.contracts_data, &self.versioned_contracts_data, topoheight, keep_last, DiskContext::VersionedContractData) 24 | } 25 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/versioned/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | rocksdb::Column, 8 | RocksStorage, 9 | VersionedBalanceProvider 10 | } 11 | }; 12 | 13 | #[async_trait] 14 | impl VersionedBalanceProvider for RocksStorage { 15 | // delete versioned balances at topoheight 16 | async fn delete_versioned_balances_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 17 | trace!("delete versioned balances at topoheight {}", topoheight); 18 | self.delete_versioned_at_topoheight(Column::Balances, Column::VersionedBalances, topoheight) 19 | } 20 | 21 | // delete versioned balances above topoheight 22 | async fn delete_versioned_balances_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 23 | trace!("delete versioned balances above topoheight {}", topoheight); 24 | self.delete_versioned_above_topoheight(Column::Balances, Column::VersionedBalances, topoheight) 25 | } 26 | 27 | // delete versioned balances below topoheight 28 | // Difference is, if we have 29 | async fn delete_versioned_balances_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 30 | trace!("delete versioned balances below topoheight {}", topoheight); 31 | self.delete_versioned_below_topoheight_default(Column::Balances, Column::VersionedBalances, topoheight, keep_last) 32 | } 33 | } -------------------------------------------------------------------------------- /xelis_common/src/transaction/payload/contract/deposits.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use indexmap::IndexMap; 4 | use schemars::JsonSchema; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | use crate::{crypto::Hash, serializer::*}; 8 | use super::ContractDeposit; 9 | 10 | 11 | #[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)] 12 | pub struct Deposits(pub IndexMap); 13 | 14 | impl Deref for Deposits { 15 | type Target = IndexMap; 16 | 17 | fn deref(&self) -> &Self::Target { 18 | &self.0 19 | } 20 | } 21 | 22 | impl DerefMut for Deposits { 23 | fn deref_mut(&mut self) -> &mut Self::Target { 24 | &mut self.0 25 | } 26 | } 27 | 28 | impl Serializer for Deposits { 29 | fn write(&self, writer: &mut Writer) { 30 | writer.write_u8(self.0.len() as u8); 31 | for (key, value) in self.0.iter() { 32 | key.write(writer); 33 | value.write(writer); 34 | } 35 | } 36 | 37 | fn read(reader: &mut Reader) -> Result { 38 | let size = reader.read_u8()?; 39 | let mut deposits = IndexMap::with_capacity(size as usize); 40 | for _ in 0..size { 41 | let k = Hash::read(reader)?; 42 | let v = ContractDeposit::read(reader)?; 43 | deposits.insert(k, v); 44 | } 45 | 46 | Ok(Self(deposits)) 47 | } 48 | 49 | fn size(&self) -> usize { 50 | // 1 is for the deposit byte size 51 | 1 + self.0.iter() 52 | .map(|(asset, deposit)| asset.size() + deposit.size()) 53 | .sum::() 54 | } 55 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/versioned/contract/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::block::TopoHeight; 4 | use crate::core::{ 5 | error::{BlockchainError, DiskContext}, 6 | storage::{ 7 | SledStorage, 8 | VersionedContractBalanceProvider 9 | } 10 | }; 11 | 12 | #[async_trait] 13 | impl VersionedContractBalanceProvider for SledStorage { 14 | async fn delete_versioned_contract_balances_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 15 | trace!("delete versioned contracts balances at topoheight {}", topoheight); 16 | Self::delete_versioned_tree_at_topoheight(&mut self.snapshot, &self.contracts_balances, &self.versioned_contracts_balances, topoheight) 17 | } 18 | 19 | async fn delete_versioned_contract_balances_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 20 | trace!("delete versioned contracts balances above topoheight {}", topoheight); 21 | Self::delete_versioned_tree_above_topoheight(&mut self.snapshot, &self.contracts_balances, &self.versioned_contracts_balances, topoheight, DiskContext::ContractBalance) 22 | } 23 | 24 | async fn delete_versioned_contract_balances_below_topoheight(&mut self, topoheight: TopoHeight, keep_last: bool) -> Result<(), BlockchainError> { 25 | trace!("delete versioned contracts balances below topoheight {}", topoheight); 26 | Self::delete_versioned_tree_below_topoheight(&mut self.snapshot, &self.contracts_balances, &self.versioned_contracts_balances, topoheight, keep_last, DiskContext::ContractBalance) 27 | } 28 | } -------------------------------------------------------------------------------- /xelis_wallet/src/api/xswd/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | use xelis_common::rpc::InternalRpcError; 3 | 4 | #[derive(Error, Debug, Clone, Copy)] 5 | pub enum XSWDError { 6 | #[error("semaphore error")] 7 | SemaphoreError, 8 | #[error("Permission denied")] 9 | PermissionDenied, 10 | #[error("Permission invalid: method wasn't mentionned during handshake")] 11 | PermissionInvalid, 12 | #[error("Application not found")] 13 | ApplicationNotFound, 14 | #[error("Invalid application data")] 15 | InvalidApplicationData, 16 | #[error("Invalid application ID")] 17 | InvalidApplicationId, 18 | #[error("Application ID already used")] 19 | ApplicationIdAlreadyUsed, 20 | #[error("Invalid hexadecimal for application ID")] 21 | InvalidHexaApplicationId, 22 | #[error("Application name is too long")] 23 | ApplicationNameTooLong, 24 | #[error("Application description is too long")] 25 | ApplicationDescriptionTooLong, 26 | #[error("Invalid URL format")] 27 | InvalidURLFormat, 28 | #[error("Invalid origin")] 29 | InvalidOrigin, 30 | #[error("Too many permissions")] 31 | TooManyPermissions, 32 | #[error("Unknown method requested in permissions list")] 33 | UnknownMethodInPermissionsList, 34 | #[error("Application permissions are not signed")] 35 | ApplicationPermissionsNotSigned, 36 | #[error("Invalid signature for application data")] 37 | InvalidSignatureForApplicationData 38 | } 39 | 40 | impl From for InternalRpcError { 41 | fn from(e: XSWDError) -> Self { 42 | let err = e.into(); 43 | InternalRpcError::AnyError(err) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/contract/contract_logs.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::{ 4 | contract::ContractLog, 5 | crypto::Hash 6 | }; 7 | use crate::core::{ 8 | error::BlockchainError, 9 | storage::{rocksdb::Column, ContractLogsProvider, RocksStorage} 10 | }; 11 | 12 | #[async_trait] 13 | impl ContractLogsProvider for RocksStorage { 14 | // Verify if the contract logs for a transaction exist 15 | async fn has_contract_logs_for_caller(&self, tx_hash: &Hash) -> Result { 16 | trace!("has contract logs for caller {}", tx_hash); 17 | self.contains_data(Column::ContractLogs, tx_hash) 18 | } 19 | 20 | // Get the contract logs for a transaction 21 | async fn get_contract_logs_for_caller(&self, tx_hash: &Hash) -> Result, BlockchainError> { 22 | trace!("get contract logs for caller {}", tx_hash); 23 | self.load_from_disk(Column::ContractLogs, tx_hash) 24 | } 25 | 26 | // Set the contract logs for a transaction 27 | async fn set_contract_logs_for_caller(&mut self, tx_hash: &Hash, contract_output: &Vec) -> Result<(), BlockchainError> { 28 | trace!("set contract logs for caller {}", tx_hash); 29 | self.insert_into_disk(Column::ContractLogs, tx_hash, contract_output) 30 | } 31 | 32 | // Delete the contract logs for a transaction 33 | async fn delete_contract_logs_for_caller(&mut self, tx_hash: &Hash) -> Result<(), BlockchainError> { 34 | trace!("delete contract logs for caller {}", tx_hash); 35 | self.remove_from_disk(Column::ContractLogs, tx_hash) 36 | } 37 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/contract/contract_logs.rs: -------------------------------------------------------------------------------- 1 | use log::trace; 2 | use async_trait::async_trait; 3 | use xelis_common::{ 4 | contract::ContractLog, 5 | crypto::Hash, 6 | serializer::Serializer 7 | }; 8 | use crate::core::{ 9 | error::{BlockchainError, DiskContext}, 10 | storage::{ContractLogsProvider, SledStorage} 11 | }; 12 | 13 | #[async_trait] 14 | impl ContractLogsProvider for SledStorage { 15 | async fn has_contract_logs_for_caller(&self, tx_hash: &Hash) -> Result { 16 | trace!("has contract logs exist for caller {}", tx_hash); 17 | self.contains_data(&self.contracts_logs, tx_hash.as_bytes()) 18 | } 19 | 20 | async fn get_contract_logs_for_caller(&self, tx_hash: &Hash) -> Result, BlockchainError> { 21 | trace!("get contract logs for caller {}", tx_hash); 22 | self.load_from_disk(&self.contracts_logs, tx_hash.as_bytes(), DiskContext::ContractOutputs) 23 | } 24 | 25 | async fn set_contract_logs_for_caller(&mut self, tx_hash: &Hash, contract_output: &Vec) -> Result<(), BlockchainError> { 26 | trace!("set contract logs for caller {}", tx_hash); 27 | Self::insert_into_disk(self.snapshot.as_mut(), &self.contracts_logs, tx_hash.as_bytes(), contract_output.to_bytes())?; 28 | Ok(()) 29 | } 30 | 31 | async fn delete_contract_logs_for_caller(&mut self, tx_hash: &Hash) -> Result<(), BlockchainError> { 32 | trace!("delete contract logs for caller {}", tx_hash); 33 | Self::remove_from_disk_without_reading(self.snapshot.as_mut(), &self.contracts_logs, tx_hash.as_bytes())?; 34 | Ok(()) 35 | } 36 | } -------------------------------------------------------------------------------- /xelis_common/src/transaction/payload/contract/deploy.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::{contract::ContractModule, serializer::*}; 5 | use super::Deposits; 6 | 7 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 8 | pub struct InvokeConstructorPayload { 9 | pub max_gas: u64, 10 | // Assets deposited with this call 11 | pub deposits: Deposits, 12 | } 13 | 14 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 15 | pub struct DeployContractPayload { 16 | #[serde(flatten)] 17 | pub contract: ContractModule, 18 | pub invoke: Option, 19 | } 20 | 21 | impl Serializer for DeployContractPayload { 22 | fn write(&self, writer: &mut Writer) { 23 | self.contract.write(writer); 24 | self.invoke.write(writer); 25 | } 26 | 27 | fn read(reader: &mut Reader) -> Result { 28 | Ok(Self { 29 | contract: ContractModule::read(reader)?, 30 | invoke: Option::read(reader)? 31 | }) 32 | } 33 | 34 | fn size(&self) -> usize { 35 | self.contract.size() + self.invoke.size() 36 | } 37 | } 38 | 39 | impl Serializer for InvokeConstructorPayload { 40 | fn write(&self, writer: &mut Writer) { 41 | self.max_gas.write(writer); 42 | self.deposits.write(writer); 43 | } 44 | 45 | fn read(reader: &mut Reader) -> Result { 46 | Ok(Self { 47 | max_gas: u64::read(reader)?, 48 | deposits: Deposits::read(reader)?, 49 | }) 50 | } 51 | 52 | fn size(&self) -> usize { 53 | self.max_gas.size() + self.deposits.size() 54 | } 55 | } -------------------------------------------------------------------------------- /xelis_common/src/prompt/error.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | sync::PoisonError, 3 | io::Error as IOError 4 | }; 5 | 6 | use anyhow::Error; 7 | use thiserror::Error; 8 | 9 | use crate::serializer::ReaderError; 10 | use super::command::CommandError; 11 | 12 | #[derive(Error, Debug)] 13 | pub enum PromptError { 14 | #[error("Logs path is not a folder, it must ends with /")] 15 | LogsPathNotFolder, 16 | #[error("Filename for log cannot be a directory")] 17 | FileNotDir, 18 | #[error("Auto compress logs is enabled but date based logs are disabled")] 19 | AutoCompressParam, 20 | #[error("Canceled read input")] 21 | Canceled, 22 | #[error("End of stream")] 23 | EndOfStream, 24 | #[error(transparent)] 25 | FernError(#[from] fern::InitError), 26 | #[error(transparent)] 27 | IOError(#[from] IOError), 28 | #[error("Poison Error: {}", _0)] 29 | PoisonError(String), 30 | #[error("Prompt is already running")] 31 | AlreadyRunning, 32 | #[error("Prompt is not running")] 33 | NotRunning, 34 | #[error("No command manager found")] 35 | NoCommandManager, 36 | #[error("Error while parsing: {}", _0)] 37 | ParseInputError(String), 38 | #[error(transparent)] 39 | ReaderError(#[from] ReaderError), 40 | #[error(transparent)] 41 | CommandError(#[from] CommandError), 42 | #[error(transparent)] 43 | Any(#[from] Error) 44 | } 45 | 46 | impl From> for PromptError { 47 | fn from(err: PoisonError) -> Self { 48 | Self::PoisonError(format!("{}", err)) 49 | } 50 | } 51 | 52 | impl From for CommandError { 53 | fn from(err: PromptError) -> Self { 54 | Self::Any(err.into()) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /xelis_common/src/serializer/mod.rs: -------------------------------------------------------------------------------- 1 | mod defaults; 2 | mod reader; 3 | mod writer; 4 | mod hexable; 5 | mod raw; 6 | mod count; 7 | mod dynamic_len; 8 | 9 | use std::marker::Sized; 10 | 11 | pub use reader::*; 12 | pub use writer::Writer; 13 | pub use defaults::DEFAULT_MAX_ITEMS; 14 | pub use hexable::*; 15 | pub use raw::*; 16 | pub use count::*; 17 | pub use dynamic_len::*; 18 | 19 | pub trait Serializer { 20 | fn write(&self, writer: &mut Writer); 21 | 22 | fn to_bytes(&self) -> Vec { 23 | let mut buffer = Vec::new(); 24 | let mut writer = Writer::new(&mut buffer); 25 | self.write(&mut writer); 26 | buffer 27 | } 28 | 29 | fn to_hex(&self) -> String { 30 | let mut buffer = Vec::new(); 31 | let mut writer = Writer::new(&mut buffer); 32 | self.write(&mut writer); 33 | hex::encode(buffer) 34 | } 35 | 36 | fn size(&self) -> usize { 37 | let mut buffer = Vec::new(); 38 | let mut writer = Writer::new(&mut buffer); 39 | self.write(&mut writer); 40 | buffer.len() 41 | } 42 | 43 | fn read(reader: &mut Reader) -> Result 44 | where Self: Sized; 45 | 46 | fn from_hex(hex: &str) -> Result 47 | where Self: Sized { 48 | match hex::decode(hex) { 49 | Ok(bytes) => { 50 | let mut reader = Reader::new(&bytes); 51 | Self::read(&mut reader) 52 | }, 53 | Err(_) => Err(ReaderError::InvalidHex) 54 | } 55 | } 56 | 57 | fn from_bytes(bytes: &[u8]) -> Result 58 | where Self: Sized { 59 | let mut reader = Reader::new(bytes); 60 | Self::read(&mut reader) 61 | } 62 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/nonce.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | account::VersionedNonce, 4 | block::TopoHeight, 5 | crypto::PublicKey 6 | }; 7 | use crate::core::error::BlockchainError; 8 | 9 | #[async_trait] 10 | pub trait NonceProvider { 11 | // Check if the account has a nonce 12 | async fn has_nonce(&self, key: &PublicKey) -> Result; 13 | 14 | // Check if the account has a nonce at a specific topoheight 15 | async fn has_nonce_at_exact_topoheight(&self, key: &PublicKey, topoheight: TopoHeight) -> Result; 16 | 17 | // Get the last topoheigh that the account has a nonce 18 | async fn get_last_topoheight_for_nonce(&self, key: &PublicKey) -> Result; 19 | 20 | // Get the last nonce of the account, this is based on the last topoheight available 21 | async fn get_last_nonce(&self, key: &PublicKey) -> Result<(TopoHeight, VersionedNonce), BlockchainError>; 22 | 23 | // Get the nonce at a specific topoheight for an account 24 | async fn get_nonce_at_exact_topoheight(&self, key: &PublicKey, topoheight: TopoHeight) -> Result; 25 | 26 | // Get the nonce under or equal topoheight requested for an account 27 | async fn get_nonce_at_maximum_topoheight(&self, key: &PublicKey, topoheight: TopoHeight) -> Result, BlockchainError>; 28 | 29 | // set the new nonce at exact topoheight for account 30 | // This will do like `set_nonce_at_topoheight` but will also update the pointer 31 | async fn set_last_nonce_to(&mut self, key: &PublicKey, topoheight: TopoHeight, nonce: &VersionedNonce) -> Result<(), BlockchainError>; 32 | } -------------------------------------------------------------------------------- /xelis_common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod crypto; 2 | pub mod serializer; 3 | pub mod transaction; 4 | pub mod block; 5 | pub mod account; 6 | pub mod api; 7 | pub mod contract; 8 | 9 | pub mod utils; 10 | pub mod config; 11 | pub mod immutable; 12 | pub mod difficulty; 13 | pub mod network; 14 | pub mod asset; 15 | pub mod context; 16 | pub mod queue; 17 | pub mod varuint; 18 | pub mod time; 19 | pub mod versioned_type; 20 | 21 | pub mod tokio; 22 | 23 | #[cfg(feature = "rpc")] 24 | pub mod rpc; 25 | 26 | #[cfg(feature = "prompt")] 27 | pub mod prompt; 28 | 29 | #[cfg(feature = "clap")] 30 | // If clap feature is enabled, build the correct style for CLI 31 | pub fn get_cli_styles() -> clap::builder::Styles { 32 | use clap::builder::styling::*; 33 | 34 | clap::builder::Styles::styled() 35 | .usage( 36 | Style::new() 37 | .bold() 38 | .fg_color(Some(Color::Ansi(AnsiColor::Yellow))), 39 | ) 40 | .header( 41 | Style::new() 42 | .bold() 43 | .fg_color(Some(Color::Ansi(AnsiColor::Yellow))), 44 | ) 45 | .literal( 46 | Style::new().fg_color(Some(Color::Ansi(AnsiColor::Green))), 47 | ) 48 | .invalid( 49 | Style::new() 50 | .bold() 51 | .fg_color(Some(Color::Ansi(AnsiColor::Red))), 52 | ) 53 | .error( 54 | Style::new() 55 | .bold() 56 | .fg_color(Some(Color::Ansi(AnsiColor::Red))), 57 | ) 58 | .valid( 59 | Style::new() 60 | .bold() 61 | .fg_color(Some(Color::Ansi(AnsiColor::Green))), 62 | ) 63 | .placeholder( 64 | Style::new().fg_color(Some(Color::Ansi(AnsiColor::Green))), 65 | ) 66 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/asset_supply.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | block::TopoHeight, 4 | crypto::Hash, 5 | versioned_type::Versioned 6 | }; 7 | use crate::core::error::BlockchainError; 8 | 9 | pub type VersionedSupply = Versioned; 10 | 11 | // Circulating Supply Provider is used for non-native assets being tracked 12 | // We don't track the emitted and burned amount because, based on the Contract 13 | // implementation it may create overflow which not would be healthy data on long term 14 | // (in case of mint/burn mechanisms). 15 | // Only the circulating supply can be ensured to be valid in the 0..2^64 range 16 | #[async_trait] 17 | pub trait AssetCirculatingSupplyProvider { 18 | // Verify if we have a supply already set for this asset 19 | async fn has_circulating_supply_for_asset(&self, asset: &Hash) -> Result; 20 | 21 | // Verify if we have a versioned data at exact topoheight 22 | async fn has_circulating_supply_for_asset_at_exact_topoheight(&self, asset: &Hash, topoheight: TopoHeight) -> Result; 23 | 24 | // Get the supply at exact topoheight 25 | async fn get_circulating_supply_for_asset_at_exact_topoheight(&self, asset: &Hash, topoheight: TopoHeight) -> Result; 26 | 27 | // Get the supply at the maximum topoheight 28 | async fn get_circulating_supply_for_asset_at_maximum_topoheight(&self, asset: &Hash, topoheight: TopoHeight) -> Result, BlockchainError>; 29 | 30 | // Set the latest supply pointer for this asset and store the versioned data 31 | async fn set_last_circulating_supply_for_asset(&mut self, asset: &Hash, topoheight: TopoHeight, supply: &VersionedSupply) -> Result<(), BlockchainError>; 32 | } -------------------------------------------------------------------------------- /xelis_common/src/crypto/proofs/range_proof.rs: -------------------------------------------------------------------------------- 1 | pub use bulletproofs::RangeProof; 2 | 3 | use crate::{ 4 | crypto::elgamal::{ 5 | RISTRETTO_COMPRESSED_SIZE, 6 | SCALAR_SIZE 7 | }, 8 | serializer::{ 9 | Reader, 10 | ReaderError, 11 | Serializer, 12 | Writer 13 | }, 14 | transaction::MAX_TRANSFER_COUNT 15 | }; 16 | 17 | 18 | #[allow(non_snake_case)] 19 | impl Serializer for RangeProof { 20 | fn write(&self, writer: &mut Writer) { 21 | let bytes = self.to_bytes(); 22 | writer.write_u16(bytes.len() as u16); 23 | writer.write_bytes(&bytes); 24 | } 25 | 26 | fn read(reader: &mut Reader) -> Result { 27 | let len = reader.read_u16()? as usize; 28 | // 7 elements in Range Proof: 3 scalars and 4 points 29 | // 2 scalars in InnerProductProof 30 | // Each element is 32 bytes 31 | let min_size = 4 * RISTRETTO_COMPRESSED_SIZE + 5 * SCALAR_SIZE; 32 | if len % 32 != 0 || len < min_size { 33 | return Err(ReaderError::InvalidSize); 34 | } 35 | 36 | // Those are wrong points 37 | if (len - min_size) % 32 != 0 { 38 | return Err(ReaderError::InvalidSize); 39 | } 40 | 41 | // Maximum size of a RangeProof is 2 * MAX_TRANSFER_COUNT * RISTRETTO_COMPRESSED_SIZE 42 | let max_size_possible = min_size + (MAX_TRANSFER_COUNT * 2).next_power_of_two() * RISTRETTO_COMPRESSED_SIZE; 43 | if len > max_size_possible { 44 | return Err(ReaderError::InvalidSize); 45 | } 46 | 47 | let bytes = reader.read_bytes_ref(len)?; 48 | RangeProof::from_bytes(&bytes).map_err(|_| ReaderError::InvalidValue) 49 | } 50 | 51 | fn size(&self) -> usize { 52 | self.to_bytes().len() + 2 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /xelis_common/src/transaction/builder/fee.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::crypto::elgamal::CompressedPublicKey; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug, Copy, Default, JsonSchema)] 7 | #[serde(rename_all = "snake_case")] 8 | pub enum ExtraFeeMode { 9 | #[default] 10 | None, 11 | // how much we want to pay above the calculated/required fees. 12 | // This is useful to have more chance to get included first 13 | Tip(u64), 14 | // multiply the calculated fee, 15 | Multiplier(f64), 16 | } 17 | 18 | #[derive(Serialize, Deserialize, Clone, Debug, Copy, JsonSchema)] 19 | #[serde(rename_all = "snake_case")] 20 | pub enum FeeBuilder { 21 | // Fixed fee amount to use for the TX 22 | Fixed(u64), 23 | // Determined either by the wallet, or by the given constraints 24 | Extra(ExtraFeeMode) 25 | // TODO: support a "maximum fee" for future 26 | } 27 | 28 | impl Default for FeeBuilder { 29 | fn default() -> Self { 30 | FeeBuilder::Extra(ExtraFeeMode::None) 31 | } 32 | } 33 | 34 | pub trait FeeHelper { 35 | type Error; 36 | 37 | /// Get the fee multiplier from wallet if wanted 38 | fn get_fee_multiplier(&self) -> f64 { 39 | 1f64 40 | } 41 | 42 | // Get the default base fee per KB 43 | // By default, returns None to use the minimal required 44 | fn get_base_fee(&self) -> Option { 45 | None 46 | } 47 | 48 | // Get the maximum fee to pay in case of higher base fee 49 | // By default, returns the same as TX fee 50 | fn get_max_fee(&self, fee: u64) -> u64 { 51 | fee 52 | } 53 | 54 | /// Verify if the account exists or if we should pay more fees for account creation 55 | fn account_exists(&self, account: &CompressedPublicKey) -> Result; 56 | } 57 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/difficulty.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use indexmap::IndexSet; 3 | use xelis_common::{ 4 | block::{BlockHeader, BlockVersion}, 5 | crypto::Hash, 6 | difficulty::{ 7 | CumulativeDifficulty, 8 | Difficulty 9 | }, 10 | immutable::Immutable, 11 | time::TimestampMillis, 12 | varuint::VarUint 13 | }; 14 | use crate::core::error::BlockchainError; 15 | 16 | // this trait is useful for P2p to check itself the validty of a chain 17 | #[async_trait] 18 | pub trait DifficultyProvider { 19 | // Get the block height using its hash 20 | async fn get_height_for_block_hash(&self, hash: &Hash) -> Result; 21 | 22 | // Get the block version using its hash 23 | async fn get_version_for_block_hash(&self, hash: &Hash) -> Result; 24 | 25 | // Get the timestamp from the block using its hash 26 | async fn get_timestamp_for_block_hash(&self, hash: &Hash) -> Result; 27 | 28 | // Get the difficulty for a block hash 29 | async fn get_difficulty_for_block_hash(&self, hash: &Hash) -> Result; 30 | 31 | // Get the cumulative difficulty for a block hash 32 | async fn get_cumulative_difficulty_for_block_hash(&self, hash: &Hash) -> Result; 33 | 34 | // Get past blocks (block tips) for a specific block hash 35 | async fn get_past_blocks_for_block_hash(&self, hash: &Hash) -> Result>, BlockchainError>; 36 | 37 | // Get a block header using its hash 38 | async fn get_block_header_by_hash(&self, hash: &Hash) -> Result, BlockchainError>; 39 | 40 | // Retrieve the estimated covariance (P) for a block hash 41 | async fn get_estimated_covariance_for_block_hash(&self, hash: &Hash) -> Result; 42 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/versioned/dag_order.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::{debug, trace}; 3 | use xelis_common::{block::TopoHeight, crypto::Hash}; 4 | use crate::core::{ 5 | error::BlockchainError, 6 | storage::{ 7 | DagOrderProvider, RocksStorage, VersionedDagOrderProvider, rocksdb::{ 8 | Column, 9 | IteratorMode 10 | }, snapshot::Direction 11 | } 12 | }; 13 | 14 | #[async_trait] 15 | impl VersionedDagOrderProvider for RocksStorage { 16 | // Delete the topoheight for a block hash 17 | async fn delete_dag_order_at_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 18 | trace!("delete topo height for block {}", topoheight); 19 | let hash = self.get_hash_at_topo_height(topoheight).await?; 20 | 21 | self.remove_from_disk(Column::TopoByHash, hash)?; 22 | self.remove_from_disk(Column::HashAtTopo, topoheight.to_be_bytes()) 23 | } 24 | 25 | // Delete every block hashes <=> topoheight relations 26 | async fn delete_dag_order_above_topoheight(&mut self, topoheight: TopoHeight) -> Result<(), BlockchainError> { 27 | trace!("delete dag order above topoheight {}", topoheight); 28 | 29 | let start = (topoheight + 1).to_be_bytes(); 30 | let snapshot = self.snapshot.clone(); 31 | for el in Self::iter_internal::(&self.db, snapshot.as_ref(), IteratorMode::From(&start, Direction::Forward), Column::HashAtTopo)? { 32 | let (topo, hash) = el?; 33 | debug!("found hash {} at topoheight {} while threshold topoheight is at {}", hash, topo, topoheight); 34 | Self::remove_from_disk_internal(&self.db, self.snapshot.as_mut(), Column::HashAtTopo, &topo.to_be_bytes())?; 35 | Self::remove_from_disk_internal(&self.db, self.snapshot.as_mut(), Column::TopoByHash, &hash)?; 36 | } 37 | 38 | Ok(()) 39 | } 40 | } -------------------------------------------------------------------------------- /xelis_common/src/account/nonce.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | use crate::{ 5 | block::TopoHeight, 6 | serializer::{ 7 | Reader, 8 | ReaderError, 9 | Serializer, 10 | Writer 11 | } 12 | }; 13 | 14 | pub type Nonce = u64; 15 | 16 | #[derive(Clone, Serialize, Deserialize, JsonSchema)] 17 | pub struct VersionedNonce { 18 | nonce: Nonce, 19 | previous_topoheight: Option, 20 | } 21 | 22 | impl VersionedNonce { 23 | pub fn new(nonce: Nonce, previous_topoheight: Option) -> Self { 24 | Self { 25 | nonce, 26 | previous_topoheight 27 | } 28 | } 29 | 30 | pub fn get_nonce(&self) -> Nonce { 31 | self.nonce 32 | } 33 | 34 | pub fn set_nonce(&mut self, value: Nonce) { 35 | self.nonce = value; 36 | } 37 | 38 | pub fn get_previous_topoheight(&self) -> Option { 39 | self.previous_topoheight 40 | } 41 | 42 | pub fn set_previous_topoheight(&mut self, previous_topoheight: Option) { 43 | self.previous_topoheight = previous_topoheight; 44 | } 45 | } 46 | 47 | impl Serializer for VersionedNonce { 48 | fn write(&self, writer: &mut Writer) { 49 | self.previous_topoheight.write(writer); 50 | self.nonce.write(writer); 51 | } 52 | 53 | fn read(reader: &mut Reader) -> Result { 54 | let previous_topoheight = Option::read(reader)?; 55 | let nonce = Nonce::read(reader)?; 56 | 57 | Ok(Self { 58 | nonce, 59 | previous_topoheight 60 | }) 61 | } 62 | 63 | fn size(&self) -> usize { 64 | self.nonce.size() + self.previous_topoheight.size() 65 | } 66 | } 67 | 68 | impl Display for VersionedNonce { 69 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 70 | write!(f, "Nonce[{}, previous: {:?}", self.nonce, self.previous_topoheight) 71 | } 72 | } -------------------------------------------------------------------------------- /xelis_daemon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xelis_daemon" 3 | version = "1.21.0" 4 | edition = "2021" 5 | authors = ["Slixe "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | xelis_common = { path = "../xelis_common", features = ["prompt", "clap", "rpc-server", "tokio"] } 11 | xelis-environment = { git = "https://github.com/xelis-project/xelis-vm", branch = "dev" } 12 | xelis-vm = { git = "https://github.com/xelis-project/xelis-vm", branch = "dev" } 13 | 14 | # Database backends 15 | sled = "0.34.7" 16 | rocksdb = { version = "0.23.0", optional = true } 17 | 18 | # Used for Diffie-Hellman key exchange in p2p 19 | x25519-dalek = { version = "2.0.1", features = ["serde", "zeroize", "static_secrets"] } 20 | 21 | metrics-exporter-prometheus = "0.17.0" 22 | semver = "1.0.23" 23 | itertools = "0.14.0" 24 | linked-hash-map = "0.5.6" 25 | bytes = "1" 26 | humantime = "2.1.0" 27 | human_bytes = "0.4.2" 28 | tokio-socks = "0.5.2" 29 | snap = "1.1.1" 30 | 31 | # Common dependencies 32 | schemars = { workspace = true } 33 | actix-web = { workspace = true } 34 | actix-cors = { workspace = true } 35 | strum = { workspace = true } 36 | clap = { workspace = true } 37 | lru = { workspace = true } 38 | async-trait = { workspace = true } 39 | lazy_static = { workspace = true } 40 | chacha20poly1305 = { workspace = true, features = ["bytes"]} 41 | metrics = { workspace = true } 42 | tokio = { workspace = true, features = ["rt-multi-thread", "io-util", "io-std", "time", "macros", "sync", "net"] } 43 | hex = { workspace = true } 44 | anyhow = { workspace = true } 45 | thiserror = { workspace = true } 46 | log = { workspace = true } 47 | rand = { workspace = true } 48 | serde = { workspace = true } 49 | serde_json = { workspace = true } 50 | indexmap = { workspace = true } 51 | futures = { workspace = true } 52 | 53 | [features] 54 | default = ["sled", "rocksdb"] 55 | rocksdb = ["dep:rocksdb"] 56 | sled = [] 57 | 58 | [dev-dependencies] 59 | tempdir = "*" 60 | -------------------------------------------------------------------------------- /xelis_common/src/transaction/verify/error.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error as AnyError; 2 | use thiserror::Error; 3 | use xelis_vm::ValidatorError; 4 | 5 | use crate::{ 6 | account::Nonce, 7 | crypto::{ 8 | proofs::ProofVerificationError, 9 | Hash 10 | }, 11 | contract::vm::ContractError 12 | }; 13 | 14 | #[derive(Error, Debug)] 15 | pub enum VerificationError { 16 | #[error("State error: {0}")] 17 | State(T), 18 | #[error("Invalid TX {} nonce, got {} expected {}", _0, _1, _2)] 19 | InvalidNonce(Hash, Nonce, Nonce), 20 | #[error("Sender is receiver")] 21 | SenderIsReceiver, 22 | #[error("Invalid signature")] 23 | InvalidSignature, 24 | #[error("Proof verification error: {0}")] 25 | Proof(#[from] ProofVerificationError), 26 | #[error("Extra Data is too big in transfer")] 27 | TransferExtraDataSize, 28 | #[error("Extra Data is too big in transaction")] 29 | TransactionExtraDataSize, 30 | #[error("Transfer count is invalid")] 31 | TransferCount, 32 | #[error("Deposit count is invalid")] 33 | DepositCount, 34 | #[error("Invalid commitments assets")] 35 | Commitments, 36 | #[error("Invalid multisig participants count")] 37 | MultiSigParticipants, 38 | #[error("Invalid multisig threshold")] 39 | MultiSigThreshold, 40 | #[error("MultiSig not configured")] 41 | MultiSigNotConfigured, 42 | #[error("MultiSig not found")] 43 | MultiSigNotFound, 44 | #[error("Invalid format")] 45 | InvalidFormat, 46 | #[error("Module error: {0}")] 47 | ModuleError(#[from] ValidatorError), 48 | #[error(transparent)] 49 | AnyError(#[from] AnyError), 50 | #[error("Invalid invoke contract")] 51 | InvalidInvokeContract, 52 | #[error("Contract not found")] 53 | ContractNotFound, 54 | #[error("Deposit decompressed not found")] 55 | DepositNotFound, 56 | #[error("Configured max gas is above the network limit")] 57 | MaxGasReached, 58 | #[error(transparent)] 59 | Contract(#[from] ContractError), 60 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/snapshot/bytes_view.rs: -------------------------------------------------------------------------------- 1 | use std::{borrow::Cow, ops::Deref}; 2 | 3 | use bytes::Bytes; 4 | 5 | /// A view over different byte storage types to provide a unified interface 6 | /// for accessing byte data. 7 | pub enum BytesView<'a> { 8 | Bytes(Cow<'a, Bytes>), 9 | #[cfg(feature = "sled")] 10 | IVec(sled::IVec), 11 | Boxed(Box<[u8]>), 12 | Ref(&'a [u8]), 13 | } 14 | 15 | impl <'a> AsRef<[u8]> for BytesView<'a> { 16 | fn as_ref(&self) -> &[u8] { 17 | match self { 18 | BytesView::Bytes(b) => b.as_ref(), 19 | #[cfg(feature = "sled")] 20 | BytesView::IVec(ivec) => ivec.as_ref(), 21 | BytesView::Boxed(b) => b.as_ref(), 22 | BytesView::Ref(r) => r, 23 | } 24 | } 25 | } 26 | 27 | impl Deref for BytesView<'_> { 28 | type Target = [u8]; 29 | 30 | fn deref(&self) -> &Self::Target { 31 | self.as_ref() 32 | } 33 | } 34 | 35 | impl From for BytesView<'_> { 36 | fn from(bytes: Bytes) -> Self { 37 | BytesView::Bytes(Cow::Owned(bytes)) 38 | } 39 | } 40 | 41 | impl<'a> From<&'a [u8]> for BytesView<'a> { 42 | fn from(slice: &'a [u8]) -> Self { 43 | BytesView::Ref(slice) 44 | } 45 | } 46 | 47 | impl<'a> From> for BytesView<'a> { 48 | fn from(cow: Cow<'a, Bytes>) -> Self { 49 | BytesView::Bytes(cow) 50 | } 51 | } 52 | 53 | impl<'a> From<&'a Bytes> for BytesView<'a> { 54 | fn from(bytes: &'a Bytes) -> Self { 55 | BytesView::Bytes(Cow::Borrowed(bytes)) 56 | } 57 | } 58 | impl From> for BytesView<'_> { 59 | fn from(boxed: Box<[u8]>) -> Self { 60 | BytesView::Boxed(boxed) 61 | } 62 | } 63 | 64 | impl From> for BytesView<'_> { 65 | fn from(vec: Vec) -> Self { 66 | BytesView::Boxed(vec.into_boxed_slice()) 67 | } 68 | } 69 | 70 | #[cfg(feature = "sled")] 71 | impl From for BytesView<'_> { 72 | fn from(ivec: sled::IVec) -> Self { 73 | BytesView::IVec(ivec) 74 | } 75 | } -------------------------------------------------------------------------------- /xelis_common/benches/address.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use std::hint::black_box; 3 | use xelis_common::crypto::{Address, AddressType, KeyPair}; 4 | 5 | fn bench_address_to_string(c: &mut Criterion) { 6 | let mut group = c.benchmark_group("address_to_string"); 7 | 8 | // Create a normal mainnet address 9 | let keypair = KeyPair::new(); 10 | let address = Address::new(true, AddressType::Normal, keypair.get_public_key().compress()); 11 | 12 | group.bench_function("normal", |b| { 13 | b.iter(|| { 14 | black_box(address.to_string()); 15 | }) 16 | }); 17 | 18 | group.finish(); 19 | } 20 | 21 | fn bench_address_from_string(c: &mut Criterion) { 22 | let mut group = c.benchmark_group("address_from_string"); 23 | 24 | // Create a normal mainnet address and convert to string 25 | let keypair = KeyPair::new(); 26 | let address = Address::new(true, AddressType::Normal, keypair.get_public_key().compress()); 27 | let address_string = address.to_string(); 28 | 29 | group.bench_function("normal", |b| { 30 | b.iter(|| { 31 | Address::from_string(black_box(&address_string)).expect("Failed to parse address"); 32 | }) 33 | }); 34 | 35 | group.finish(); 36 | } 37 | 38 | fn bench_address_roundtrip(c: &mut Criterion) { 39 | let mut group = c.benchmark_group("address_roundtrip"); 40 | 41 | let keypair = KeyPair::new(); 42 | let address = Address::new(true, AddressType::Normal, keypair.get_public_key().compress()); 43 | 44 | group.bench_function("to_string_and_from_string", |b| { 45 | b.iter(|| { 46 | let address_string = black_box(&address).to_string(); 47 | Address::from_string(black_box(&address_string)).expect("Failed to parse address"); 48 | }) 49 | }); 50 | 51 | group.finish(); 52 | } 53 | 54 | criterion_group!( 55 | address_benches, 56 | bench_address_to_string, 57 | bench_address_from_string, 58 | bench_address_roundtrip 59 | ); 60 | criterion_main!(address_benches); 61 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/client_protocol.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::crypto::Hash; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::Tips 6 | }; 7 | 8 | #[async_trait] 9 | pub trait ClientProtocolProvider { 10 | // Get the block hash that executed the transaction 11 | async fn get_block_executor_for_tx(&self, tx: &Hash) -> Result; 12 | 13 | // Check if the transaction was executed 14 | async fn is_tx_executed_in_a_block(&self, tx: &Hash) -> Result; 15 | 16 | // Check if the transaction was executed in a specific block 17 | async fn is_tx_executed_in_block(&self, tx: &Hash, block: &Hash) -> Result; 18 | 19 | // Is the transaction included in at least a block 20 | async fn is_tx_linked_to_blocks(&self, hash: &Hash) -> Result; 21 | 22 | // Is the block linked to the transaction 23 | async fn has_block_linked_to_tx(&self, tx: &Hash, block: &Hash) -> Result; 24 | 25 | // Same as has_block_linked_to_tx + add_block_for_tx but read only one time 26 | async fn add_block_linked_to_tx_if_not_present(&mut self, tx: &Hash, block: &Hash) -> Result; 27 | 28 | // Unlink the transaction from the block 29 | async fn unlink_transaction_from_block(&mut self, tx: &Hash, block: &Hash) -> Result; 30 | 31 | // Get all blocks in which the transaction is included 32 | async fn get_blocks_for_tx(&self, hash: &Hash) -> Result; 33 | 34 | // Set the block hash that executed the transaction 35 | async fn mark_tx_as_executed_in_block(&mut self, tx: &Hash, block: &Hash) -> Result<(), BlockchainError>; 36 | 37 | // Unmark the transaction as executed 38 | async fn unmark_tx_from_executed(&mut self, tx: &Hash) -> Result<(), BlockchainError>; 39 | 40 | // Set all blocks in which the transaction is included 41 | async fn set_blocks_for_tx(&mut self, tx: &Hash, blocks: &Tips) -> Result<(), BlockchainError>; 42 | } -------------------------------------------------------------------------------- /xelis_common/src/context.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any, TypeId}, 3 | collections::HashMap, 4 | hash::{BuildHasher, BuildHasherDefault, Hasher} 5 | }; 6 | 7 | use anyhow::{Result, Context as AnyContext}; 8 | 9 | // A hasher for `TypeId`s that takes advantage of its known characteristics. 10 | #[derive(Debug, Default, Clone, Copy)] 11 | pub struct NoOpHasher(u64); 12 | 13 | impl Hasher for NoOpHasher { 14 | fn write(&mut self, _: &[u8]) { 15 | unimplemented!("This NoOpHasher can only handle u64s") 16 | } 17 | 18 | fn write_u64(&mut self, i: u64) { 19 | self.0 = i; 20 | } 21 | 22 | fn finish(&self) -> u64 { 23 | self.0 24 | } 25 | } 26 | 27 | #[derive(Clone, Default)] 28 | pub struct NoOpBuildHasher; 29 | 30 | impl BuildHasher for NoOpBuildHasher { 31 | type Hasher = NoOpHasher; 32 | 33 | fn build_hasher(&self) -> Self::Hasher { 34 | NoOpHasher::default() 35 | } 36 | } 37 | 38 | pub struct Context { 39 | values: HashMap, BuildHasherDefault>, 40 | } 41 | 42 | impl Context { 43 | pub fn new() -> Self { 44 | Self { 45 | values: HashMap::default() 46 | } 47 | } 48 | 49 | pub fn store(&mut self, data: T) { 50 | self.values.insert(TypeId::of::(), Box::new(data)); 51 | } 52 | 53 | pub fn remove(&mut self) { 54 | self.values.remove(&TypeId::of::()); 55 | } 56 | 57 | pub fn has(&self) -> bool { 58 | self.values.contains_key(&TypeId::of::()) 59 | } 60 | 61 | pub fn get_optional(&self) -> Option<&T> { 62 | self.values.get(&TypeId::of::()).and_then(|b| b.downcast_ref()) 63 | } 64 | 65 | pub fn get(&self) -> Result<&T> { 66 | self.get_optional().context("Requested type not found") 67 | } 68 | 69 | pub fn get_copy(&self) -> Result { 70 | self.get().copied() 71 | } 72 | } 73 | 74 | impl Default for Context { 75 | fn default() -> Self { 76 | Self::new() 77 | } 78 | } -------------------------------------------------------------------------------- /xelis_common/src/tokio/thread_pool.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, pin::Pin, sync::Arc 3 | }; 4 | 5 | use log::debug; 6 | use super::{ 7 | sync::{mpsc, Mutex}, 8 | task::JoinHandle, 9 | spawn_task 10 | }; 11 | 12 | type Job = Pin + Send>>; 13 | 14 | pub struct ThreadPool { 15 | sender: mpsc::Sender, 16 | workers: Vec, 17 | } 18 | 19 | impl ThreadPool { 20 | pub fn new(size: usize) -> Self { 21 | let (sender, receiver) = mpsc::channel(size); 22 | 23 | let shared_receiver = Arc::new(Mutex::new(receiver)); 24 | let workers = (0..size) 25 | .map(|id| Worker::new(id, shared_receiver.clone())) 26 | .collect(); 27 | 28 | Self { sender, workers } 29 | } 30 | 31 | pub fn tasks_count(&self) -> usize { 32 | self.workers.len() 33 | } 34 | 35 | pub async fn execute(&self, future: F) -> Result<(), mpsc::error::SendError> 36 | where 37 | F: Future + Send + 'static, 38 | F::Output: Send + 'static, 39 | { 40 | let job = Box::pin(future); 41 | self.sender.send(job).await 42 | } 43 | 44 | pub fn stop(&mut self) { 45 | for worker in self.workers.drain(..) { 46 | debug!("Stopping worker {}", worker.id); 47 | worker.handle.abort(); 48 | } 49 | } 50 | } 51 | 52 | struct Worker { 53 | id: usize, 54 | handle: JoinHandle<()> 55 | } 56 | 57 | impl Worker { 58 | pub fn new(id: usize, receiver: Arc>>) -> Self { 59 | let handle = spawn_task(format!("thread-pool-#{}", id), async move { 60 | debug!("Worker {} started", id); 61 | loop { 62 | let job = { 63 | let mut receiver = receiver.lock().await; 64 | receiver.recv().await 65 | }; 66 | 67 | if let Some(job) = job { 68 | job.await; 69 | } else { 70 | break; 71 | } 72 | } 73 | }); 74 | 75 | Self { id, handle } 76 | } 77 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/account.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | crypto::PublicKey, 4 | block::TopoHeight, 5 | }; 6 | use crate::core::error::BlockchainError; 7 | 8 | use super::{AssetProvider, BalanceProvider, NetworkProvider, NonceProvider}; 9 | 10 | #[async_trait] 11 | pub trait AccountProvider: NonceProvider + BalanceProvider + NetworkProvider + AssetProvider { 12 | // Get the number of accounts with nonces available on chain 13 | async fn count_accounts(&self) -> Result; 14 | 15 | // first time we saw this account on chain 16 | async fn get_account_registration_topoheight(&self, key: &PublicKey) -> Result; 17 | 18 | // set the registration topoheight 19 | async fn set_account_registration_topoheight(&mut self, key: &PublicKey, topoheight: TopoHeight) -> Result<(), BlockchainError>; 20 | 21 | // delete the registration of an account 22 | async fn delete_account_for(&mut self, key: &PublicKey) -> Result<(), BlockchainError>; 23 | 24 | // Check if account is registered 25 | async fn is_account_registered(&self, key: &PublicKey) -> Result; 26 | 27 | // Check if account is registered at topoheight 28 | // This will check that the registration topoheight is less or equal to the given topoheight 29 | async fn is_account_registered_for_topoheight(&self, key: &PublicKey, topoheight: TopoHeight) -> Result; 30 | 31 | // Get registered accounts supporting pagination and filtering by topoheight 32 | // Returned keys must have a nonce or a balance updated in the range given 33 | async fn get_registered_keys<'a>(&'a self, minimum_topoheight: Option, maximum_topoheight: Option) -> Result> + 'a, BlockchainError>; 34 | 35 | // Check if the account has a nonce updated in the range given 36 | // It will also check balances if no nonce found 37 | async fn has_key_updated_in_range(&self, key: &PublicKey, minimum_topoheight: TopoHeight, maximum_topoheight: TopoHeight) -> Result; 38 | 39 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/snapshot.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::{debug, trace}; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{ 6 | sled::{Snapshot, TreeWrapper}, 7 | CacheProvider, 8 | SledStorage, 9 | SnapshotProvider 10 | } 11 | }; 12 | 13 | #[async_trait] 14 | impl SnapshotProvider for SledStorage { 15 | type Column = TreeWrapper; 16 | 17 | // Check if we have a commit point already set 18 | async fn has_snapshot(&self) -> Result { 19 | trace!("has snapshot"); 20 | Ok(self.snapshot.is_some()) 21 | } 22 | 23 | async fn start_snapshot(&mut self) -> Result<(), BlockchainError> { 24 | trace!("start snapshot"); 25 | if self.snapshot.is_some() { 26 | return Err(BlockchainError::CommitPointAlreadyStarted); 27 | } 28 | 29 | let snapshot = Snapshot::new(self.cache.clone_mut()); 30 | self.snapshot = Some(snapshot); 31 | Ok(()) 32 | } 33 | 34 | async fn end_snapshot(&mut self, apply: bool) -> Result<(), BlockchainError> { 35 | trace!("end snapshot"); 36 | let snapshot = self.snapshot.take() 37 | .ok_or(BlockchainError::CommitPointNotStarted)?; 38 | 39 | if apply { 40 | self.cache = snapshot.cache; 41 | 42 | for (tree, batch) in snapshot.trees { 43 | trace!("Applying batch to tree {:?}", tree.0.name()); 44 | for (key, value) in batch.into_iter() { 45 | match value { 46 | Some(value) => tree.insert(key, value.as_ref())?, 47 | None => tree.remove(key)?, 48 | }; 49 | } 50 | } 51 | } else { 52 | debug!("Clearing caches due to invalidation of the commit point"); 53 | self.clear_objects_cache().await?; 54 | } 55 | 56 | Ok(()) 57 | } 58 | 59 | async fn swap_snapshot(&mut self, other: Snapshot) -> Result, BlockchainError> { 60 | trace!("swap snapshot"); 61 | Ok(self.snapshot.replace(other)) 62 | } 63 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/rocksdb/providers/snapshot.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::{debug, trace}; 3 | use crate::core::{ 4 | error::BlockchainError, 5 | storage::{ 6 | rocksdb::{Column, Snapshot}, 7 | CacheProvider, 8 | RocksStorage, 9 | SnapshotProvider, 10 | } 11 | }; 12 | 13 | #[async_trait] 14 | impl SnapshotProvider for RocksStorage { 15 | type Column = Column; 16 | 17 | // Check if we have a commit point already set 18 | async fn has_snapshot(&self) -> Result { 19 | trace!("has snapshot"); 20 | Ok(self.snapshot.is_some()) 21 | } 22 | 23 | async fn start_snapshot(&mut self) -> Result<(), BlockchainError> { 24 | trace!("start snapshot"); 25 | if self.snapshot.is_some() { 26 | return Err(BlockchainError::CommitPointAlreadyStarted); 27 | } 28 | 29 | self.snapshot = Some(Snapshot::new(self.cache.clone_mut())); 30 | Ok(()) 31 | } 32 | 33 | async fn end_snapshot(&mut self, apply: bool) -> Result<(), BlockchainError> { 34 | trace!("end snapshot"); 35 | let snapshot = self.snapshot.take() 36 | .ok_or(BlockchainError::CommitPointNotStarted)?; 37 | 38 | if apply { 39 | trace!("applying snapshot"); 40 | for (column, batch) in snapshot.trees { 41 | for (key, value) in batch { 42 | if let Some(value) = value { 43 | self.insert_into_disk(column, &key.as_ref(), &value.as_ref())?; 44 | } else { 45 | self.remove_from_disk(column, &key.as_ref())?; 46 | } 47 | } 48 | } 49 | 50 | self.cache = snapshot.cache; 51 | } else { 52 | debug!("Clearing caches due to invalidation of the commit point"); 53 | self.clear_objects_cache().await?; 54 | } 55 | 56 | Ok(()) 57 | } 58 | 59 | async fn swap_snapshot(&mut self, other: Snapshot) -> Result, BlockchainError> { 60 | trace!("swap snapshot"); 61 | Ok(self.snapshot.replace(other)) 62 | } 63 | } -------------------------------------------------------------------------------- /xelis_common/src/difficulty.rs: -------------------------------------------------------------------------------- 1 | use crate::{varuint::VarUint, crypto::Hash}; 2 | use primitive_types::U256; 3 | use thiserror::Error; 4 | 5 | // This type is used to easily switch between u64 and u128 as example 6 | // And its easier to see where we use the block difficulty 7 | // Difficulty is a value that represents the amount of work required to mine a block 8 | // On XELIS, each difficulty point is a hash per second 9 | pub type Difficulty = VarUint; 10 | // Cumulative difficulty is the sum of all difficulties of all blocks in the chain 11 | // It is used to determine which branch is the main chain in BlockDAG merging. 12 | pub type CumulativeDifficulty = VarUint; 13 | 14 | #[derive(Error, Debug)] 15 | pub enum DifficultyError { 16 | #[error("Difficulty cannot be a value zero")] 17 | DifficultyCannotBeZero, 18 | #[error("Error while converting value to BigUint")] 19 | ErrorOnConversionBigUint 20 | } 21 | 22 | // Verify the validity of a block difficulty against the current network difficulty 23 | // All operations are done on U256 to avoid overflow 24 | pub fn check_difficulty(hash: &Hash, difficulty: &Difficulty) -> Result { 25 | let target = compute_difficulty_target(difficulty)?; 26 | Ok(check_difficulty_against_target(hash, &target)) 27 | } 28 | 29 | // Compute the difficulty target from the difficulty value 30 | // This can be used to keep the target in cache instead of recomputing it each time 31 | pub fn compute_difficulty_target(difficulty: &Difficulty) -> Result { 32 | let diff = difficulty.as_ref(); 33 | if diff.is_zero() { 34 | return Err(DifficultyError::DifficultyCannotBeZero) 35 | } 36 | 37 | Ok(U256::max_value() / diff) 38 | } 39 | 40 | // Check if the hash is below the target difficulty 41 | pub fn check_difficulty_against_target(hash: &Hash, target: &U256) -> bool { 42 | let hash_work = U256::from_big_endian(hash.as_bytes()); 43 | hash_work <= *target 44 | } 45 | 46 | // Convert a hash to a difficulty value 47 | // This is only used by miner 48 | #[inline(always)] 49 | pub fn difficulty_from_hash(hash: &Hash) -> Difficulty { 50 | (U256::max_value() / U256::from_big_endian(hash.as_bytes())).into() 51 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/contract/balance.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::{ 3 | block::TopoHeight, 4 | crypto::Hash, 5 | versioned_type::Versioned 6 | }; 7 | use crate::core::error::BlockchainError; 8 | 9 | pub type VersionedContractBalance = Versioned; 10 | 11 | #[async_trait] 12 | pub trait ContractBalanceProvider { 13 | // Check if a balance exists for asset and contract 14 | async fn has_contract_balance_for(&self, contract: &Hash, asset: &Hash) -> Result; 15 | 16 | // Check if a balance exists for asset and contract at specific topoheight 17 | async fn has_contract_balance_at_exact_topoheight(&self, contract: &Hash, asset: &Hash, topoheight: TopoHeight) -> Result; 18 | 19 | // Get the balance at a specific topoheight for asset and contract 20 | async fn get_contract_balance_at_exact_topoheight(&self, contract: &Hash, asset: &Hash, topoheight: TopoHeight) -> Result; 21 | 22 | // Get the balance under or equal topoheight requested for asset and contract 23 | async fn get_contract_balance_at_maximum_topoheight(&self, contract: &Hash, asset: &Hash, topoheight: TopoHeight) -> Result, BlockchainError>; 24 | 25 | // Get the last topoheight that the contract has a balance 26 | async fn get_last_topoheight_for_contract_balance(&self, contract: &Hash, asset: &Hash) -> Result, BlockchainError>; 27 | 28 | // Get the latest topoheight & versioned data for a contract balance 29 | async fn get_last_contract_balance(&self, contract: &Hash, asset: &Hash) -> Result<(TopoHeight, VersionedContractBalance), BlockchainError>; 30 | 31 | // Get all the contract balances assets 32 | async fn get_contract_assets_for<'a>(&'a self, contract: &'a Hash) -> Result> + 'a, BlockchainError>; 33 | 34 | // Set the last balance for asset and contract at specific topoheight 35 | async fn set_last_contract_balance_to(&mut self, contract: &Hash, asset: &Hash, topoheight: TopoHeight, balance: VersionedContractBalance) -> Result<(), BlockchainError>; 36 | } -------------------------------------------------------------------------------- /xelis_common/src/serializer/dynamic_len.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | // Dynamic length encoding 4 | // It can encode lengths up to u32::MAX 5 | // Encoding: 6 | // - If length <= 0xFC: 1 byte (length) 7 | // - If length <= 0xFFFF: 1 byte (0xFD) + 2 bytes (length) 8 | // - If length <= 0xFFFFFFFF: 1 byte (0xFE) + 4 bytes (length) 9 | // - 0xFF is reserved for future use 10 | pub struct DynamicLen(pub usize); 11 | 12 | impl Serializer for DynamicLen { 13 | fn read(reader: &mut Reader) -> Result { 14 | let tag = reader.read_u8()?; 15 | let len = match tag { 16 | n @ 0x00..=0xFC => n as usize, 17 | 0xFD => reader.read_u16()? as usize, 18 | 0xFE => reader.read_u32()? as usize, 19 | _ => return Err(ReaderError::InvalidValue), 20 | }; 21 | Ok(Self(len)) 22 | } 23 | 24 | fn write(&self, writer: &mut Writer) { 25 | let len = self.0; 26 | if len <= 0xFC { 27 | writer.write_u8(len as u8); 28 | } else if len <= 0xFFFF { 29 | writer.write_u8(0xFD); 30 | writer.write_u16(len as u16); 31 | } else { 32 | writer.write_u8(0xFE); 33 | writer.write_u32(len as u32); 34 | } 35 | } 36 | 37 | fn size(&self) -> usize { 38 | let len = self.0; 39 | if len <= 0xFC { 40 | 1 41 | } else if len <= 0xFFFF { 42 | 3 43 | } else { 44 | 5 45 | } 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use super::*; 52 | use crate::serializer::Reader; 53 | 54 | #[test] 55 | fn test_dynamic_len_serialization() { 56 | let lengths = [0, 1, 252, 253, 254, 255, 256, 65535, 65536, 1_000_000, u32::MAX as usize]; 57 | 58 | for &len in lengths.iter() { 59 | let dynamic_len = DynamicLen(len); 60 | 61 | let mut bytes = Vec::new(); 62 | let mut writer = Writer::new(&mut bytes); 63 | dynamic_len.write(&mut writer); 64 | 65 | let mut reader = Reader::new(&bytes); 66 | let decoded = DynamicLen::read(&mut reader).unwrap(); 67 | 68 | assert_eq!(dynamic_len.0, decoded.0); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /xelis_common/src/contract/module.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, str::FromStr, sync::Arc}; 2 | 3 | use schemars::JsonSchema; 4 | use serde::{Deserialize, Serialize}; 5 | pub use xelis_vm::Module; 6 | 7 | use crate::serializer::*; 8 | 9 | #[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema)] 10 | #[serde(rename_all = "snake_case")] 11 | #[repr(u8)] 12 | pub enum ContractVersion { 13 | #[default] 14 | V0, 15 | } 16 | 17 | impl FromStr for ContractVersion { 18 | type Err = &'static str; 19 | 20 | fn from_str(s: &str) -> Result { 21 | match s { 22 | "v0" | "0" => Ok(ContractVersion::V0), 23 | _ => Err("Invalid contract version"), 24 | } 25 | } 26 | } 27 | 28 | #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] 29 | pub struct ContractModule { 30 | pub version: ContractVersion, 31 | // keep it behind Arc to reduce cloning overhead 32 | pub module: Arc, 33 | } 34 | 35 | impl fmt::Display for ContractVersion { 36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 37 | match self { 38 | ContractVersion::V0 => write!(f, "v0"), 39 | } 40 | } 41 | } 42 | 43 | impl Serializer for ContractVersion { 44 | fn write(&self, writer: &mut Writer) { 45 | writer.write_u8(*self as u8); 46 | } 47 | 48 | fn read(reader: &mut Reader) -> Result { 49 | match reader.read_u8()? { 50 | 0 => Ok(ContractVersion::V0), 51 | _ => Err(ReaderError::InvalidValue), 52 | } 53 | } 54 | 55 | fn size(&self) -> usize { 56 | 1 57 | } 58 | } 59 | 60 | impl Serializer for ContractModule { 61 | fn write(&self, writer: &mut Writer) { 62 | self.version.write(writer); 63 | self.module.write(writer); 64 | } 65 | 66 | fn read(reader: &mut Reader) -> Result { 67 | let version = ContractVersion::read(reader)?; 68 | let module = Module::read(reader)?; 69 | 70 | Ok(Self { 71 | version, 72 | module: Arc::new(module), 73 | }) 74 | } 75 | 76 | fn size(&self) -> usize { 77 | self.version.size() + self.module.size() 78 | } 79 | } -------------------------------------------------------------------------------- /xelis_common/benches/homomorphic_encryption.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use std::hint::black_box; 3 | use curve25519_dalek::Scalar; 4 | use xelis_common::crypto::KeyPair; 5 | 6 | // Current Homomorphic Encryption operations used by XELIS network 7 | // Those are based on the Twisted elGamal encryption scheme. 8 | fn bench_he_operations(c: &mut Criterion) { 9 | let mut group = c.benchmark_group("he_operations"); 10 | 11 | let keypair = KeyPair::new(); 12 | let amount = 100u64; 13 | let scalar = Scalar::from(50u64); 14 | 15 | // Generate the ciphertexts 16 | let ct1 = keypair.get_public_key().encrypt(amount); 17 | let ct2 = keypair.get_public_key().encrypt(scalar); 18 | 19 | group.bench_function("add", |b| { 20 | b.iter(|| { 21 | let _result = black_box(ct1.clone() + ct2.clone()); 22 | }) 23 | }); 24 | 25 | group.bench_function("add scalar", |b| { 26 | b.iter(|| { 27 | let _result = black_box(ct1.clone() - scalar); 28 | }) 29 | }); 30 | 31 | group.bench_function("sub", |b| { 32 | b.iter(|| { 33 | let _result = black_box(ct1.clone() - ct2.clone()); 34 | }) 35 | }); 36 | 37 | group.bench_function("sub scalar", |b| { 38 | b.iter(|| { 39 | let _result = black_box(ct1.clone() - scalar); 40 | }) 41 | }); 42 | 43 | group.bench_function("mul scalar", |b| { 44 | b.iter(|| { 45 | let _result = black_box(ct1.clone() * scalar); 46 | }) 47 | }); 48 | 49 | group.bench_function("div scalar", |b| { 50 | b.iter(|| { 51 | let _result = black_box(ct1.clone() * scalar.invert()); 52 | }) 53 | }); 54 | 55 | group.bench_function("compress", |b| { 56 | b.iter(|| { 57 | let _ = black_box(ct1.compress()); 58 | }) 59 | }); 60 | 61 | let compressed = ct1.compress(); 62 | group.bench_function("decompress", |b| { 63 | b.iter(|| { 64 | let _ = black_box(compressed.decompress().unwrap()); 65 | }) 66 | }); 67 | 68 | group.finish(); 69 | } 70 | 71 | 72 | criterion_group!( 73 | he_benches, 74 | bench_he_operations 75 | ); 76 | criterion_main!(he_benches); -------------------------------------------------------------------------------- /xelis_common/src/contract/provider.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_vm::tid; 3 | 4 | use crate::{ 5 | account::CiphertextCache, 6 | contract::ContractModule, 7 | asset::AssetData, 8 | block::TopoHeight, 9 | crypto::{Hash, PublicKey} 10 | }; 11 | 12 | use super::ContractStorage; 13 | 14 | #[async_trait] 15 | pub trait ContractProvider: ContractStorage + Send + Sync + 'static { 16 | // Returns the balance of the contract 17 | async fn get_contract_balance_for_asset(&self, contract: &Hash, asset: &Hash, topoheight: TopoHeight) -> Result, anyhow::Error>; 18 | 19 | // Get the account balance for asset 20 | async fn get_account_balance_for_asset(&self, key: &PublicKey, asset: &Hash, topoheight: TopoHeight) -> Result, anyhow::Error>; 21 | 22 | // Verify if we have already a registered execution for such contract at a specific topoheight 23 | async fn has_scheduled_execution_at_topoheight(&self, contract: &Hash, topoheight: TopoHeight) -> Result; 24 | 25 | // Verify if an asset exists in the storage 26 | async fn asset_exists(&self, asset: &Hash, topoheight: TopoHeight) -> Result; 27 | 28 | // Load the asset data from the storage 29 | async fn load_asset_data(&self, asset: &Hash, topoheight: TopoHeight) -> Result, anyhow::Error>; 30 | 31 | // Load the asset supply 32 | // Supply is the current circulating supply 33 | async fn load_asset_circulating_supply(&self, asset: &Hash, topoheight: TopoHeight) -> Result<(TopoHeight, u64), anyhow::Error>; 34 | 35 | // Verify if the address is well registered 36 | async fn account_exists(&self, key: &PublicKey, topoheight: TopoHeight) -> Result; 37 | 38 | // Load a contract module 39 | async fn load_contract_module(&self, contract: &Hash, topoheight: TopoHeight) -> Result, anyhow::Error>; 40 | } 41 | 42 | // This is a wrapper around the storage to allow for the storage to be passed in the Context 43 | pub struct ContractProviderWrapper<'a, S: ContractProvider>(pub &'a S); 44 | 45 | tid! { impl<'a, S: 'static> TidAble<'a> for ContractProviderWrapper<'a, S> where S: ContractProvider } 46 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/contract/scheduled_execution.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use futures::Stream; 3 | use xelis_common::{block::TopoHeight, contract::ScheduledExecution, crypto::Hash}; 4 | 5 | use crate::core::error::BlockchainError; 6 | 7 | #[async_trait] 8 | pub trait ContractScheduledExecutionProvider { 9 | // Set contract scheduled execution at provided topoheight 10 | // Caller must ensures that the topoheight configured is >= current topoheight 11 | async fn set_contract_scheduled_execution_at_topoheight(&mut self, contract: &Hash, topoheight: TopoHeight, execution: &ScheduledExecution, execution_topoheight: TopoHeight) -> Result<(), BlockchainError>; 12 | 13 | // Has a contract scheduled execution registered at the provided topoheight? 14 | // only one scheduled execution per contract and per topoheight can exist. 15 | async fn has_contract_scheduled_execution_at_topoheight(&self, contract: &Hash, topoheight: TopoHeight) -> Result; 16 | 17 | // Get the contract scheduled execution registered at the provided topoheight 18 | async fn get_contract_scheduled_execution_at_topoheight(&self, contract: &Hash, topoheight: TopoHeight) -> Result; 19 | 20 | // Get the registered scheduled executions at the provided topoheight 21 | async fn get_registered_contract_scheduled_executions_at_topoheight<'a>(&'a self, topoheight: TopoHeight) -> Result> + Send + 'a, BlockchainError>; 22 | 23 | // Get the scheduled executions planned for the provided topoheight 24 | async fn get_contract_scheduled_executions_at_topoheight<'a>(&'a self, topoheight: TopoHeight) -> Result> + Send + 'a, BlockchainError>; 25 | 26 | // Get the registered scheduled executions at maximum topoheight (inclusive) 27 | // Returns a stream of (execution_topoheight, registration_topoheight, execution) 28 | async fn get_registered_contract_scheduled_executions_in_range<'a>(&'a self, minimum_topoheight: TopoHeight, maximum_topoheight: TopoHeight) -> Result> + Send + 'a, BlockchainError>; 29 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/block.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use async_trait::async_trait; 3 | use xelis_common::{ 4 | block::{Block, BlockHeader}, 5 | crypto::Hash, 6 | difficulty::{CumulativeDifficulty, Difficulty}, 7 | immutable::Immutable, 8 | transaction::Transaction, 9 | varuint::VarUint 10 | }; 11 | use crate::core::error::BlockchainError; 12 | use super::{BlocksAtHeightProvider, DifficultyProvider, TransactionProvider}; 13 | 14 | #[async_trait] 15 | pub trait BlockProvider: TransactionProvider + DifficultyProvider + BlocksAtHeightProvider { 16 | // Check if the storage has blocks 17 | async fn has_blocks(&self) -> Result; 18 | 19 | // Count the number of blocks stored 20 | async fn count_blocks(&self) -> Result; 21 | 22 | // Decrease the count of blocks that is currently stored 23 | async fn decrease_blocks_count(&mut self, amount: u64) -> Result<(), BlockchainError>; 24 | 25 | // Check if the block exists using its hash 26 | async fn has_block_with_hash(&self, hash: &Hash) -> Result; 27 | 28 | // Get a block with transactions using its hash 29 | async fn get_block_by_hash(&self, hash: &Hash) -> Result; 30 | 31 | // Get the block size with txs included 32 | async fn get_block_size(&self, hash: &Hash) -> Result; 33 | 34 | // Get the block size with txs included 35 | // EMA is in bytes 36 | async fn get_block_size_ema(&self, hash: &Hash) -> Result; 37 | 38 | // Save a new block with its transactions and difficulty 39 | // Hash is Immutable to be stored efficiently in caches and sharing the same object 40 | // with others caches (like P2p or GetWork) 41 | async fn save_block( 42 | &mut self, 43 | block: Arc, 44 | txs: &[Arc], 45 | difficulty: Difficulty, 46 | cumulative_difficulty: CumulativeDifficulty, 47 | p: VarUint, 48 | size_ema: u32, 49 | hash: Immutable 50 | ) -> Result<(), BlockchainError>; 51 | 52 | // Delete a block using its hash 53 | async fn delete_block_by_hash(&mut self, hash: &Hash) -> Result, BlockchainError>; 54 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/snapshot/changes.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{btree_map::{Entry, IntoIter}, BTreeMap}; 2 | use bytes::Bytes; 3 | 4 | use super::EntryState; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Changes { 8 | pub writes: BTreeMap>, 9 | } 10 | 11 | impl Changes { 12 | /// Set a key to a new value 13 | /// Returns the previous value if any 14 | pub fn insert(&mut self, key: K, value: V) -> EntryState 15 | where 16 | K: Into, 17 | V: Into, 18 | { 19 | match self.writes.insert(key.into(), Some(value.into())) { 20 | Some(Some(prev)) => EntryState::Stored(prev), 21 | Some(None) => EntryState::Deleted, 22 | None => EntryState::Absent, 23 | } 24 | } 25 | 26 | /// Remove a key 27 | /// If bool return true, we must read from disk 28 | /// Returns the previous value if any 29 | pub fn remove(&mut self, key: K) -> EntryState 30 | where 31 | K: Into, 32 | { 33 | match self.writes.entry(key.into()) { 34 | Entry::Occupied(mut entry) => { 35 | let value = entry.get_mut().take(); 36 | match value { 37 | Some(v) => EntryState::Stored(v), 38 | None => EntryState::Deleted, 39 | } 40 | }, 41 | Entry::Vacant(v) => { 42 | v.insert(None); 43 | EntryState::Absent 44 | }, 45 | } 46 | } 47 | 48 | /// Check if key is present in our batch 49 | /// Return None if key wasn't overwritten yet 50 | /// Otherwise, return Some(true) if key is present, Some(false) if it was deleted 51 | pub fn contains(&self, key: K) -> Option 52 | where 53 | K: AsRef<[u8]> 54 | { 55 | self.writes.get(key.as_ref()).map(|v| v.is_some()) 56 | } 57 | } 58 | 59 | impl IntoIterator for Changes { 60 | type Item = (Bytes, Option); 61 | type IntoIter = IntoIter>; 62 | 63 | fn into_iter(self) -> Self::IntoIter { 64 | self.writes.into_iter() 65 | } 66 | } 67 | 68 | impl Default for Changes { 69 | fn default() -> Self { 70 | Self { writes: BTreeMap::new() } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /xelis_common/src/contract/metadata.rs: -------------------------------------------------------------------------------- 1 | use std::hash; 2 | use indexmap::IndexMap; 3 | use xelis_vm::ModuleMetadata as ModuleMetadataInner; 4 | use crate::{crypto::Hash, transaction::ContractDeposit}; 5 | 6 | pub type ModuleMetadata<'a> = ModuleMetadataInner<'a, ContractMetadata>; 7 | 8 | // TODO: include the contract hash, etc 9 | #[derive(Debug, Clone)] 10 | pub struct ContractMetadata { 11 | // Contract hash of the executor 12 | // This may not be the real module hash (depending on delegation), 13 | // but the hash of the contract that is being executed 14 | pub contract_executor: Hash, 15 | // Actual contract caller, if any 16 | // In case a contract calls another contract, 17 | // caller will be set to the contract hash that called this module 18 | // Example: Contract A calls Contract B 19 | // - For Contract A execution, contract_caller is None 20 | // - For Contract B execution, contract_caller is Some(A's hash) 21 | // In case of delegation between contracts, this will still be the original caller 22 | // Example: Contract A delegates to Contract B which calls Contract C 23 | // - For Contract A execution, contract_caller is None 24 | // - For Contract B execution, contract_caller is None (as A delegated to B) 25 | // - For Contract C execution, contract_caller is Some(A's hash) 26 | pub contract_caller: Option, 27 | // All deposits made for this contract 28 | // This is a map of assets to deposit to the called contract 29 | // Entry point contains the deposits made by the user calling it 30 | // In case of contract-to-contract calls, deposits are not propagated and may be empty. 31 | // If during the NEW contract execution some deposits from contract A to contract B are made, 32 | // they will be recorded here. 33 | // This allows to easily track (and do) deposits between contracts. 34 | pub deposits: IndexMap, 35 | } 36 | 37 | impl PartialEq for ContractMetadata { 38 | fn eq(&self, other: &Self) -> bool { 39 | self.contract_executor == other.contract_executor 40 | } 41 | } 42 | 43 | impl Eq for ContractMetadata {} 44 | 45 | impl hash::Hash for ContractMetadata { 46 | fn hash(&self, state: &mut H) { 47 | self.contract_executor.hash(state); 48 | } 49 | } -------------------------------------------------------------------------------- /xelis_wallet/src/precomputed_tables/native.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::create_dir_all, 3 | path::Path, 4 | sync::Arc, 5 | }; 6 | 7 | use anyhow::{bail, Result}; 8 | use log::info; 9 | use xelis_common::{ 10 | crypto::ecdlp, 11 | time::Instant, 12 | utils::detect_available_parallelism 13 | }; 14 | 15 | use super::*; 16 | 17 | // Check if the precomputed tables exists 18 | pub async fn has_precomputed_tables(path: Option<&str>, l1: usize) -> Result { 19 | let path = path.unwrap_or_default(); 20 | let full_path = format!("{path}precomputed_tables_{l1}.bin"); 21 | 22 | Ok(Path::new(&full_path).exists()) 23 | } 24 | 25 | // This will read from file if exists, or generate and store it in file 26 | // This must be call only one time, and can be cloned to be shared through differents wallets 27 | pub async fn read_or_generate_precomputed_tables(path: Option<&str>, l1: usize, progress_report: P, store_on_disk: bool) -> Result { 28 | if let Some(p) = &path { 29 | if !(p.ends_with('/') || p.ends_with('\\')) { 30 | bail!("Path for precomputed tables must ends with / or \\"); 31 | } 32 | 33 | let path = Path::new(&p); 34 | if !path.exists() { 35 | create_dir_all(path)?; 36 | } 37 | } 38 | 39 | let path = path.unwrap_or_default(); 40 | let full_path = format!("{path}precomputed_tables_{l1}.bin"); 41 | 42 | let tables = if Path::new(&full_path).exists() { 43 | info!("Loading precomputed tables from {}", full_path); 44 | ecdlp::ECDLPTables::load_from_file(l1, full_path.as_str())? 45 | } else { 46 | // File does not exists, generate and store it 47 | info!("Generating precomputed tables"); 48 | let instant = Instant::now(); 49 | let tables = ecdlp::ECDLPTables::generate_with_progress_report_par(l1, detect_available_parallelism(), progress_report)?; 50 | if store_on_disk { 51 | info!("Precomputed tables generated, storing to {}", full_path); 52 | tables.write_to_file(full_path.as_str())?; 53 | } 54 | info!("Took {:?} to generate the precomputed tables", instant.elapsed()); 55 | 56 | tables 57 | }; 58 | 59 | Ok(Arc::new(RwLock::new(tables))) 60 | } -------------------------------------------------------------------------------- /xelis_daemon/src/p2p/packet/bootstrap/mod.rs: -------------------------------------------------------------------------------- 1 | mod step; 2 | mod types; 3 | 4 | use xelis_common::serializer::*; 5 | 6 | pub use step::*; 7 | pub use types::*; 8 | 9 | #[derive(Debug)] 10 | pub struct BootstrapChainRequest<'a> { 11 | id: u64, 12 | step: StepRequest<'a> 13 | } 14 | 15 | impl<'a> BootstrapChainRequest<'a> { 16 | pub fn new(id: u64, step: StepRequest<'a>) -> Self { 17 | Self { 18 | id, 19 | step 20 | } 21 | } 22 | 23 | pub fn id(&self) -> u64 { 24 | self.id 25 | } 26 | 27 | pub fn kind(&self) -> StepKind { 28 | self.step.kind() 29 | } 30 | 31 | pub fn step(self) -> StepRequest<'a> { 32 | self.step 33 | } 34 | } 35 | 36 | impl Serializer for BootstrapChainRequest<'_> { 37 | fn read(reader: &mut Reader) -> Result { 38 | let id = reader.read_u64()?; 39 | let step = StepRequest::read(reader)?; 40 | Ok(Self::new(id, step)) 41 | } 42 | 43 | fn write(&self, writer: &mut Writer) { 44 | self.id.write(writer); 45 | self.step.write(writer); 46 | } 47 | 48 | fn size(&self) -> usize { 49 | self.id.size() + 50 | self.step.size() 51 | } 52 | } 53 | 54 | 55 | #[derive(Debug)] 56 | pub struct BootstrapChainResponse { 57 | id: u64, 58 | response: StepResponse 59 | } 60 | 61 | impl BootstrapChainResponse { 62 | pub fn new(id: u64, response: StepResponse) -> Self { 63 | Self { 64 | id, 65 | response 66 | } 67 | } 68 | 69 | pub fn id(&self) -> u64 { 70 | self.id 71 | } 72 | 73 | pub fn kind(&self) -> StepKind { 74 | self.response.kind() 75 | } 76 | 77 | pub fn response(self) -> StepResponse { 78 | self.response 79 | } 80 | } 81 | 82 | impl Serializer for BootstrapChainResponse { 83 | fn read(reader: &mut Reader) -> Result { 84 | let id = reader.read_u64()?; 85 | let response = StepResponse::read(reader)?; 86 | Ok(Self::new(id, response)) 87 | } 88 | 89 | fn write(&self, writer: &mut Writer) { 90 | self.id.write(writer); 91 | self.response.write(writer); 92 | } 93 | 94 | fn size(&self) -> usize { 95 | self.id.size() + 96 | self.response.size() 97 | } 98 | } -------------------------------------------------------------------------------- /xelis_wallet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xelis_wallet" 3 | version = "1.21.0" 4 | edition = "2021" 5 | authors = ["Slixe "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | xelis_common = { path = "../xelis_common", features = ["tokio"] } 11 | sled = "0.34.7" 12 | argon2 = "0.4.1" 13 | crc32fast = "1.3.2" 14 | bytemuck = "1.15.0" 15 | itertools = "0.14.0" 16 | 17 | aes-gcm = { version = "0.11.0-rc.0", optional = true } 18 | actix-web-httpauth = { version = "0.8.0", optional = true } 19 | 20 | # common dependencies 21 | actix-web = { workspace = true, optional = true } 22 | clap = { workspace = true, optional = true } 23 | chacha20poly1305 = { workspace = true } 24 | lazy_static = { workspace = true } 25 | hex = { workspace = true } 26 | strum = { workspace = true } 27 | lru = { workspace = true } 28 | indexmap = { workspace = true } 29 | log = { workspace = true } 30 | rand = { workspace = true } 31 | thiserror = { workspace = true } 32 | anyhow = { workspace = true } 33 | serde = { workspace = true } 34 | serde_json = { workspace = true } 35 | async-trait = { workspace = true } 36 | chrono = { workspace = true } 37 | futures = { workspace = true } 38 | cfg-if = { workspace = true } 39 | tokio-tungstenite-wasm = { workspace = true, optional = true } 40 | 41 | # WASM dependencies 42 | [target.'cfg(target_arch = "wasm32")'.dependencies] 43 | web-sys = { version = "0.3.69", features = ["Window", "Storage", "File", "StorageManager", "FileSystemGetFileOptions", "Navigator", "FileSystemFileHandle", "FileSystemDirectoryHandle", "FileSystemWritableFileStream", "FileSystemSyncAccessHandle"] } 44 | base64 = "0.22.1" 45 | wasm-bindgen-futures = "0.4.43" 46 | 47 | [features] 48 | # Set as default dependencies until https://github.com/rust-lang/cargo/issues/4663 is resolved for binary targets 49 | default = ["cli", "api_server", "tokio-multi-thread"] 50 | xswd = ["xelis_common/rpc", "dep:tokio-tungstenite-wasm", "dep:aes-gcm"] 51 | cli = ["tokio-multi-thread", "dep:clap", "xelis_common/prompt"] 52 | network_handler = ["xelis_common/rpc-client"] 53 | api_server = ["xswd", "network_handler", "xelis_common/rpc-server", "dep:actix-web", "dep:actix-web-httpauth"] 54 | tokio-multi-thread = ["xelis_common/tokio-multi-thread"] 55 | 56 | [lib] 57 | crate-type = ["cdylib", "rlib"] 58 | -------------------------------------------------------------------------------- /xelis_daemon/src/rpc/getwork/miner.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, sync::Arc}; 2 | 3 | use indexmap::IndexSet; 4 | use xelis_common::{ 5 | crypto::{Hash, PublicKey}, 6 | time::{get_current_time_in_millis, TimestampMillis} 7 | }; 8 | 9 | pub struct Miner { 10 | // Used to display correctly its address 11 | mainnet: bool, 12 | // timestamp of first connection 13 | first_seen: TimestampMillis, 14 | // public key of account (address) 15 | key: PublicKey, 16 | // worker name 17 | name: String, 18 | // blocks accepted by us since he is connected 19 | blocks_accepted: IndexSet>, 20 | // blocks rejected since he is connected 21 | blocks_rejected: usize, 22 | // timestamp of the last invalid block received 23 | last_invalid_block: TimestampMillis 24 | } 25 | 26 | impl Miner { 27 | pub fn new(mainnet: bool, key: PublicKey, name: String) -> Self { 28 | Self { 29 | mainnet, 30 | first_seen: get_current_time_in_millis(), 31 | key, 32 | name, 33 | blocks_accepted: IndexSet::new(), 34 | blocks_rejected: 0, 35 | last_invalid_block: 0 36 | } 37 | } 38 | 39 | pub fn first_seen(&self) -> u64 { 40 | self.first_seen 41 | } 42 | 43 | pub fn get_public_key(&self) -> &PublicKey { 44 | &self.key 45 | } 46 | 47 | pub fn get_name(&self) -> &String { 48 | &self.name 49 | } 50 | 51 | pub fn get_blocks_accepted(&self) -> usize { 52 | self.blocks_accepted.len() 53 | } 54 | 55 | pub fn add_new_accepted_block(&mut self, hash: Arc) { 56 | self.blocks_accepted.insert(hash); 57 | } 58 | 59 | pub fn mark_rejected_block(&mut self) { 60 | self.blocks_rejected += 1; 61 | self.last_invalid_block += get_current_time_in_millis(); 62 | } 63 | } 64 | 65 | impl fmt::Display for Miner { 66 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 | let valid_blocks = self.blocks_accepted.iter() 68 | .take(8) 69 | .map(|h| h.to_string()) 70 | .collect::>() 71 | .join(","); 72 | 73 | write!(f, "Miner[address={}, name={}, accepted={} ({}), rejected={}]", self.key.as_address(self.mainnet), self.name, self.blocks_accepted.len(), valid_blocks, self.blocks_rejected) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/blockdag.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use log::trace; 3 | use xelis_common::{ 4 | block::{BlockHeader, TopoHeight}, 5 | crypto::Hash, 6 | immutable::Immutable, 7 | serializer::Serializer 8 | }; 9 | 10 | use crate::core::{ 11 | error::{BlockchainError, DiskContext}, 12 | storage::{ 13 | types::TopoHeightMetadata, 14 | BlockDagProvider, 15 | DagOrderProvider, 16 | DifficultyProvider, 17 | SledStorage 18 | } 19 | }; 20 | 21 | #[async_trait] 22 | impl BlockDagProvider for SledStorage { 23 | async fn get_block_header_at_topoheight(&self, topoheight: TopoHeight) -> Result<(Hash, Immutable), BlockchainError> { 24 | trace!("get block at topoheight: {}", topoheight); 25 | let hash = self.get_hash_at_topo_height(topoheight).await?; 26 | let block = self.get_block_header_by_hash(&hash).await?; 27 | Ok((hash, block)) 28 | } 29 | 30 | async fn get_block_reward_at_topo_height(&self, topoheight: TopoHeight) -> Result { 31 | trace!("get block reward at topo height {}", topoheight); 32 | self.get_metadata_at_topoheight(topoheight).await 33 | .map(|metadata| metadata.block_reward) 34 | } 35 | 36 | async fn get_emitted_supply_at_topo_height(&self, topoheight: TopoHeight) -> Result { 37 | trace!("get supply at topo height {}", topoheight); 38 | self.get_metadata_at_topoheight(topoheight).await 39 | .map(|metadata| metadata.emitted_supply) 40 | } 41 | 42 | async fn get_metadata_at_topoheight(&self, topoheight: TopoHeight) -> Result { 43 | trace!("get metadata at topoheight {}", topoheight); 44 | // TODO: maybe use a cache 45 | self.load_from_disk(&self.topoheight_metadata, &topoheight.to_be_bytes(), DiskContext::MetadataAtTopoHeight(topoheight)) 46 | } 47 | 48 | // Set the metadata for topoheight 49 | async fn set_metadata_at_topoheight(&mut self, topoheight: TopoHeight, metadata: TopoHeightMetadata) -> Result<(), BlockchainError> { 50 | trace!("set topoheight metadata at {}", topoheight); 51 | 52 | Self::insert_into_disk(self.snapshot.as_mut(), &self.topoheight_metadata, &topoheight.to_be_bytes(), metadata.to_bytes())?; 53 | 54 | Ok(()) 55 | } 56 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/sled/providers/block_execution_order.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use xelis_common::crypto::Hash; 3 | use crate::core::{ 4 | error::{BlockchainError, DiskContext}, 5 | storage::{sled::BLOCKS_EXECUTION_ORDER_COUNT, BlockExecutionOrderProvider, SledStorage} 6 | }; 7 | 8 | #[async_trait] 9 | impl BlockExecutionOrderProvider for SledStorage { 10 | async fn get_blocks_execution_order<'a>(&'a self) -> Result> + 'a, BlockchainError> { 11 | Ok(Self::iter_keys(self.snapshot.as_ref(), &self.blocks_execution_order)) 12 | } 13 | 14 | async fn get_block_position_in_order(&self, hash: &Hash) -> Result { 15 | let position = self.load_from_disk(&self.blocks_execution_order, hash.as_bytes(), DiskContext::SearchBlockPositionInOrder)?; 16 | Ok(position) 17 | } 18 | 19 | async fn has_block_position_in_order(&self, hash: &Hash) -> Result { 20 | let contains = self.contains_data(&self.blocks_execution_order, hash.as_bytes())?; 21 | Ok(contains) 22 | } 23 | 24 | async fn add_block_execution_to_order(&mut self, hash: &Hash) -> Result<(), BlockchainError> { 25 | let cache = self.cache_mut(); 26 | let position = cache.blocks_execution_count; 27 | cache.blocks_execution_count += 1; 28 | 29 | Self::insert_into_disk(self.snapshot.as_mut(), &self.blocks_execution_order, hash.as_bytes(), &position.to_be_bytes())?; 30 | Self::insert_into_disk(self.snapshot.as_mut(), &self.extra, BLOCKS_EXECUTION_ORDER_COUNT, &position.to_be_bytes())?; 31 | 32 | Ok(()) 33 | } 34 | 35 | async fn get_blocks_execution_count(&self) -> Result { 36 | Ok(self.cache().blocks_execution_count) 37 | } 38 | 39 | async fn swap_blocks_executions_positions(&mut self, left: &Hash, right: &Hash) -> Result<(), BlockchainError> { 40 | let left_position = self.get_block_position_in_order(left).await?; 41 | let right_position = self.get_block_position_in_order(right).await?; 42 | 43 | Self::insert_into_disk(self.snapshot.as_mut(), &self.blocks_execution_order, left.as_bytes(), &right_position.to_be_bytes())?; 44 | Self::insert_into_disk(self.snapshot.as_mut(), &self.blocks_execution_order, right.as_bytes(), &left_position.to_be_bytes())?; 45 | 46 | Ok(()) 47 | } 48 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/multisig.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use async_trait::async_trait; 4 | use xelis_common::{ 5 | block::TopoHeight, 6 | crypto::PublicKey, 7 | transaction::MultiSigPayload, 8 | versioned_type::Versioned 9 | }; 10 | use crate::core::error::BlockchainError; 11 | 12 | pub type VersionedMultiSig<'a> = Versioned>>; 13 | 14 | #[async_trait] 15 | pub trait MultiSigProvider { 16 | // Retrieve the last topoheight for a given account 17 | async fn get_last_topoheight_for_multisig(&self, account: &PublicKey) -> Result, BlockchainError>; 18 | 19 | // Retrieve a multisig setup for a given account 20 | async fn get_multisig_at_topoheight_for<'a>(&'a self, account: &PublicKey, topoheight: TopoHeight) -> Result, BlockchainError>; 21 | 22 | // Delete the last topoheight for a given account 23 | async fn delete_last_topoheight_for_multisig(&mut self, account: &PublicKey) -> Result<(), BlockchainError>; 24 | 25 | // Retrieve the multisig setup at the maximum topoheight for a given account 26 | async fn get_multisig_at_maximum_topoheight_for<'a>(&'a self, account: &PublicKey, maximum_topoheight: TopoHeight) -> Result)>, BlockchainError>; 27 | 28 | // Verify if an account has a multisig setup 29 | // If the latest version is None, the account has no multisig setup 30 | async fn has_multisig(&self, account: &PublicKey) -> Result; 31 | 32 | // Verify if a version exists at a given topoheight 33 | async fn has_multisig_at_exact_topoheight(&self, account: &PublicKey, topoheight: TopoHeight) -> Result; 34 | 35 | // Retrieve the last multisig setup for a given account 36 | async fn get_last_multisig<'a>(&'a self, account: &PublicKey) -> Result<(TopoHeight, VersionedMultiSig<'a>), BlockchainError> { 37 | let topoheight = self.get_last_topoheight_for_multisig(account).await? 38 | .ok_or(BlockchainError::NoMultisig)?; 39 | 40 | let state = self.get_multisig_at_topoheight_for(account, topoheight).await?; 41 | 42 | Ok((topoheight, state)) 43 | } 44 | 45 | // Store the last multisig setup for a given account 46 | async fn set_last_multisig_to<'a>(&mut self, account: &PublicKey, topoheight: TopoHeight, multisig: VersionedMultiSig<'a>) -> Result<(), BlockchainError>; 47 | } -------------------------------------------------------------------------------- /.github/workflows/docker-publish.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | tags: [ "v**" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | schedule: 10 | - cron: '0 14 * * *' 11 | 12 | env: 13 | SHOULD_PUSH: ${{ github.event_name != 'pull_request' && (github.ref_type == 'tag' || github.ref == 'refs/heads/dev') }} 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: read 20 | packages: write 21 | id-token: write 22 | strategy: 23 | matrix: 24 | app: 25 | - daemon 26 | - miner 27 | - wallet 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v3 31 | 32 | - name: Setup Docker buildx 33 | uses: docker/setup-buildx-action@v3 34 | 35 | - name: Log in to the Docker Hub 36 | if: github.event_name != 'pull_request' 37 | uses: docker/login-action@v3 38 | with: 39 | username: xelis 40 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 41 | 42 | - name: Retrieve version from Cargo.toml 43 | run: | 44 | VERSION=`awk -F ' = ' '$1 ~ /version/ { gsub(/[\\"]/, "", $2); printf("%s",$2) }' xelis_${{ matrix.app }}/Cargo.toml` 45 | echo "XELIS_VERSION=${VERSION}" >> "$GITHUB_ENV" 46 | 47 | - name: Extract Docker metadata 48 | id: meta 49 | uses: docker/metadata-action@v5 50 | with: 51 | images: xelis/${{ matrix.app }} 52 | labels: | 53 | org.opencontainers.image.title=Xelis ${{ matrix.app }} 54 | tags: | 55 | type=schedule,pattern=${{ env.XELIS_VERSION }}-nightly-{{date 'YYYYMMDD'}} 56 | type=semver,pattern={{version}},event=tag 57 | type=ref,event=pr 58 | 59 | - name: Build and push Docker image 60 | id: build-and-push 61 | uses: docker/build-push-action@v5 62 | with: 63 | context: . 64 | push: ${{ env.SHOULD_PUSH }} 65 | tags: ${{ steps.meta.outputs.tags }} 66 | labels: ${{ steps.meta.outputs.labels }} 67 | cache-from: type=gha 68 | cache-to: ${{ env.SHOULD_PUSH && 'type=gha,mode=max' || '' }} 69 | build-args: | 70 | app=xelis_${{ matrix.app }} 71 | commit_hash=${{ github.sha }} 72 | platforms: ${{ env.SHOULD_PUSH && 'linux/amd64,linux/arm64' || 'linux/amd64' }} 73 | -------------------------------------------------------------------------------- /xelis_common/src/transaction/payload/multisig.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexSet; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | use crate::{ 6 | crypto::elgamal::CompressedPublicKey, 7 | serializer::*, 8 | transaction::MAX_MULTISIG_PARTICIPANTS 9 | }; 10 | 11 | // MultiSigPayload is a public payload allowing to setup a multi signature account 12 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 13 | pub struct MultiSigPayload { 14 | // The threshold is the minimum number of signatures required to validate a transaction 15 | pub threshold: u8, 16 | // The participants are the public keys that can sign the transaction 17 | pub participants: IndexSet, 18 | } 19 | 20 | impl MultiSigPayload { 21 | // Is the transaction a delete multisig transaction 22 | pub fn is_delete(&self) -> bool { 23 | self.threshold == 0 && self.participants.is_empty() 24 | } 25 | } 26 | 27 | impl Serializer for MultiSigPayload { 28 | fn write(&self, writer: &mut Writer) { 29 | writer.write_u8(self.threshold); 30 | if self.threshold != 0 { 31 | writer.write_u8(self.participants.len() as u8); 32 | for participant in &self.participants { 33 | participant.write(writer); 34 | } 35 | } 36 | } 37 | 38 | fn read(reader: &mut Reader) -> Result { 39 | let threshold = reader.read_u8()?; 40 | // Only 0 threshold is allowed for delete multisig 41 | if threshold == 0 { 42 | return Ok(MultiSigPayload { 43 | threshold, 44 | participants: IndexSet::new() 45 | }) 46 | } 47 | 48 | let participants_len = reader.read_u8()?; 49 | if participants_len == 0 || participants_len > MAX_MULTISIG_PARTICIPANTS as u8 { 50 | return Err(ReaderError::InvalidSize) 51 | } 52 | 53 | let mut participants = IndexSet::new(); 54 | for _ in 0..participants_len { 55 | if !participants.insert(CompressedPublicKey::read(reader)?) { 56 | return Err(ReaderError::InvalidValue) 57 | } 58 | } 59 | 60 | Ok(MultiSigPayload { 61 | threshold, 62 | participants 63 | }) 64 | } 65 | 66 | fn size(&self) -> usize { 67 | 1 + 1 + self.participants.iter().map(|p| p.size()).sum::() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /xelis_common/src/contract/cache.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use xelis_vm::ValueCell; 3 | use crate::{ 4 | asset::AssetData, 5 | context::NoOpBuildHasher, 6 | contract::DeterministicRandom, 7 | crypto::Hash, 8 | versioned_type::VersionedState 9 | }; 10 | 11 | #[derive(Debug, Clone)] 12 | pub struct AssetChanges { 13 | // The asset data 14 | pub data: (VersionedState, AssetData), 15 | // The supply of the asset 16 | pub circulating_supply: (VersionedState, u64), 17 | } 18 | 19 | // Contract cache containing all the changes/cache made by the contract 20 | #[derive(Debug, Clone)] 21 | pub struct ContractCache { 22 | // The random number generator 23 | // It is deterministic so we can replay the contract 24 | // If none, it means no Random was initiated / needed yetc 25 | // Lazy init using the Option 26 | // This is shared between all executions of the same contract 27 | pub random: Option, 28 | // The storage of the contract 29 | // All the changes made by the contract are stored here 30 | pub storage: HashMap)>>, 31 | // The contract balances 32 | // Those already present are loaded due to the deposits to be added 33 | // If its none, it means we don't have any balance yet 34 | pub balances: HashMap>, 35 | // Memory Storage 36 | // This is shared between all executions of the same contract 37 | pub memory_shared: HashMap, 38 | // Temporary Memory Storage 39 | // This is only available for the current contract call 40 | pub memory: HashMap, 41 | // Custom events generated by the contract 42 | pub events: HashMap, NoOpBuildHasher>, 43 | } 44 | 45 | impl Default for ContractCache { 46 | fn default() -> Self { 47 | Self::new() 48 | } 49 | } 50 | 51 | impl ContractCache { 52 | pub fn new() -> Self { 53 | Self { 54 | random: None, 55 | storage: HashMap::new(), 56 | balances: HashMap::new(), 57 | memory_shared: HashMap::new(), 58 | memory: HashMap::new(), 59 | events: HashMap::default() 60 | } 61 | } 62 | 63 | // Clean the contract cache when it is stored 64 | pub fn clean_up(&mut self) { 65 | // We clean the temporary memory from it 66 | self.memory.clear(); 67 | } 68 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/difficulty/v1.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use humantime::format_duration; 3 | use log::trace; 4 | use xelis_common::{ 5 | difficulty::Difficulty, 6 | time::TimestampMillis, 7 | utils::format_difficulty, 8 | varuint::VarUint 9 | }; 10 | use crate::core::difficulty::kalman_filter; 11 | 12 | const SHIFT: u64 = 32; 13 | // This is equal to 2 ** 32 14 | const LEFT_SHIFT: VarUint = VarUint::from_u64(1 << SHIFT); 15 | // Process noise covariance: 5% of shift 16 | const PROCESS_NOISE_COVAR: VarUint = VarUint::from_u64((1 << SHIFT) / 100 * 5); 17 | 18 | // Initial estimate covariance 19 | // It is used by first blocks 20 | pub const P: VarUint = LEFT_SHIFT; 21 | 22 | // Calculate the required difficulty for the next block based on the solve time of the previous block 23 | // We are using a Kalman filter to estimate the hashrate and adjust the difficulty 24 | pub fn calculate_difficulty(solve_time: TimestampMillis, previous_difficulty: Difficulty, p: VarUint, minimum_difficulty: Difficulty, target_block_time: TimestampMillis) -> (Difficulty, VarUint) { 25 | let z = previous_difficulty / solve_time; 26 | trace!("Calculating difficulty v1, solve time: {}, previous_difficulty: {}, z: {}, p: {}", format_duration(Duration::from_millis(solve_time)), format_difficulty(previous_difficulty), z, p); 27 | let (x_est_new, p_new) = kalman_filter(z, previous_difficulty / target_block_time, p, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 28 | trace!("x_est_new: {}, p_new: {}", x_est_new, p_new); 29 | 30 | let difficulty = x_est_new * target_block_time; 31 | if difficulty < minimum_difficulty { 32 | return (minimum_difficulty, P); 33 | } 34 | 35 | (difficulty, p_new) 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn test_kalman_filter_v1() { 44 | const DEFAULT_DIFFICULTY: Difficulty = Difficulty::from_u64(20 * 15 * 1000); 45 | let z = DEFAULT_DIFFICULTY / VarUint::from_u64(1000); 46 | let (x_est_new, p_new) = kalman_filter(z, VarUint::one(), P, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 47 | assert_eq!(x_est_new, VarUint::one()); 48 | assert_eq!(p_new, VarUint::from_u64(4501837440)); 49 | 50 | let (x_est_new, p_new) = kalman_filter(DEFAULT_DIFFICULTY / VarUint::from_u64(2000), x_est_new, p_new, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 51 | assert_eq!(x_est_new, VarUint::one()); 52 | assert_eq!(p_new, VarUint::from_u64(4699383461)); 53 | } 54 | } -------------------------------------------------------------------------------- /xelis_common/src/serializer/writer.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::Hash; 2 | 3 | pub struct Writer<'a> { 4 | bytes: &'a mut Vec, 5 | len: usize, 6 | } 7 | 8 | impl<'a> Writer<'a> { 9 | pub fn new(bytes: &'a mut Vec) -> Self { 10 | Self { 11 | len: bytes.len(), 12 | bytes 13 | } 14 | } 15 | 16 | pub fn write_bytes(&mut self, bytes: &[u8]) { 17 | self.bytes.extend(bytes); 18 | } 19 | 20 | pub fn write_hash(&mut self, hash: &Hash) { 21 | self.bytes.extend(hash.as_bytes()) 22 | } 23 | 24 | pub fn write_bool(&mut self, value: bool) { 25 | self.bytes.push(if value { 1 } else { 0 }); 26 | } 27 | pub fn write_u8(&mut self, value: u8) { 28 | self.bytes.push(value); 29 | } 30 | 31 | pub fn write_u16(&mut self, value: u16) { 32 | self.bytes.extend(value.to_be_bytes()); 33 | } 34 | 35 | pub fn write_u32(&mut self, value: u32) { 36 | self.bytes.extend(value.to_be_bytes()); 37 | } 38 | 39 | pub fn write_u64(&mut self, value: u64) { 40 | self.bytes.extend(value.to_be_bytes()); 41 | } 42 | 43 | pub fn write_u128(&mut self, value: u128) { 44 | self.bytes.extend(value.to_be_bytes()); 45 | } 46 | 47 | // max 255 bytes 48 | pub fn write_string(&mut self, value: &str) { 49 | let bytes = value.as_bytes(); 50 | self.bytes.push(bytes.len() as u8); 51 | self.bytes.extend(bytes.iter().take(u8::MAX as usize)); 52 | } 53 | 54 | pub fn write_optional_string(&mut self, opt: &Option) { 55 | match opt { 56 | Some(v) => { 57 | self.write_string(v); 58 | }, 59 | None => { 60 | self.bytes.push(0); 61 | } 62 | }; 63 | } 64 | 65 | pub fn write_optional_non_zero_u8(&mut self, opt: Option) { 66 | self.bytes.push(opt.unwrap_or(0)); 67 | } 68 | 69 | pub fn write_optional_non_zero_u16(&mut self, opt: Option) { 70 | self.write_u16(opt.unwrap_or(0)); 71 | } 72 | 73 | pub fn write_optional_non_zero_u64(&mut self, opt: Option) { 74 | self.write_u64(opt.unwrap_or(0)); 75 | } 76 | 77 | pub fn total_write(&self) -> usize { 78 | self.bytes.len() - self.len 79 | } 80 | 81 | pub fn as_mut_bytes(&mut self) -> &mut Vec { 82 | &mut self.bytes 83 | } 84 | 85 | pub fn as_bytes(&self) -> &[u8] { 86 | &self.bytes 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/blocks_at_height.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use async_trait::async_trait; 3 | use indexmap::IndexSet; 4 | use log::error; 5 | use xelis_common::{ 6 | crypto::{Hash, HASH_SIZE}, 7 | serializer::{Reader, ReaderError, Serializer, Writer} 8 | }; 9 | use crate::core::error::BlockchainError; 10 | 11 | // This struct is used to store the blocks hashes at a specific height 12 | // We use an IndexSet to store the hashes and maintains the order we processed them 13 | #[derive(Default)] 14 | pub struct OrderedHashes<'a>(pub Cow<'a, IndexSet>); 15 | 16 | #[async_trait] 17 | pub trait BlocksAtHeightProvider { 18 | // Check if there are blocks at a specific height 19 | async fn has_blocks_at_height(&self, height: u64) -> Result; 20 | 21 | // Retrieve the blocks hashes at a specific height 22 | async fn get_blocks_at_height(&self, height: u64) -> Result, BlockchainError>; 23 | 24 | // This is used to store the blocks hashes at a specific height 25 | async fn set_blocks_at_height(&mut self, tips: &IndexSet, height: u64) -> Result<(), BlockchainError>; 26 | 27 | // Append a block hash at a specific height 28 | async fn add_block_hash_at_height(&mut self, hash: &Hash, height: u64) -> Result<(), BlockchainError>; 29 | 30 | // Remove a block hash at a specific height 31 | async fn remove_block_hash_at_height(&mut self, hash: &Hash, height: u64) -> Result<(), BlockchainError>; 32 | } 33 | 34 | impl Serializer for OrderedHashes<'_> { 35 | fn write(&self, writer: &mut Writer) { 36 | for hash in self.0.iter() { 37 | hash.write(writer); 38 | } 39 | } 40 | 41 | fn read(reader: &mut Reader) -> Result { 42 | let total_size = reader.total_size(); 43 | if total_size % HASH_SIZE != 0 { 44 | error!("Invalid size: {}, expected a multiple of 32 for hashes", total_size); 45 | return Err(ReaderError::InvalidSize) 46 | } 47 | 48 | let count = total_size / HASH_SIZE; 49 | let mut hashes = IndexSet::with_capacity(count); 50 | for _ in 0..count { 51 | hashes.insert(Hash::read(reader)?); 52 | } 53 | 54 | if hashes.len() != count { 55 | error!("Invalid size: received {} elements while sending {}", hashes.len(), count); 56 | return Err(ReaderError::InvalidSize) 57 | } 58 | 59 | Ok(OrderedHashes(Cow::Owned(hashes))) 60 | } 61 | } -------------------------------------------------------------------------------- /xelis_common/src/transaction/source_commitment.rs: -------------------------------------------------------------------------------- 1 | use std::hash; 2 | 3 | use schemars::JsonSchema; 4 | use serde::{Serialize, Deserialize}; 5 | 6 | use crate::{ 7 | serializer::*, 8 | crypto::{ 9 | elgamal::CompressedCommitment, 10 | Hash, 11 | proofs::CommitmentEqProof 12 | } 13 | }; 14 | 15 | // SourceCommitment is a structure that holds the commitment and the equality proof 16 | // of the commitment to the asset 17 | // In a transaction, every spendings are summed up in a single commitment per asset 18 | #[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)] 19 | pub struct SourceCommitment { 20 | commitment: CompressedCommitment, 21 | proof: CommitmentEqProof, 22 | asset: Hash, 23 | } 24 | 25 | // Implement Hash on SourceCommitment 26 | // Note that we only have one SourceCommitment per asset 27 | // So we only need to hash the asset 28 | impl hash::Hash for SourceCommitment { 29 | fn hash(&self, state: &mut H) { 30 | self.asset.hash(state); 31 | } 32 | } 33 | 34 | impl SourceCommitment { 35 | /// Create a new SourceCommitment 36 | pub fn new(commitment: CompressedCommitment, proof: CommitmentEqProof, asset: Hash) -> Self { 37 | SourceCommitment { 38 | commitment, 39 | proof, 40 | asset 41 | } 42 | } 43 | 44 | // Get the commitment 45 | #[inline] 46 | pub fn get_commitment(&self) -> &CompressedCommitment { 47 | &self.commitment 48 | } 49 | 50 | // Get the equality proof 51 | #[inline] 52 | pub fn get_proof(&self) -> &CommitmentEqProof { 53 | &self.proof 54 | } 55 | 56 | // Get the asset hash 57 | #[inline] 58 | pub fn get_asset(&self) -> &Hash { 59 | &self.asset 60 | } 61 | } 62 | 63 | impl Serializer for SourceCommitment { 64 | fn write(&self, writer: &mut Writer) { 65 | self.commitment.write(writer); 66 | self.proof.write(writer); 67 | self.asset.write(writer); 68 | } 69 | 70 | fn read(reader: &mut Reader) -> Result { 71 | let commitment = CompressedCommitment::read(reader)?; 72 | let proof = CommitmentEqProof::read(reader)?; 73 | let asset = Hash::read(reader)?; 74 | 75 | Ok(SourceCommitment { 76 | commitment, 77 | proof, 78 | asset 79 | }) 80 | } 81 | 82 | fn size(&self) -> usize { 83 | self.commitment.size() + self.proof.size() + self.asset.size() 84 | } 85 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/storage/providers/contract/data.rs: -------------------------------------------------------------------------------- 1 | use async_trait::async_trait; 2 | use futures::Stream; 3 | use xelis_vm::ValueCell; 4 | use xelis_common::{ 5 | block::TopoHeight, 6 | crypto::Hash, 7 | versioned_type::Versioned, 8 | }; 9 | use crate::core::error::BlockchainError; 10 | 11 | // Versioned contract data 12 | // ValueCell is optional because it can be deleted 13 | pub type VersionedContractData = Versioned>; 14 | 15 | #[async_trait] 16 | pub trait ContractDataProvider { 17 | // Set a contract data 18 | async fn set_last_contract_data_to(&mut self, contract: &Hash, key: &ValueCell, topoheight: TopoHeight, version: &VersionedContractData) -> Result<(), BlockchainError>; 19 | 20 | // Retrieve the last topoheight for a given contract data 21 | async fn get_last_topoheight_for_contract_data(&self, contract: &Hash, key: &ValueCell) -> Result, BlockchainError>; 22 | 23 | // Retrieve a contract data at a given topoheight 24 | async fn get_contract_data_at_exact_topoheight_for<'a>(&self, contract: &Hash, key: &ValueCell, topoheight: TopoHeight) -> Result; 25 | 26 | // Retrieve a contract data at maximum topoheight 27 | async fn get_contract_data_at_maximum_topoheight_for<'a>(&self, contract: &Hash, key: &ValueCell, maximum_topoheight: TopoHeight) -> Result, BlockchainError>; 28 | 29 | // Retrieve the topoheight of a contract data at maximum topoheight 30 | async fn get_contract_data_topoheight_at_maximum_topoheight_for<'a>(&self, contract: &Hash, key: &ValueCell, maximum_topoheight: TopoHeight) -> Result, BlockchainError>; 31 | 32 | // Check if a contract data exists at a given topoheight 33 | // If the version is None, it returns false 34 | async fn has_contract_data_at_maximum_topoheight(&self, contract: &Hash, key: &ValueCell, topoheight: TopoHeight) -> Result; 35 | 36 | // Check if we have a contract data version at a given topoheight 37 | // It only checks if the topoheight exists 38 | async fn has_contract_data_at_exact_topoheight(&self, contract: &Hash, key: &ValueCell, topoheight: TopoHeight) -> Result; 39 | 40 | // Get all the contract data entries at a maximum topoheight 41 | async fn get_contract_data_entries_at_maximum_topoheight<'a>(&'a self, contract: &'a Hash, topoheight: TopoHeight) -> Result> + Send + 'a, BlockchainError>; 42 | } -------------------------------------------------------------------------------- /xelis_wallet/src/storage/types.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | use xelis_common::{ 5 | account::CiphertextCache, 6 | block::TopoHeight, 7 | crypto::Hash, 8 | serializer::{ 9 | Reader, 10 | ReaderError, 11 | Serializer, 12 | Writer 13 | }, 14 | transaction::{MultiSigPayload, Reference} 15 | }; 16 | 17 | 18 | #[derive(Debug, Clone)] 19 | pub struct Balance { 20 | pub amount: u64, 21 | pub ciphertext: CiphertextCache, 22 | pub topoheight: TopoHeight, 23 | } 24 | 25 | impl Balance { 26 | pub fn new(amount: u64, ciphertext: CiphertextCache, topoheight: TopoHeight) -> Self { 27 | Self { 28 | amount, 29 | ciphertext, 30 | topoheight 31 | } 32 | } 33 | } 34 | 35 | impl Serializer for Balance { 36 | fn write(&self, writer: &mut Writer) { 37 | self.amount.write(writer); 38 | self.ciphertext.write(writer); 39 | self.topoheight.write(writer); 40 | } 41 | 42 | fn read(reader: &mut Reader) -> Result { 43 | let amount = u64::read(reader)?; 44 | let ciphertext = CiphertextCache::read(reader)?; 45 | let topoheight = TopoHeight::read(reader)?; 46 | Ok(Self { 47 | amount, 48 | ciphertext, 49 | topoheight, 50 | }) 51 | } 52 | } 53 | 54 | 55 | #[derive(Debug, Clone)] 56 | pub struct TxCache { 57 | // This is used to store the nonce used to create new transactions 58 | pub nonce: u64, 59 | // Last reference used to build a transaction 60 | pub reference: Reference, 61 | // Last transaction hash created 62 | // This is used to determine if we should erase the last unconfirmed balance or not 63 | pub last_tx_hash_created: Option, 64 | // Set of assets used in the last transaction 65 | pub assets: HashSet, 66 | } 67 | 68 | // A multisig state in the wallet DB 69 | #[derive(Debug, Clone, Serialize, Deserialize)] 70 | pub struct MultiSig { 71 | pub payload: MultiSigPayload, 72 | pub topoheight: TopoHeight, 73 | } 74 | 75 | impl Serializer for MultiSig { 76 | fn write(&self, writer: &mut Writer) { 77 | self.payload.write(writer); 78 | self.topoheight.write(writer); 79 | } 80 | 81 | fn read(reader: &mut Reader) -> Result { 82 | let payload = MultiSigPayload::read(reader)?; 83 | let topoheight = TopoHeight::read(reader)?; 84 | Ok(Self { 85 | payload, 86 | topoheight 87 | }) 88 | } 89 | } -------------------------------------------------------------------------------- /xelis_daemon/src/core/difficulty/v2.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use humantime::format_duration; 3 | use log::trace; 4 | use xelis_common::{ 5 | difficulty::Difficulty, 6 | time::TimestampMillis, 7 | utils::format_difficulty, 8 | varuint::VarUint 9 | }; 10 | use crate::{ 11 | config::MILLIS_PER_SECOND, 12 | core::difficulty::kalman_filter 13 | }; 14 | 15 | const SHIFT: u64 = 20; 16 | // This is equal to 2 ** 20 17 | const LEFT_SHIFT: VarUint = VarUint::from_u64(1 << SHIFT); 18 | // Process noise covariance: 2% of shift 19 | const PROCESS_NOISE_COVAR: VarUint = VarUint::from_u64((1 << SHIFT) * SHIFT / MILLIS_PER_SECOND); 20 | 21 | // Initial estimate covariance 22 | // It is used by first blocks 23 | pub const P: VarUint = LEFT_SHIFT; 24 | 25 | // Calculate the required difficulty for the next block based on the solve time of the previous block 26 | // We are using a Kalman filter to estimate the hashrate and adjust the difficulty 27 | pub fn calculate_difficulty(solve_time: TimestampMillis, previous_difficulty: Difficulty, p: VarUint, minimum_difficulty: Difficulty, block_time_target: TimestampMillis) -> (Difficulty, VarUint) { 28 | let z = previous_difficulty * MILLIS_PER_SECOND / solve_time; 29 | trace!("Calculating difficulty v2, solve time: {}, previous_difficulty: {}, z: {}, p: {}", format_duration(Duration::from_millis(solve_time)), format_difficulty(previous_difficulty), z, p); 30 | let (x_est_new, p_new) = kalman_filter(z, previous_difficulty * MILLIS_PER_SECOND / block_time_target, p, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 31 | trace!("x_est_new: {}, p_new: {}", x_est_new, p_new); 32 | 33 | let difficulty = x_est_new * block_time_target / MILLIS_PER_SECOND; 34 | if difficulty < minimum_difficulty { 35 | return (minimum_difficulty, P); 36 | } 37 | 38 | (difficulty, p_new) 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | 45 | #[test] 46 | fn test_kalman_filter_v2() { 47 | const DEFAULT_DIFFICULTY: Difficulty = Difficulty::from_u64(20 * 15 * 1000); 48 | 49 | let z = DEFAULT_DIFFICULTY / VarUint::from_u64(1000); 50 | let (x_est_new, p_new) = kalman_filter(z, VarUint::one(), P, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 51 | assert_eq!(x_est_new, VarUint::one()); 52 | assert_eq!(p_new, VarUint::from_u64(1067732)); 53 | 54 | let (x_est_new, p_new) = kalman_filter(DEFAULT_DIFFICULTY / VarUint::from_u64(2000), x_est_new, p_new, SHIFT, LEFT_SHIFT, PROCESS_NOISE_COVAR); 55 | assert_eq!(x_est_new, VarUint::one()); 56 | assert_eq!(p_new, VarUint::from_u64(1084948)); 57 | } 58 | } -------------------------------------------------------------------------------- /xelis_common/src/contract/opaque/storage/btree/record.rs: -------------------------------------------------------------------------------- 1 | use xelis_vm::ValueCell; 2 | 3 | use crate::serializer::{Reader, ReaderError, Serializer, Writer}; 4 | 5 | use super::{Node, NodeHeader}; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct NodeRecord { 9 | pub key: Vec, 10 | pub value: ValueCell, 11 | pub parent: Option, 12 | pub left: Option, 13 | pub right: Option, 14 | } 15 | 16 | impl From<&Node> for NodeRecord { 17 | fn from(node: &Node) -> Self { 18 | Self { 19 | key: node.key.clone(), 20 | value: node.value.clone(), 21 | parent: node.parent, 22 | left: node.left, 23 | right: node.right, 24 | } 25 | } 26 | } 27 | 28 | impl NodeRecord { 29 | pub fn into_node(self, id: u64) -> Node { 30 | Node { 31 | id, 32 | key: self.key, 33 | value: self.value, 34 | parent: self.parent, 35 | left: self.left, 36 | right: self.right, 37 | } 38 | } 39 | } 40 | 41 | impl Serializer for NodeRecord { 42 | fn read(reader: &mut Reader) -> Result { 43 | let (parent, left, right, key) = read_node_header_parts(reader)?; 44 | let value = ValueCell::read(reader)?; 45 | Ok(Self { key, value, parent, left, right }) 46 | } 47 | 48 | fn write(&self, writer: &mut Writer) { 49 | debug_assert!(self.key.len() <= u32::MAX as usize); 50 | writer.write_optional_non_zero_u64(self.parent); 51 | writer.write_optional_non_zero_u64(self.left); 52 | writer.write_optional_non_zero_u64(self.right); 53 | writer.write_u32(self.key.len() as u32); 54 | writer.write_bytes(&self.key); 55 | self.value.write(writer); 56 | } 57 | 58 | fn size(&self) -> usize { 59 | 8 * 3 + 4 + self.key.len() + self.value.size() 60 | } 61 | } 62 | 63 | pub fn read_node_header_from_reader(reader: &mut Reader, id: u64) -> Result { 64 | let (parent, left, right, key) = read_node_header_parts(reader)?; 65 | Ok(NodeHeader { id, key, parent, left, right }) 66 | } 67 | 68 | pub fn read_node_header_parts(reader: &mut Reader) -> Result<(Option, Option, Option, Vec), ReaderError> { 69 | let parent = reader.read_optional_non_zero_u64()?; 70 | let left = reader.read_optional_non_zero_u64()?; 71 | let right = reader.read_optional_non_zero_u64()?; 72 | 73 | let key_len = reader.read_u32()? as usize; 74 | let key = reader.read_bytes_ref(key_len)?.to_vec(); 75 | 76 | Ok((parent, left, right, key)) 77 | } 78 | --------------------------------------------------------------------------------