diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7ac7ecdb4..151e8a8a50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build and Test Workflow +name: Build, Lint, and Test on: push: @@ -43,6 +43,10 @@ jobs: sudo apt-get install -y protobuf-compiler cargo install just + - name: Run linting + run: | + just ${{ matrix.just_variants }} lint + - name: Build all crates in workspace run: just ${{ matrix.just_variants }} build diff --git a/.github/workflows/lints.yml b/.github/workflows/lints.yml deleted file mode 100644 index 61106f6d0c..0000000000 --- a/.github/workflows/lints.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Lints Workflow - -on: - push: - branches: - - 'main' - pull_request: - branches: - - 'main' - - '*/*' - workflow_dispatch: - -jobs: - lints: - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - uses: styfle/cancel-workflow-action@0.11.0 - name: Cancel Outdated Builds - with: - all_but_latest: true - access_token: ${{ github.token }} - - - name: Install Nix - uses: cachix/install-nix-action@v23 - - - uses: actions/checkout@v4 - name: Checkout Repository - - - uses: Swatinem/rust-cache@v2 - name: Enable Rust Caching - with: - shared-key: "" - prefix-key: lint - - - name: Format Check - run: cargo fmt -- --check - - - name: Clippy - run: | - nix develop -c just async_std lint - nix develop -c just tokio lint diff --git a/.gitignore b/.gitignore index 839d85dc3d..ff07ca5941 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /target_dirs /.vscode/settings.json **/.DS_Store +*.cache \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4a4c2f5394..5cc96e8e54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "f6cd65a4b849ace0b7f6daeebcc1a1d111282227ca745458c61dbf670e52a597" dependencies = [ "anstyle", "anstyle-parse", @@ -202,9 +202,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" dependencies = [ "anstyle", "windows-sys", @@ -286,7 +286,7 @@ dependencies = [ "blake2", "derivative", "digest 0.10.7", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -526,7 +526,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.28", + "time 0.3.29", ] [[package]] @@ -1003,9 +1003,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", @@ -1208,9 +1208,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", @@ -1218,9 +1218,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", @@ -1282,7 +1282,7 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "commit" version = "0.2.2" -source = "git+https://github.com/EspressoSystems/commit?tag=0.2.2#7e938186607f87da5fd642ce36313aaeab0cfaca" +source = "git+https://github.com/EspressoSystems/commit#5f1c28f1a109f2b36cf597e61a222614958db3b2" dependencies = [ "arbitrary", "ark-serialize 0.3.0", @@ -1290,11 +1290,10 @@ dependencies = [ "derivative", "derive_more", "funty", - "generic-array", "hex", "serde", "sha3", - "tagged-base64 0.2.4", + "tagged-base64 0.3.1", ] [[package]] @@ -2011,7 +2010,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "zeroize", ] @@ -2615,10 +2614,9 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_json", - "sha3", "snafu", "surf-disco", - "time 0.3.28", + "time 0.3.29", "tokio", "toml 0.7.8", "tracing", @@ -2767,7 +2765,7 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "snafu", - "time 0.3.28", + "time 0.3.29", "tokio", "tracing", ] @@ -2839,10 +2837,11 @@ dependencies = [ "rand_chacha 0.3.1", "serde", "serde_json", - "sha2 0.10.7", + "sha2 0.10.8", + "sha3", "snafu", "tagged-base64 0.2.4", - "time 0.3.28", + "time 0.3.29", "tokio", "tracing", "typenum", @@ -3277,7 +3276,7 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jf-primitives" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#f6866c5395405f807e62516b5d56d9dc390aeb61" +source = "git+https://github.com/EspressoSystems/jellyfish#1626a8448e7af8ea153241004375deff9fd961da" dependencies = [ "anyhow", "ark-bls12-377", @@ -3301,7 +3300,6 @@ dependencies = [ "digest 0.10.7", "displaydoc", "espresso-systems-common 0.4.0", - "generic-array", "hashbrown 0.13.2", "itertools 0.10.5", "jf-relation", @@ -3312,7 +3310,7 @@ dependencies = [ "rand_chacha 0.3.1", "rayon", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "sha3", "tagged-base64 0.3.3", "typenum", @@ -3322,7 +3320,7 @@ dependencies = [ [[package]] name = "jf-relation" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#f6866c5395405f807e62516b5d56d9dc390aeb61" +source = "git+https://github.com/EspressoSystems/jellyfish#1626a8448e7af8ea153241004375deff9fd961da" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -3348,7 +3346,7 @@ dependencies = [ [[package]] name = "jf-utils" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#f6866c5395405f807e62516b5d56d9dc390aeb61" +source = "git+https://github.com/EspressoSystems/jellyfish#1626a8448e7af8ea153241004375deff9fd961da" dependencies = [ "ark-ec", "ark-ff", @@ -3357,7 +3355,7 @@ dependencies = [ "digest 0.10.7", "rayon", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "tagged-base64 0.3.3", ] @@ -3610,7 +3608,7 @@ dependencies = [ "rand 0.8.5", "regex", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "smallvec", "unsigned-varint", "void", @@ -3654,7 +3652,7 @@ dependencies = [ "rand 0.8.5", "ring", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "thiserror", "zeroize", ] @@ -3680,7 +3678,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "serde", - "sha2 0.10.7", + "sha2 0.10.8", "smallvec", "thiserror", "uint", @@ -3788,7 +3786,7 @@ dependencies = [ "once_cell", "quick-protobuf", "rand 0.8.5", - "sha2 0.10.7", + "sha2 0.10.8", "snow", "static_assertions", "thiserror", @@ -4843,7 +4841,7 @@ checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" dependencies = [ "once_cell", "pest", - "sha2 0.10.7", + "sha2 0.10.8", ] [[package]] @@ -5168,9 +5166,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13f81c9a9d574310b8351f8666f5a93ac3b0069c45c28ad52c10291389a7cf9" +checksum = "2c78e758510582acc40acb90458401172d41f1016f8c9dde89e49677afb7eec1" dependencies = [ "bytes 1.4.0", "rand 0.8.5", @@ -5312,7 +5310,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.28", + "time 0.3.29", "yasna", ] @@ -5745,7 +5743,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 2.3.3", - "time 0.3.28", + "time 0.3.29", ] [[package]] @@ -5762,7 +5760,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 3.3.0", - "time 0.3.28", + "time 0.3.29", ] [[package]] @@ -5842,9 +5840,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -6033,7 +6031,7 @@ dependencies = [ "rand_core 0.6.4", "ring", "rustc_version 0.4.0", - "sha2 0.10.7", + "sha2 0.10.8", "subtle", ] @@ -6417,6 +6415,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "tagged-base64" +version = "0.3.1" +source = "git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.3.1#9207d39ba7a9c11801511a7077ea4330b6173e44" +dependencies = [ + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "base64 0.13.1", + "crc-any", + "serde", + "snafu", + "tagged-base64-macros 0.3.1", +] + [[package]] name = "tagged-base64" version = "0.3.3" @@ -6452,6 +6464,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tagged-base64-macros" +version = "0.3.1" +source = "git+https://github.com/EspressoSystems/tagged-base64.git?tag=0.3.1#9207d39ba7a9c11801511a7077ea4330b6173e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "tagged-base64-macros" version = "0.3.3" @@ -6646,22 +6667,22 @@ dependencies = [ [[package]] name = "time" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", "itoa 1.0.9", "serde", "time-core", - "time-macros 0.2.14", + "time-macros 0.2.15", ] [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" @@ -6675,9 +6696,9 @@ dependencies = [ [[package]] name = "time-macros" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -7577,7 +7598,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.28", + "time 0.3.29", ] [[package]] @@ -7610,7 +7631,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.28", + "time 0.3.29", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c1a4d45a6d..688e9c37d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,14 +44,19 @@ bitvec = { version = "1.0.1", default-features = false, features = [ "atomic", "serde", ] } -blake3 = "1.4" -commit = { git = "https://github.com/EspressoSystems/commit", tag = "0.2.2" } +blake3 = "1.5" +commit = { git = "https://github.com/EspressoSystems/commit" } custom_debug = "0.5" digest = "0.10" either = { version = "1.8" } espresso-systems-common = { git = "https://github.com/espressosystems/espresso-systems-common", tag = "0.4.1" } ethereum-types = { version = "0.14.1", features = ["impl-serde"] } futures = "0.3.28" + +# TODO generic-array should not be a direct dependency +# https://github.com/EspressoSystems/HotShot/issues/1850 +generic-array = { version = "0.14.7", features = ["serde"] } + jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish" } jf-relation = { git = "https://github.com/EspressoSystems/jellyfish" } jf-utils = { git = "https://github.com/espressosystems/jellyfish" } @@ -64,7 +69,7 @@ serde = { version = "1.0.188", features = ["derive"] } sha2 = "0.10" snafu = "0.7.5" surf-disco = { git = "https://github.com/EspressoSystems/surf-disco.git", tag = "v0.4.2" } -time = "0.3.28" +time = "0.3.29" toml = "0.7.8" tracing = "0.1.37" typenum = "1.17.0" diff --git a/crates/hotshot-qc/Cargo.toml b/crates/hotshot-qc/Cargo.toml index 0faa1f0a87..9c53fb335a 100644 --- a/crates/hotshot-qc/Cargo.toml +++ b/crates/hotshot-qc/Cargo.toml @@ -19,7 +19,7 @@ ark-std = { workspace = true } bincode = { workspace = true } bitvec = { workspace = true } ethereum-types = { workspace = true } -generic-array = "0.14.7" +generic-array = { workspace = true } hotshot-types = { path = "../types" } jf-primitives = { workspace = true } jf-relation = { workspace = true } diff --git a/crates/hotshot-stake-table/Cargo.toml b/crates/hotshot-stake-table/Cargo.toml index 8f11e4d66f..4059589a85 100644 --- a/crates/hotshot-stake-table/Cargo.toml +++ b/crates/hotshot-stake-table/Cargo.toml @@ -16,7 +16,7 @@ bitvec = { workspace = true } digest = { workspace = true } displaydoc = { version = "0.2.3", default-features = false } ethereum-types = { workspace = true } -generic-array = "0.14.7" +generic-array = { workspace = true } hotshot-types = { path = "../types" } jf-primitives = { workspace = true } jf-relation = { workspace = true } diff --git a/crates/hotshot/Cargo.toml b/crates/hotshot/Cargo.toml index 34f9a9b63e..77f9f63d4e 100644 --- a/crates/hotshot/Cargo.toml +++ b/crates/hotshot/Cargo.toml @@ -108,7 +108,6 @@ libp2p-networking = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } serde = { workspace = true, features = ["rc"] } -sha3 = "^0.10" snafu = { workspace = true } surf-disco = { workspace = true } time = { workspace = true } diff --git a/crates/hotshot/examples/infra/modDA.rs b/crates/hotshot/examples/infra/modDA.rs index 2b63a8a794..a06f023159 100644 --- a/crates/hotshot/examples/infra/modDA.rs +++ b/crates/hotshot/examples/infra/modDA.rs @@ -21,8 +21,8 @@ use hotshot_orchestrator::{ config::{NetworkConfig, WebServerConfig}, }; use hotshot_task::task::FilterEvent; -use hotshot_types::HotShotConfig; use hotshot_types::{ + block_impl::{VIDBlockPayload, VIDTransaction}, certificate::ViewSyncCertificate, consensus::ConsensusMetricsValue, data::{QuorumProposal, SequencingLeaf, TestableLeaf}, @@ -38,6 +38,7 @@ use hotshot_types::{ }, state::{ConsensusTime, TestableBlock, TestableState}, }, + HotShotConfig, }; use libp2p_identity::{ ed25519::{self, SecretKey}, @@ -47,6 +48,8 @@ use libp2p_networking::{ network::{MeshParams, NetworkNodeConfigBuilder, NetworkNodeType}, reexport::Multiaddr, }; +use rand::rngs::StdRng; +use rand::SeedableRng; use std::{collections::BTreeSet, sync::Arc}; use std::{num::NonZeroUsize, str::FromStr}; // use libp2p::{ @@ -59,9 +62,9 @@ use std::{num::NonZeroUsize, str::FromStr}; // }; use libp2p_identity::PeerId; // use libp2p_networking::network::{MeshParams, NetworkNodeConfigBuilder, NetworkNodeType}; +use std::{fmt::Debug, net::Ipv4Addr}; use std::{ //collections::{BTreeSet, VecDeque}, - collections::VecDeque, //fs, mem, net::IpAddr, @@ -71,7 +74,6 @@ use std::{ //time::{Duration, Instant}, time::Instant, }; -use std::{fmt::Debug, net::Ipv4Addr}; //use surf_disco::error::ClientError; //use surf_disco::Client; use tracing::{debug, error, info, warn}; @@ -125,6 +127,19 @@ pub async fn run_orchestrator_da< .await; } +/// Helper function to calculate the nuymber of transactions to send per node per round +fn calculate_num_tx_per_round( + node_index: u64, + total_num_nodes: usize, + transactions_per_round: usize, +) -> usize { + if node_index == 0 { + transactions_per_round / total_num_nodes + transactions_per_round % total_num_nodes + } else { + transactions_per_round / total_num_nodes + } +} + /// Defines the behavior of a "run" of the network with a given configuration #[async_trait] pub trait RunDA< @@ -254,38 +269,23 @@ pub trait RunDA< } = self.get_config(); let size = mem::size_of::(); - let adjusted_padding = if padding < size { 0 } else { padding - size }; - let mut txns: VecDeque = VecDeque::new(); - - // TODO ED: In the future we should have each node generate transactions every round to simulate a more realistic network - let tx_to_gen = transactions_per_round * rounds * 3; - { - let mut txn_rng = rand::thread_rng(); - for _ in 0..tx_to_gen { - let txn = - <::StateType as TestableState>::create_random_transaction( - None, - &mut txn_rng, - padding as u64, - ); - txns.push_back(txn); - } - } - debug!("Generated {} transactions", tx_to_gen); + let padding = padding.saturating_sub(size); + let mut txn_rng = StdRng::seed_from_u64(node_index); - debug!("Adjusted padding size is {:?} bytes", adjusted_padding); - let mut round = 0; - let mut total_transactions = 0; + debug!("Adjusted padding size is {:?} bytes", padding); - let start = Instant::now(); + let mut total_transactions_committed = 0; + let mut total_transactions_sent = 0; + let transactions_to_send_per_round = + calculate_num_tx_per_round(node_index, total_nodes.get(), transactions_per_round); info!("Starting hotshot!"); + let start = Instant::now(); + let (mut event_stream, _streamid) = context.get_event_stream(FilterEvent::default()).await; let mut anchor_view: TYPES::Time = ::genesis(); let mut num_successful_commits = 0; - let total_nodes_u64 = total_nodes.get() as u64; - context.hotshot.start_consensus().await; loop { @@ -314,8 +314,20 @@ pub trait RunDA< } } + // send transactions + for _ in 0..transactions_to_send_per_round { + let txn = + <::StateType as TestableState>::create_random_transaction( + None, + &mut txn_rng, + padding as u64, + ); + _ = context.submit_transaction(txn).await.unwrap(); + total_transactions_sent += 1; + } + if let Some(size) = block_size { - total_transactions += size; + total_transactions_committed += size; } num_successful_commits += leaf_chain.len(); @@ -334,39 +346,16 @@ pub trait RunDA< EventType::NextLeaderViewTimeout { view_number } => { warn!("Timed out as the next leader in view {:?}", view_number); } - EventType::ViewFinished { view_number } => { - if *view_number > round { - round = *view_number; - info!("view finished: {:?}", view_number); - for _ in 0..transactions_per_round { - if node_index >= total_nodes_u64 - 10 { - let txn = txns.pop_front().unwrap(); - - debug!("Submitting txn on round {}", round); - - let result = context.submit_transaction(txn).await; - - if result.is_err() { - error! ( - "Could not send transaction to web server on round {}", - round - ) - } - } - } - } - } + EventType::ViewFinished { view_number: _ } => {} _ => unimplemented!(), } } } - - round += 1; } // Output run results let total_time_elapsed = start.elapsed(); - error!("{rounds} rounds completed in {total_time_elapsed:?} - Total transactions committed: {total_transactions} - Total commitments: {num_successful_commits}"); + error!("[{node_index}]: {rounds} rounds completed in {total_time_elapsed:?} - Total transactions sent: {total_transactions_sent} - Total transactions committed: {total_transactions_committed} - Total commitments: {num_successful_commits}"); } /// Returns the da network for this run @@ -408,7 +397,7 @@ pub struct WebServerDARun< #[async_trait] impl< - TYPES: NodeType, + TYPES: NodeType, MEMBERSHIP: Membership + Debug, NODE: NodeImplementation< TYPES, @@ -552,7 +541,7 @@ pub struct Libp2pDARun, MEMBERSHIP #[async_trait] impl< - TYPES: NodeType, + TYPES: NodeType, MEMBERSHIP: Membership + Debug, NODE: NodeImplementation< TYPES, @@ -766,7 +755,7 @@ where /// Main entry point for validators pub async fn main_entry_point< - TYPES: NodeType, + TYPES: NodeType, MEMBERSHIP: Membership + Debug, DANETWORK: CommunicationChannel, MEMBERSHIP> + Debug, QUORUMNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, diff --git a/crates/hotshot/src/demo.rs b/crates/hotshot/src/demo.rs index feaed2fe6b..81cac76f4b 100644 --- a/crates/hotshot/src/demo.rs +++ b/crates/hotshot/src/demo.rs @@ -5,15 +5,13 @@ //! //! These implementations are useful in examples and integration testing, but are not suitable for //! production use. -use crate::{ - block_impl::{BlockPayloadError, VIDBlockPayload, VIDTransaction}, - traits::election::static_committee::{StaticElectionConfig, StaticVoteToken}, -}; +use crate::traits::election::static_committee::{StaticElectionConfig, StaticVoteToken}; use commit::{Commitment, Committable}; use derivative::Derivative; use either::Either; use hotshot_signature_key::bn254::BLSPubKey; use hotshot_types::{ + block_impl::{BlockPayloadError, VIDBlockPayload, VIDTransaction}, certificate::{AssembledSignature, QuorumCertificate}, data::{ fake_commitment, genesis_proposer_id, random_commitment, LeafType, SequencingLeaf, @@ -72,10 +70,6 @@ impl State for SDemoState { type Time = ViewNumber; - fn next_block(_state: Option) -> Self::BlockType { - VIDBlockPayload(Vec::new()) - } - fn validate_block(&self, _block: &Self::BlockType, view_number: &Self::Time) -> bool { if view_number == &ViewNumber::genesis() { &self.view_number == view_number @@ -109,7 +103,9 @@ impl TestableState for SDemoState { _rng: &mut dyn rand::RngCore, padding: u64, ) -> ::Transaction { - VIDTransaction(vec![0; padding as usize]) + /// clippy appeasement for `RANDOM_TX_BASE_SIZE` + const RANDOM_TX_BASE_SIZE: usize = 8; + VIDTransaction(vec![0; RANDOM_TX_BASE_SIZE + (padding as usize)]) } } /// Implementation of [`NodeType`] for [`VDemoNode`] @@ -179,7 +175,7 @@ where /// Provides a random [`QuorumCertificate`] pub fn random_quorum_certificate>( rng: &mut dyn rand::RngCore, -) -> QuorumCertificate { +) -> QuorumCertificate> { QuorumCertificate { // block_commitment: random_commitment(rng), leaf_commitment: random_commitment(rng), diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 06dd78bfc5..81e3292ccb 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -19,7 +19,6 @@ #[cfg(feature = "docs")] pub mod documentation; -pub mod block_impl; /// Contains structures and functions for committee election pub mod certificate; #[cfg(feature = "demo")] @@ -42,7 +41,7 @@ use crate::{ }; use async_compatibility_layer::{ art::{async_spawn, async_spawn_local}, - async_primitives::{broadcast::BroadcastSender, subscribable_rwlock::SubscribableRwLock}, + async_primitives::broadcast::BroadcastSender, channel::UnboundedSender, }; use async_lock::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; @@ -56,6 +55,7 @@ use hotshot_task::{ use hotshot_task_impls::{events::SequencingHotShotEvent, network::NetworkTaskKind}; use hotshot_types::{ + block_impl::{VIDBlockPayload, VIDTransaction}, certificate::{DACertificate, ViewSyncCertificate}, consensus::{BlockStore, Consensus, ConsensusMetricsValue, View, ViewInner, ViewQueue}, data::{DAProposal, DeltasType, LeafType, QuorumProposal, SequencingLeaf}, @@ -82,7 +82,7 @@ use hotshot_types::{ }; use snafu::ResultExt; use std::{ - collections::{BTreeMap, HashMap, HashSet}, + collections::{BTreeMap, HashMap}, marker::PhantomData, num::NonZeroUsize, sync::Arc, @@ -131,11 +131,6 @@ pub struct SystemContextInner> { /// the metrics that the implementor is using. _metrics: Arc, - /// Transactions - /// (this is shared btwn hotshot and `Consensus`) - transactions: - Arc, TYPES::Transaction>>>, - /// The hotstuff implementation consensus: Arc>>, @@ -215,8 +210,6 @@ impl> SystemContext { state_map, cur_view: start_view, last_decided_view: anchored_leaf.get_view_number(), - transactions: Arc::default(), - seen_transactions: HashSet::new(), saved_leaves, saved_blocks, // TODO this is incorrect @@ -226,17 +219,14 @@ impl> SystemContext { metrics: consensus_metrics.clone(), }; let consensus = Arc::new(RwLock::new(consensus)); - let txns = consensus.read().await.get_transactions(); let inner: Arc> = Arc::new(SystemContextInner { id: nonce, channel_maps: I::new_channel_maps(start_view), consensus, - transactions: txns, public_key, private_key, config, - // networking, storage, exchanges: Arc::new(exchanges), event_sender: RwLock::default(), @@ -541,11 +531,6 @@ impl> SystemContext { /// [`HotShot`] implementations that depend on [`TYPES::ConsensusType`]. #[async_trait] pub trait HotShotType> { - /// Get the [`transactions`] field of [`HotShot`]. - fn transactions( - &self, - ) -> &Arc, TYPES::Transaction>>>; - /// Get the [`hotstuff`] field of [`HotShot`]. fn consensus(&self) -> &Arc>>; @@ -645,7 +630,7 @@ pub trait HotShotType> { #[async_trait] impl< - TYPES: NodeType, + TYPES: NodeType, I: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -658,8 +643,8 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, Membership = MEMBERSHIP, > + 'static, CommitteeEx: ConsensusExchange< @@ -667,7 +652,7 @@ where Message, Proposal = DAProposal, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, Membership = MEMBERSHIP, > + 'static, ViewSyncEx: ConsensusExchange< @@ -675,16 +660,10 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, Membership = MEMBERSHIP, > + 'static, { - fn transactions( - &self, - ) -> &Arc, TYPES::Transaction>>> { - &self.inner.transactions - } - fn consensus(&self) -> &Arc>> { &self.inner.consensus } @@ -1076,7 +1055,7 @@ impl> HotShotInitializer::genesis(); + let justify_qc = QuorumCertificate::>::genesis(); Ok(Self { inner: LEAF::new(time, justify_qc, genesis_block, state), diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index 619fdb6d59..66f9fb20da 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -5,7 +5,7 @@ use crate::{ QuorumCertificate, SequencingQuorumEx, }; use async_compatibility_layer::art::async_sleep; -use commit::Committable; +use commit::{Commitment, CommitmentBounds}; use futures::FutureExt; use hotshot_task::{ boxed_sync, @@ -27,6 +27,7 @@ use hotshot_task_impls::{ view_sync::{ViewSyncTaskState, ViewSyncTaskStateTypes}, }; use hotshot_types::{ + block_impl::{VIDBlockPayload, VIDTransaction}, certificate::ViewSyncCertificate, data::{ProposalType, QuorumProposal, SequencingLeaf}, event::Event, @@ -38,12 +39,15 @@ use hotshot_types::{ CommitteeEx, ExchangesType, NodeImplementation, NodeType, ViewSyncEx, }, state::ConsensusTime, - BlockPayload, }, vote::{ViewSyncData, VoteType}, }; -use serde::Serialize; -use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; +use std::{ + collections::{HashMap, HashSet}, + marker::PhantomData, + sync::Arc, + time::Duration, +}; /// event for global event stream #[derive(Clone, Debug)] @@ -64,9 +68,9 @@ pub async fn add_network_message_task< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, - COMMITTABLE: Committable + Serialize + Clone, + COMMITMENT: CommitmentBounds, PROPOSAL: ProposalType, - VOTE: VoteType, + VOTE: VoteType, MEMBERSHIP: Membership, EXCHANGE: ConsensusExchange< TYPES, @@ -174,9 +178,9 @@ pub async fn add_network_event_task< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, - COMMITTABLE: Committable + Serialize + Clone, + COMMITMENT: CommitmentBounds, PROPOSAL: ProposalType, - VOTE: VoteType, + VOTE: VoteType, MEMBERSHIP: Membership, EXCHANGE: ConsensusExchange< TYPES, @@ -246,7 +250,7 @@ where /// # Panics /// Is unable to panic. This section here is just to satisfy clippy pub async fn add_consensus_task< - TYPES: NodeType, + TYPES: NodeType, I: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -263,14 +267,14 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, CommitteeEx: ConsensusExchange< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { let consensus = handle.hotshot.get_consensus(); @@ -284,7 +288,7 @@ where consensus, timeout: handle.hotshot.inner.config.next_view_timeout, cur_view: TYPES::Time::new(0), - block: TYPES::BlockType::new(), + block: VIDBlockPayload::genesis(), quorum_exchange: c_api.inner.exchanges.quorum_exchange().clone().into(), api: c_api.clone(), committee_exchange: c_api.inner.exchanges.committee_exchange().clone().into(), @@ -360,7 +364,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { // build the da task @@ -412,7 +416,7 @@ where /// # Panics /// Is unable to panic. This section here is just to satisfy clippy pub async fn add_transaction_task< - TYPES: NodeType, + TYPES: NodeType, I: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -429,7 +433,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { // build the transactions task @@ -441,6 +445,8 @@ where registry: registry.clone(), api: c_api.clone(), consensus: handle.hotshot.get_consensus(), + transactions: Arc::default(), + seen_transactions: HashSet::new(), cur_view: TYPES::Time::new(0), committee_exchange: committee_exchange.into(), event_stream: event_stream.clone(), @@ -501,7 +507,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { let api = HotShotSequencingConsensusApi { diff --git a/crates/hotshot/src/traits/networking/libp2p_network.rs b/crates/hotshot/src/traits/networking/libp2p_network.rs index c7259ec23f..20e6718955 100644 --- a/crates/hotshot/src/traits/networking/libp2p_network.rs +++ b/crates/hotshot/src/traits/networking/libp2p_network.rs @@ -4,14 +4,14 @@ use super::NetworkingMetricsValue; use crate::NodeImplementation; use async_compatibility_layer::{ - art::{async_block_on, async_sleep, async_spawn, async_timeout}, + art::{async_block_on, async_sleep, async_spawn}, channel::{unbounded, UnboundedReceiver, UnboundedSendError, UnboundedSender}, }; use async_lock::RwLock; use async_trait::async_trait; use bimap::BiHashMap; use bincode::Options; -use hotshot_constants::{KAD_DEFAULT_REPUB_INTERVAL_SEC, LOOK_AHEAD}; +use hotshot_constants::LOOK_AHEAD; use hotshot_task::{boxed_sync, BoxSyncFuture}; use hotshot_types::{ data::ViewNumber, @@ -81,7 +81,7 @@ struct Libp2pNetworkInner { /// this node's public key pk: K, /// handle to control the network - handle: Arc>, + handle: Arc>, /// map of known replica peer ids to public keys broadcast_recv: UnboundedReceiver, /// Sender for broadcast messages @@ -92,8 +92,6 @@ struct Libp2pNetworkInner { direct_recv: UnboundedReceiver, /// Sender for node lookup (relevant view number, key of node) (None for shutdown) node_lookup_send: UnboundedSender>, - /// Sender for shutdown of the peer cache's garbage collection task - cache_gc_shutdown_send: UnboundedSender<()>, /// this is really cheating to enable local tests /// hashset of (bootstrap_addr, peer_id) bootstrap_addrs: PeerInfoVec, @@ -286,7 +284,7 @@ impl Libp2pNetwork { ) -> Result, NetworkError> { assert!(bootstrap_addrs_len > 4, "Need at least 5 bootstrap nodes"); let network_handle = Arc::new( - NetworkNodeHandle::<(), K>::new(config, id) + Box::pin(NetworkNodeHandle::<()>::new(config, id)) .await .map_err(Into::::into)?, ); @@ -317,7 +315,6 @@ impl Libp2pNetwork { let (direct_send, direct_recv) = unbounded(); let (broadcast_send, broadcast_recv) = unbounded(); let (node_lookup_send, node_lookup_recv) = unbounded(); - let (cache_gc_shutdown_send, cache_gc_shutdown_recv) = unbounded::<()>(); let mut result = Libp2pNetwork { inner: Arc::new(Libp2pNetworkInner { @@ -335,7 +332,6 @@ impl Libp2pNetwork { metrics, topic_map, node_lookup_send, - cache_gc_shutdown_send, // Start the latest view from 0. "Latest" refers to "most recent view we are polling for // proposals on". We need this because to have consensus info injected we need a working // network already. In the worst case, we send a few lookups we don't need. @@ -344,20 +340,15 @@ impl Libp2pNetwork { }; result.spawn_event_generator(direct_send, broadcast_send); - result.spawn_node_lookup(node_lookup_recv, cache_gc_shutdown_recv); + result.spawn_node_lookup(node_lookup_recv); result.spawn_connect(id); Ok(result) } /// Spawns task for looking up nodes pre-emptively - /// as well as garbage collecting the peer cache #[allow(clippy::cast_sign_loss, clippy::cast_precision_loss)] - fn spawn_node_lookup( - &self, - node_lookup_recv: UnboundedReceiver>, - cache_gc_shutdown_send: UnboundedReceiver<()>, - ) { + fn spawn_node_lookup(&self, node_lookup_recv: UnboundedReceiver>) { let handle = self.inner.handle.clone(); let dht_timeout = self.inner.dht_timeout; let latest_seen_view = self.inner.latest_seen_view.clone(); @@ -374,32 +365,13 @@ impl Libp2pNetwork { // only run if we are not too close to the next view number if latest_seen_view.load(Ordering::Relaxed) + THRESHOLD <= *view_number { - // look up node, caching if applicable + // look up if let Err(err) = handle_.lookup_node::(pk.clone(), dht_timeout).await { error!("Failed to perform lookup for key {:?}: {}", pk, err); }; } } }); - - // deals with garbage collecting the lookup queue - let handle_ = handle.clone(); - async_spawn(async move { - loop { - let ttl = handle_ - .config() - .ttl - .unwrap_or(Duration::from_secs(KAD_DEFAULT_REPUB_INTERVAL_SEC * 8)); - if async_timeout(ttl, cache_gc_shutdown_send.recv()) - .await - .is_err() - { - handle_.prune_peer_cache().await; - } else { - break; - } - } - }); } /// Initiates connection to the outside world @@ -579,7 +551,6 @@ impl ConnectedNetwork for Libp2p { let closure = async move { self.inner.node_lookup_send.send(None).await.unwrap(); - self.inner.cache_gc_shutdown_send.send(()).await.unwrap(); if self.inner.handle.is_killed() { error!("Called shut down when already shut down! Noop."); } else { diff --git a/crates/hotshot/src/traits/networking/memory_network.rs b/crates/hotshot/src/traits/networking/memory_network.rs index 5fa6530680..1a9c2bcb8e 100644 --- a/crates/hotshot/src/traits/networking/memory_network.rs +++ b/crates/hotshot/src/traits/networking/memory_network.rs @@ -580,359 +580,3 @@ impl, MEMBERSHIP: Membership; - // type Transaction = VDemoTransaction; - // type ElectionConfigType = StaticElectionConfig; - // type StateType = VDemoState; - // } - // - // type TestMembership = GeneralStaticCommittee; - // type TestNetwork = MemoryCommChannel; - // - // impl NodeImplementation for TestImpl { - // type ConsensusMessage = ValidatingMessage; - // type Exchanges = ValidatingExchanges< - // Test, - // Message, - // QuorumExchange< - // Test, - // TestLeaf, - // TestProposal, - // TestMembership, - // TestNetwork, - // Message, - // >, - // ViewSyncExchange>, - // >; - // type Leaf = TestLeaf; - // type Storage = MemoryStorage; - // - // fn new_channel_maps( - // start_view: ViewNumber, - // ) -> (ChannelMaps, Option>) { - // (ChannelMaps::new(start_view), None) - // } - // } - // - // type TestLeaf = ValidatingLeaf; - // type TestVote = QuorumVote; - // type TestProposal = ValidatingProposal; - // - // /// fake Eq - // /// we can't compare the votetokentype for equality, so we can't - // /// derive EQ on `VoteType` and thereby message - // /// we are only sending data messages, though so we compare key and - // /// data message - // fn fake_message_eq(message_1: Message, message_2: Message) { - // assert_eq!(message_1.sender, message_2.sender); - // if let MessageKind::Data(DataMessage::SubmitTransaction(d_1, _)) = message_1.kind { - // if let MessageKind::Data(DataMessage::SubmitTransaction(d_2, _)) = message_2.kind { - // assert_eq!(d_1, d_2); - // } - // } else { - // panic!("Got unexpected message type in memory test!"); - // } - // } - // - // #[instrument] - // fn get_pubkey() -> Ed25519Pub { - // let priv_key = Ed25519Priv::generate(); - // Ed25519Pub::from_private(&priv_key) - // } - // - // /// create a message - // fn gen_messages(num_messages: u64, seed: u64, pk: Ed25519Pub) -> Vec> { - // let mut messages = Vec::new(); - // for i in 0..num_messages { - // let message = Message { - // sender: pk, - // kind: MessageKind::Data(DataMessage::SubmitTransaction( - // VDemoTransaction { - // add: Addition { - // account: "A".to_string(), - // amount: 50 + i + seed, - // }, - // sub: Subtraction { - // account: "B".to_string(), - // amount: 50 + i + seed, - // }, - // nonce: seed + i, - // padding: vec![50; 0], - // }, - // ::new(0), - // )), - // _phantom: PhantomData, - // }; - // messages.push(message); - // } - // messages - // } - // - // // Spawning a single MemoryNetwork should produce no errors - // #[cfg_attr( - // feature = "tokio-executor", - // tokio::test(flavor = "multi_thread", worker_threads = 2) - // )] - // #[cfg_attr(feature = "async-std-executor", async_std::test)] - // #[instrument] - // async fn spawn_single() { - // setup_logging(); - // let group: Arc, ::SignatureKey>> = - // MasterMap::new(); - // trace!(?group); - // let pub_key = get_pubkey(); - // let _network = MemoryNetwork::new(pub_key, NoMetrics::boxed(), group, Option::None); - // } - // - // // // Spawning a two MemoryNetworks and connecting them should produce no errors - // #[cfg_attr( - // feature = "tokio-executor", - // tokio::test(flavor = "multi_thread", worker_threads = 2) - // )] - // #[cfg_attr(feature = "async-std-executor", async_std::test)] - // #[instrument] - // async fn spawn_double() { - // setup_logging(); - // let group: Arc, ::SignatureKey>> = - // MasterMap::new(); - // trace!(?group); - // let pub_key_1 = get_pubkey(); - // let _network_1 = - // MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); - // let pub_key_2 = get_pubkey(); - // let _network_2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); - // } - // - // // Check to make sure direct queue works - // #[cfg_attr( - // feature = "tokio-executor", - // tokio::test(flavor = "multi_thread", worker_threads = 2) - // )] - // #[cfg_attr(feature = "async-std-executor", async_std::test)] - // #[allow(deprecated)] - // #[instrument] - // async fn direct_queue() { - // setup_logging(); - // // Create some dummy messages - // - // // Make and connect the networking instances - // let group: Arc, ::SignatureKey>> = - // MasterMap::new(); - // trace!(?group); - // let pub_key_1 = get_pubkey(); - // let network1 = - // MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); - // let pub_key_2 = get_pubkey(); - // let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); - // - // let first_messages: Vec> = gen_messages(5, 100, pub_key_1); - // - // // Test 1 -> 2 - // // Send messages - // for sent_message in first_messages { - // network1 - // .direct_message(sent_message.clone(), pub_key_2) - // .await - // .expect("Failed to message node"); - // let mut recv_messages = network2 - // .recv_msgs(TransmitType::Direct) - // .await - // .expect("Failed to receive message"); - // let recv_message = recv_messages.pop().unwrap(); - // assert!(recv_messages.is_empty()); - // fake_message_eq(sent_message, recv_message); - // } - // - // let second_messages: Vec> = gen_messages(5, 200, pub_key_2); - // - // // Test 2 -> 1 - // // Send messages - // for sent_message in second_messages { - // network2 - // .direct_message(sent_message.clone(), pub_key_1) - // .await - // .expect("Failed to message node"); - // let mut recv_messages = network1 - // .recv_msgs(TransmitType::Direct) - // .await - // .expect("Failed to receive message"); - // let recv_message = recv_messages.pop().unwrap(); - // assert!(recv_messages.is_empty()); - // fake_message_eq(sent_message, recv_message); - // } - // } - // - // // Check to make sure direct queue works - // #[cfg_attr( - // feature = "tokio-executor", - // tokio::test(flavor = "multi_thread", worker_threads = 2) - // )] - // #[cfg_attr(feature = "async-std-executor", async_std::test)] - // #[allow(deprecated)] - // #[instrument] - // async fn broadcast_queue() { - // setup_logging(); - // // Make and connect the networking instances - // let group: Arc, ::SignatureKey>> = - // MasterMap::new(); - // trace!(?group); - // let pub_key_1 = get_pubkey(); - // let network1 = - // MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); - // let pub_key_2 = get_pubkey(); - // let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); - // - // let first_messages: Vec> = gen_messages(5, 100, pub_key_1); - // - // // Test 1 -> 2 - // // Send messages - // for sent_message in first_messages { - // network1 - // .broadcast_message( - // sent_message.clone(), - // vec![pub_key_2].into_iter().collect::>(), - // ) - // .await - // .expect("Failed to message node"); - // let mut recv_messages = network2 - // .recv_msgs(TransmitType::Broadcast) - // .await - // .expect("Failed to receive message"); - // let recv_message = recv_messages.pop().unwrap(); - // assert!(recv_messages.is_empty()); - // fake_message_eq(sent_message, recv_message); - // } - // - // let second_messages: Vec> = gen_messages(5, 200, pub_key_2); - // - // // Test 2 -> 1 - // // Send messages - // for sent_message in second_messages { - // network2 - // .broadcast_message( - // sent_message.clone(), - // vec![pub_key_1].into_iter().collect::>(), - // ) - // .await - // .expect("Failed to message node"); - // let mut recv_messages = network1 - // .recv_msgs(TransmitType::Broadcast) - // .await - // .expect("Failed to receive message"); - // let recv_message = recv_messages.pop().unwrap(); - // assert!(recv_messages.is_empty()); - // fake_message_eq(sent_message, recv_message); - // } - // } - // - // #[cfg_attr( - // feature = "tokio-executor", - // tokio::test(flavor = "multi_thread", worker_threads = 2) - // )] - // #[cfg_attr(feature = "async-std-executor", async_std::test)] - // #[instrument] - // #[allow(deprecated)] - // async fn test_in_flight_message_count() { - // // setup_logging(); - // - // // let group: Arc, ::SignatureKey>> = - // // MasterMap::new(); - // // trace!(?group); - // // let pub_key_1 = get_pubkey(); - // // let network1 = - // // MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); - // // let pub_key_2 = get_pubkey(); - // // let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); - // - // // // Create some dummy messages - // // let messages: Vec> = gen_messages(5, 100, pub_key_1); - // - // // // assert_eq!(network1.in_flight_message_count(), Some(0)); - // // // assert_eq!(network2.in_flight_message_count(), Some(0)); - // - // // for (_count, message) in messages.iter().enumerate() { - // // network1 - // // .direct_message(message.clone(), pub_key_2) - // // .await - // // .unwrap(); - // // // network 2 has received `count` broadcast messages and `count + 1` direct messages - // // // assert_eq!(network2.in_flight_message_count(), Some(count + count + 1)); - // - // // // network2.broadcast_message(message.clone()).await.unwrap(); - // // // network 1 has received `count` broadcast messages - // // // assert_eq!(network1.in_flight_message_count(), Some(count + 1)); - // - // // // network 2 has received `count + 1` broadcast messages and `count + 1` direct messages - // // // assert_eq!(network2.in_flight_message_count(), Some((count + 1) * 2)); - // // } - // - // // for _count in (0..messages.len()).rev() { - // // network1.recv_msgs(TransmitType::Broadcast).await.unwrap(); - // // // assert_eq!(network1.in_flight_message_count(), Some(count)); - // - // // network2.recv_msgs(TransmitType::Broadcast).await.unwrap(); - // // network2.recv_msgs(TransmitType::Direct).await.unwrap(); - // // // assert_eq!(network2.in_flight_message_count(), Some(count * 2)); - // // } - // - // // // assert_eq!(network1.in_flight_message_count(), Some(0)); - // // // assert_eq!(network2.in_flight_message_count(), Some(0)); - // } -} diff --git a/crates/hotshot/src/types/handle.rs b/crates/hotshot/src/types/handle.rs index 5b8e737c1a..35e79a78dd 100644 --- a/crates/hotshot/src/types/handle.rs +++ b/crates/hotshot/src/types/handle.rs @@ -190,7 +190,7 @@ impl + 'static> SystemContextHandl if let Ok(anchor_leaf) = self.storage().get_anchored_view().await { if anchor_leaf.view_number == TYPES::Time::genesis() { let leaf: I::Leaf = I::Leaf::from_stored_view(anchor_leaf); - let mut qc = QuorumCertificate::::genesis(); + let mut qc = QuorumCertificate::>::genesis(); qc.set_leaf_commitment(leaf.commit()); let event = Event { view_number: TYPES::Time::genesis(), @@ -226,7 +226,7 @@ impl + 'static> SystemContextHandl // ) -> Result< // ( // Vec<>::Leaf>, - // QuorumCertificate>::Leaf>, + // QuorumCertificate>::Leaf>>, // ), // HotShotError, // > { @@ -333,7 +333,7 @@ impl + 'static> SystemContextHandl #[cfg(feature = "hotshot-testing")] pub fn create_yes_message( &self, - justify_qc_commitment: Commitment>, + justify_qc_commitment: Commitment>>, leaf_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, @@ -342,7 +342,7 @@ impl + 'static> SystemContextHandl QuorumEx: ConsensusExchange< TYPES, Message, - Certificate = QuorumCertificate, + Certificate = QuorumCertificate>, >, { let inner = self.hotshot.inner.clone(); diff --git a/crates/libp2p-networking/src/network/behaviours/dht/cache.rs b/crates/libp2p-networking/src/network/behaviours/dht/cache.rs new file mode 100644 index 0000000000..602bb41e16 --- /dev/null +++ b/crates/libp2p-networking/src/network/behaviours/dht/cache.rs @@ -0,0 +1,325 @@ +use std::{ + collections::{BTreeMap, HashMap}, + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, + time::{Duration, SystemTime}, +}; + +use async_compatibility_layer::art::async_block_on; +use async_lock::RwLock; +use bincode::Options; +use dashmap::{mapref::one::Ref, DashMap}; +use hotshot_constants::KAD_DEFAULT_REPUB_INTERVAL_SEC; +use hotshot_utils::bincode::bincode_opts; +use snafu::{ResultExt, Snafu}; + +/// Error wrapper type for cache +#[derive(Debug, Snafu)] +#[snafu(visibility(pub))] +pub enum CacheError { + /// Failed to read or write from disk + Disk { + /// source of error + source: std::io::Error, + }, + + /// Failure to serialize the cache + Serialization { + /// source of error + source: Box, + }, + + /// Failure to deserialize the cache + Deserialization { + /// source of error + source: Box, + }, + + /// General cache error + GeneralCache { + /// source of error + source: Box, + }, +} + +#[derive(Clone, derive_builder::Builder, custom_debug::Debug, Default)] +pub struct Config { + #[builder(default = "Some(\"dht.cache\".to_string())")] + pub filename: Option, + #[builder(default = "Duration::from_secs(KAD_DEFAULT_REPUB_INTERVAL_SEC * 16)")] + pub expiry: Duration, + #[builder(default = "4")] + pub max_disk_parity_delta: u32, +} + +impl Default for Cache { + fn default() -> Self { + async_block_on(Self::new(Config::default())) + } +} + +pub struct Cache { + /// the cache's config + config: Config, + + /// the cache for records (key -> value) + cache: Arc, Vec>>, + /// the expiries for the dht cache, in order (expiry time -> key) + expiries: Arc>>>, + + /// number of inserts since the last save + disk_parity_delta: Arc, +} + +impl Cache { + pub async fn new(config: Config) -> Self { + let cache = Self { + cache: Arc::new(DashMap::new()), + expiries: Arc::new(RwLock::new(BTreeMap::new())), + config, + disk_parity_delta: Arc::new(AtomicU32::new(0)), + }; + + // try loading from file + if let Err(err) = cache.load().await { + tracing::warn!("failed to load cache from file: {}", err); + }; + + cache + } + + pub async fn load(&self) -> Result<(), CacheError> { + if let Some(filename) = &self.config.filename { + let encoded = std::fs::read(filename).context(DiskSnafu)?; + + let cache: HashMap, Vec)> = bincode_opts() + .deserialize(&encoded) + .context(DeserializationSnafu)?; + + // inline prune and insert + let now = SystemTime::now(); + for (expiry, (key, value)) in cache { + if now < expiry { + self.cache.insert(key.clone(), value); + self.expiries.write().await.insert(expiry, key); + } + } + } + + Ok(()) + } + + pub async fn save(&self) -> Result<(), CacheError> { + if let Some(filename) = &self.config.filename { + // prune first + self.prune().await; + + // serialize + let mut cache_to_write = HashMap::new(); + let expiries = self.expiries.read().await; + for (expiry, key) in &*expiries { + if let Some(entry) = self.cache.get(key) { + cache_to_write.insert(expiry, (key, entry.value().clone())); + } else { + tracing::warn!("key not found in cache: {:?}", key); + Err(CacheError::GeneralCache { + source: Box::new(bincode::ErrorKind::Custom( + "key not found in cache".to_string(), + )), + })?; + }; + } + + let encoded = bincode_opts() + .serialize(&cache_to_write) + .context(SerializationSnafu)?; + + std::fs::write(filename, encoded).context(DiskSnafu)?; + } + + Ok(()) + } + + async fn prune(&self) { + let now = SystemTime::now(); + let mut expiries = self.expiries.write().await; + let mut removed: u32 = 0; + + while let Some((expires, key)) = expiries.pop_first() { + if now > expires { + self.cache.remove(&key); + removed += 1; + } else { + expiries.insert(expires, key); + break; + } + } + + if removed > 0 { + self.disk_parity_delta.fetch_add(removed, Ordering::Relaxed); + } + } + + pub async fn get(&self, key: &Vec) -> Option, Vec>> { + // prune, save if necessary + self.prune().await; + self.save_if_necessary().await; + + // get + self.cache.get(key) + } + + pub async fn insert(&self, key: Vec, value: Vec) { + // insert into cache and expiries + self.cache.insert(key.clone(), value); + self.expiries + .write() + .await + .insert(SystemTime::now() + self.config.expiry, key); + + // save if reached max disk parity delta + self.disk_parity_delta.fetch_add(1, Ordering::Relaxed); + self.save_if_necessary().await; + } + + async fn save_if_necessary(&self) { + let cur_disk_parity_delta = self.disk_parity_delta.load(Ordering::Relaxed); + if cur_disk_parity_delta >= self.config.max_disk_parity_delta { + if let Err(err) = self.save().await { + tracing::error!("failed to save cache to file: {}", err); + }; + } + } +} + +#[cfg(test)] +mod test { + + use super::*; + use async_compatibility_layer::art::async_sleep; + use libp2p_identity::PeerId; + use tracing::instrument; + + /// cache eviction test + #[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) + )] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + #[instrument] + async fn test_dht_cache_eviction() { + async_compatibility_layer::logging::setup_logging(); + async_compatibility_layer::logging::setup_backtrace(); + + // cache with 1s eviction + let cache = Cache::new(Config { + filename: None, + expiry: Duration::from_secs(1), + max_disk_parity_delta: 4, + }) + .await; + + let (key, value) = (PeerId::random(), PeerId::random()); + + // insert + cache.insert(key.to_bytes(), value.to_bytes()).await; + + // check that it is in the cache and expiries + assert_eq!( + cache.get(&key.to_bytes()).await.unwrap().value(), + &value.to_bytes() + ); + assert_eq!(cache.expiries.read().await.len(), 1); + + // sleep for 1s + async_sleep(Duration::from_secs(1)).await; + + // check that now is evicted + assert!(cache.get(&key.to_bytes()).await.is_none()); + + // check that the cache and expiries are empty + assert!(cache.expiries.read().await.is_empty()); + assert!(cache.cache.is_empty()); + } + + /// cache add test + #[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) + )] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + #[instrument] + async fn test_dht_cache_save_load() { + let _ = std::fs::remove_file("test.cache"); + + let cache = Cache::new(Config { + filename: Some("test.cache".to_string()), + expiry: Duration::from_secs(600), + max_disk_parity_delta: 4, + }) + .await; + + // add 10 key-value pairs to the cache + for i in 0u8..10u8 { + let (key, value) = (vec![i; 1], vec![i + 1; 1]); + cache.insert(key, value).await; + } + + // save the cache + cache.save().await.unwrap(); + + // load the cache + let cache = Cache::new(Config { + filename: Some("test.cache".to_string()), + expiry: Duration::from_secs(600), + max_disk_parity_delta: 4, + }) + .await; + + // check that the cache has the 10 key-value pairs + for i in 0u8..10u8 { + let (key, value) = (vec![i; 1], vec![i + 1; 1]); + assert_eq!(cache.get(&key).await.unwrap().value(), &value); + } + + // delete the cache file + let _ = std::fs::remove_file("test.cache"); + } + + #[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) + )] + #[cfg_attr(async_executor_impl = "async-std", async_std::test)] + #[instrument] + async fn test_dht_disk_parity() { + let _ = std::fs::remove_file("test.cache"); + + let cache = Cache::new(Config { + // tests run sequentially, shouldn't matter + filename: Some("test.cache".to_string()), + expiry: Duration::from_secs(600), + max_disk_parity_delta: 4, + }) + .await; + + // insert into cache + for i in 0..3 { + cache.insert(vec![i; 1], vec![i + 1; 1]).await; + } + + // check that file is not saved + assert!(!std::path::Path::new("test.cache").exists()); + + // insert into cache + cache.insert(vec![0; 1], vec![1; 1]).await; + + // check that file is saved + assert!(std::path::Path::new("test.cache").exists()); + + // delete the cache file + _ = std::fs::remove_file("test.cache"); + } +} diff --git a/crates/libp2p-networking/src/network/behaviours/dht.rs b/crates/libp2p-networking/src/network/behaviours/dht/mod.rs similarity index 94% rename from crates/libp2p-networking/src/network/behaviours/dht.rs rename to crates/libp2p-networking/src/network/behaviours/dht/mod.rs index 46adb05d0c..7086b6dab1 100644 --- a/crates/libp2p-networking/src/network/behaviours/dht.rs +++ b/crates/libp2p-networking/src/network/behaviours/dht/mod.rs @@ -5,6 +5,9 @@ use std::{ time::Duration, }; +mod cache; + +use async_compatibility_layer::art::async_block_on; use futures::channel::oneshot::Sender; use libp2p::{ kad::{ @@ -21,6 +24,8 @@ use tracing::{error, info, warn}; pub(crate) const NUM_REPLICATED_TO_TRUST: usize = 2; const MAX_DHT_QUERY_SIZE: usize = 5; +use self::cache::Cache; + use super::exponential_backoff::ExponentialBackoff; /// Behaviour wrapping libp2p's kademlia @@ -56,6 +61,8 @@ pub struct DHTBehaviour { pub peer_id: PeerId, /// replication factor pub replication_factor: NonZeroUsize, + /// kademlia cache + cache: Cache, } /// State of bootstrapping @@ -106,10 +113,11 @@ impl DHTBehaviour { /// Create a new DHT behaviour #[must_use] - pub fn new( + pub async fn new( mut kadem: Kademlia, pid: PeerId, replication_factor: NonZeroUsize, + cache_location: Option, ) -> Self { // needed because otherwise we stay in client mode when testing locally // and don't publish keys stuff @@ -138,6 +146,13 @@ impl DHTBehaviour { }, in_progress_get_closest_peers: HashMap::default(), replication_factor, + cache: Cache::new( + cache::ConfigBuilder::default() + .filename(cache_location) + .build() + .unwrap_or_default(), + ) + .await, } } @@ -223,17 +238,26 @@ impl DHTBehaviour { return; } - let qid = self.kadem.get_record(key.clone().into()); - let query = KadGetQuery { - backoff, - progress: DHTProgress::InProgress(qid), - notify: chan, - num_replicas: factor, - key, - retry_count: retry_count - 1, - records: HashMap::default(), - }; - self.in_progress_get_record_queries.insert(qid, query); + // check cache before making the request + if let Some(entry) = async_block_on(self.cache.get(&key)) { + // exists in cache + if chan.send(entry.value().clone()).is_err() { + warn!("Get DHT: channel closed before get record request result could be sent"); + } + } else { + // doesn't exist in cache, actually propagate request + let qid = self.kadem.get_record(key.clone().into()); + let query = KadGetQuery { + backoff, + progress: DHTProgress::InProgress(qid), + notify: chan, + num_replicas: factor, + key, + retry_count: retry_count - 1, + records: HashMap::default(), + }; + self.in_progress_get_record_queries.insert(qid, query); + } } /// update state based on recv-ed get query @@ -279,6 +303,10 @@ impl DHTBehaviour { .into_iter() .find(|(_, v)| *v >= NUM_REPLICATED_TO_TRUST) { + // insert into cache + async_block_on(self.cache.insert(key, r.clone())); + + // return value if notify.send(r).is_err() { warn!("Get DHT: channel closed before get record request result could be sent"); } diff --git a/crates/libp2p-networking/src/network/mod.rs b/crates/libp2p-networking/src/network/mod.rs index 54b3d89035..3ec1c07b73 100644 --- a/crates/libp2p-networking/src/network/mod.rs +++ b/crates/libp2p-networking/src/network/mod.rs @@ -34,7 +34,7 @@ use libp2p::{ use libp2p_identity::PeerId; use rand::seq::IteratorRandom; use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, fmt::Debug, hash::Hash, str::FromStr, sync::Arc, time::Duration}; +use std::{collections::HashSet, fmt::Debug, str::FromStr, sync::Arc, time::Duration}; use tracing::{info, instrument}; #[cfg(async_executor_impl = "async-std")] @@ -229,12 +229,12 @@ pub async fn gen_transport( /// a single node, connects them to each other /// and waits for connections to propagate to all nodes. #[instrument] -pub async fn spin_up_swarm( +pub async fn spin_up_swarm( timeout_len: Duration, known_nodes: Vec<(Option, Multiaddr)>, config: NetworkNodeConfig, idx: usize, - handle: &Arc>, + handle: &Arc>, ) -> Result<(), NetworkNodeHandleError> { info!("known_nodes{:?}", known_nodes); handle.add_known_peers(known_nodes).await?; @@ -248,9 +248,9 @@ pub async fn spin_up_swarm( - handles: &[Arc>], +pub fn get_random_handle( + handles: &[Arc>], rng: &mut dyn rand::RngCore, -) -> Arc> { +) -> Arc> { handles.iter().choose(rng).unwrap().clone() } diff --git a/crates/libp2p-networking/src/network/node.rs b/crates/libp2p-networking/src/network/node.rs index 2f7dd46ba4..835058bf84 100644 --- a/crates/libp2p-networking/src/network/node.rs +++ b/crates/libp2p-networking/src/network/node.rs @@ -269,7 +269,9 @@ impl NetworkNode { config .replication_factor .unwrap_or_else(|| NonZeroUsize::new(4).unwrap()), - ), + config.dht_cache_location.clone(), + ) + .await, identify, DMBehaviour::new(request_response), ); diff --git a/crates/libp2p-networking/src/network/node/config.rs b/crates/libp2p-networking/src/network/node/config.rs index 4d19b6f516..d97097e8e1 100644 --- a/crates/libp2p-networking/src/network/node/config.rs +++ b/crates/libp2p-networking/src/network/node/config.rs @@ -24,6 +24,10 @@ pub struct NetworkNodeConfig { #[builder(setter(into, strip_option), default = "DEFAULT_REPLICATION_FACTOR")] pub replication_factor: Option, + /// location of the dht cache, default is None + #[builder(default = "None")] + pub dht_cache_location: Option, + #[builder(default)] /// parameters for gossipsub mesh network pub mesh_params: Option, diff --git a/crates/libp2p-networking/src/network/node/handle.rs b/crates/libp2p-networking/src/network/node/handle.rs index 04961140c6..c4e6460666 100644 --- a/crates/libp2p-networking/src/network/node/handle.rs +++ b/crates/libp2p-networking/src/network/node/handle.rs @@ -11,25 +11,22 @@ use async_compatibility_layer::{ UnboundedReceiver, UnboundedRecvError, UnboundedSender, }, }; -use async_lock::{Mutex, RwLock}; +use async_lock::Mutex; use bincode::Options; -use dashmap::DashMap; use futures::{stream::FuturesOrdered, Future, FutureExt}; -use hotshot_constants::KAD_DEFAULT_REPUB_INTERVAL_SEC; use hotshot_utils::bincode::bincode_opts; use libp2p::{request_response::ResponseChannel, Multiaddr}; use libp2p_identity::PeerId; use serde::{Deserialize, Serialize}; use snafu::{ResultExt, Snafu}; use std::{ - collections::{BTreeMap, HashSet}, + collections::HashSet, fmt::Debug, - hash::Hash, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, - time::{Duration, Instant, SystemTime}, + time::{Duration, Instant}, }; use tracing::{debug, info, instrument}; @@ -37,7 +34,7 @@ use tracing::{debug, info, instrument}; /// - A reference to the state /// - Controls for the swarm #[derive(Debug)] -pub struct NetworkNodeHandle { +pub struct NetworkNodeHandle { /// network configuration network_config: NetworkNodeConfig, /// the state of the replica @@ -51,10 +48,6 @@ pub struct NetworkNodeHandle { peer_id: PeerId, /// human readable id id: usize, - /// the cache for peers we've looked up - peer_cache: Arc>, - /// the expiries for the peer cache, in order - peer_cache_expiries: Arc>>, /// A list of webui listeners that are listening for changes on this node webui_listeners: Arc>>>, @@ -91,7 +84,7 @@ impl NetworkNodeReceiver { } } -impl NetworkNodeHandle { +impl NetworkNodeHandle { /// constructs a new node listening on `known_addr` #[instrument] pub async fn new(config: NetworkNodeConfig, id: usize) -> Result { @@ -126,8 +119,6 @@ impl NetworkNodeHa listen_addr, peer_id, id, - peer_cache: Arc::new(DashMap::new()), - peer_cache_expiries: Arc::new(RwLock::new(BTreeMap::new())), webui_listeners: Arc::default(), receiver: NetworkNodeReceiver { kill_switch, @@ -147,10 +138,9 @@ impl NetworkNodeHa #[allow(clippy::unused_async)] pub async fn spawn_handler(self: &Arc, cb: F) -> impl Future where - F: Fn(NetworkEvent, Arc>) -> RET + Sync + Send + 'static, + F: Fn(NetworkEvent, Arc>) -> RET + Sync + Send + 'static, RET: Future> + Send + 'static, S: Send + 'static, - K: Send + Sync + 'static, { assert!( !self.receiver.receiver_spawned.swap(true, Ordering::Relaxed), @@ -269,7 +259,7 @@ impl NetworkNodeHa } } -impl NetworkNodeHandle { +impl NetworkNodeHandle { /// Print out the routing table used by kademlia /// NOTE: only for debugging purposes currently /// # Errors @@ -292,48 +282,16 @@ impl NetworkNodeHandle { r.await.map_err(|_| NetworkNodeHandleError::RecvError) } - /// Prunes the peer lookup cache, removing old entries - /// Should be 1:1 with kademlia expiries - pub async fn prune_peer_cache(&self) { - let now = SystemTime::now(); - let mut expiries = self.peer_cache_expiries.write().await; - - while let Some((expires, key)) = expiries.pop_first() { - if now > expires { - self.peer_cache.remove(&key); - } else { - expiries.insert(expires, key); - break; - } - } - } - /// Looks up a node's `PeerId` and attempts to validate routing - /// Will use cached `PeerId` if available /// # Errors /// if the peer was unable to be looked up (did not provide a response, DNE) - pub async fn lookup_node Deserialize<'a>>( + pub async fn lookup_node Deserialize<'a> + Serialize>( &self, - key: K, + key: V, dht_timeout: Duration, ) -> Result { - let pid = if let Some(record) = self.peer_cache.get(&key) { - // exists in cache. look up routing but skip kademlia - *record.value() - } else { - // does not exist in cache. look up in kademlia, store in cache - let pid = self.get_record_timeout::(&key, dht_timeout).await?; - self.peer_cache.insert(key.clone(), pid); - self.peer_cache_expiries.write().await.insert( - SystemTime::now() - + self - .network_config - .ttl - .unwrap_or(Duration::from_secs(KAD_DEFAULT_REPUB_INTERVAL_SEC * 16)), - key, - ); - pid - }; + // get record (from DHT) + let pid = self.get_record_timeout::(&key, dht_timeout).await?; // pid lookup for routing self.lookup_pid(pid).await?; @@ -669,7 +627,7 @@ impl NetworkNodeHandle { } } -impl NetworkNodeHandle { +impl NetworkNodeHandle { /// Get a clone of the internal state pub async fn state(&self) -> S { self.state.cloned().await @@ -738,106 +696,3 @@ pub mod network_node_handle_error { NetworkSnafu, NodeConfigSnafu, RecvSnafu, SendSnafu, SerializationSnafu, TimeoutSnafu, }; } - -#[cfg(test)] -mod test { - use super::*; - - /// libp2p peer cache test - #[cfg_attr( - async_executor_impl = "tokio", - tokio::test(flavor = "multi_thread", worker_threads = 2) - )] - #[cfg_attr(async_executor_impl = "async-std", async_std::test)] - #[instrument] - async fn test_libp2p_cache_eviction() { - async_compatibility_layer::logging::setup_logging(); - async_compatibility_layer::logging::setup_backtrace(); - - let handle: NetworkNodeHandle<(), PeerId> = - NetworkNodeHandle::new(NetworkNodeConfig::default(), 0) - .await - .unwrap(); - - let now = SystemTime::now(); - let later = now + Duration::from_secs(1); - - // present insert - let present_key = PeerId::random(); - let present_pid = PeerId::random(); - handle.peer_cache.insert(present_key, present_pid); - handle - .peer_cache_expiries - .write() - .await - .insert(now, present_key); - - // later insert - let later_key = PeerId::random(); - let later_pid = PeerId::random(); - handle.peer_cache.insert(later_key, later_pid); - handle - .peer_cache_expiries - .write() - .await - .insert(now + Duration::from_secs(1), later_key); - - // check that now and later exist - assert!(handle - .peer_cache - .get(&present_key) - .is_some_and(|entry| entry.value() == &present_pid)); - assert!(handle - .peer_cache - .get(&later_key) - .is_some_and(|entry| entry.value() == &later_pid)); - assert!(handle - .peer_cache_expiries - .read() - .await - .get(&now) - .is_some_and(|entry| entry == &present_key)); - assert!(handle - .peer_cache_expiries - .read() - .await - .get(&later) - .is_some_and(|entry| entry == &later_key)); - - // prune - handle.prune_peer_cache().await; - - // check that now doesn't exist and later does - assert!(handle.peer_cache.get(&present_key).is_none()); - assert!(handle - .peer_cache - .get(&later_key) - .is_some_and(|entry| entry.value() == &later_pid)); - assert!(handle.peer_cache_expiries.read().await.get(&now).is_none()); - assert!(handle - .peer_cache_expiries - .read() - .await - .get(&later) - .is_some_and(|entry| entry == &later_key)); - - // wait for later to expire - async_sleep(Duration::from_secs(1)).await; - - // prune - handle.prune_peer_cache().await; - - // check that later doesn't exist - assert!(handle.peer_cache.get(&later_key).is_none()); - assert!(handle - .peer_cache_expiries - .read() - .await - .get(&later) - .is_none()); - - // check that the expiries and cache are empty - assert!(handle.peer_cache_expiries.read().await.is_empty()); - assert!(handle.peer_cache.is_empty()); - } -} diff --git a/crates/libp2p-networking/tests/common/mod.rs b/crates/libp2p-networking/tests/common/mod.rs index 5a531bbafb..ca9e967319 100644 --- a/crates/libp2p-networking/tests/common/mod.rs +++ b/crates/libp2p-networking/tests/common/mod.rs @@ -39,8 +39,8 @@ pub async fn test_bed, FutG: Future> + 'static + Send + Sync, - F: FnOnce(Vec>>, Duration) -> FutF, - G: Fn(NetworkEvent, Arc>) -> FutG + 'static + Send + Sync, + F: FnOnce(Vec>>, Duration) -> FutF, + G: Fn(NetworkEvent, Arc>) -> FutG + 'static + Send + Sync, { setup_logging(); setup_backtrace(); @@ -69,7 +69,7 @@ pub async fn test_bed(handles: &[Arc>]) -> HashMap { +fn gen_peerid_map(handles: &[Arc>]) -> HashMap { let mut r_val = HashMap::new(); for handle in handles { r_val.insert(handle.peer_id(), handle.id()); @@ -79,7 +79,7 @@ fn gen_peerid_map(handles: &[Arc>]) -> HashMap(handles: &[Arc>]) { +pub async fn print_connections(handles: &[Arc>]) { let m = gen_peerid_map(handles); warn!("PRINTING CONNECTION STATES"); for handle in handles.iter() { @@ -104,7 +104,7 @@ pub async fn spin_up_swarms( num_of_nodes: usize, timeout_len: Duration, num_bootstrap: usize, -) -> Result>>, TestError> { +) -> Result>>, TestError> { let mut handles = Vec::new(); let mut bootstrap_addrs = Vec::<(PeerId, Multiaddr)>::new(); let mut connecting_futs = Vec::new(); diff --git a/crates/libp2p-networking/tests/counter.rs b/crates/libp2p-networking/tests/counter.rs index 85b7cb2a7f..eefbdcf37b 100644 --- a/crates/libp2p-networking/tests/counter.rs +++ b/crates/libp2p-networking/tests/counter.rs @@ -56,7 +56,7 @@ pub enum CounterMessage { #[instrument] pub async fn counter_handle_network_event( event: NetworkEvent, - handle: Arc>, + handle: Arc>, ) -> Result<(), NetworkNodeHandleError> { use CounterMessage::*; use NetworkEvent::*; @@ -121,8 +121,8 @@ pub async fn counter_handle_network_event( /// `requester_handle` asks for `requestee_handle`'s state, /// and then `requester_handle` updates its state to equal `requestee_handle`. async fn run_request_response_increment<'a>( - requester_handle: Arc>, - requestee_handle: Arc>, + requester_handle: Arc>, + requestee_handle: Arc>, timeout: Duration, ) -> Result<(), TestError> { async move { @@ -168,7 +168,7 @@ async fn run_request_response_increment<'a>( /// broadcasts `msg` from a randomly chosen handle /// then asserts that all nodes match `new_state` async fn run_gossip_round( - handles: &[Arc>], + handles: &[Arc>], msg: CounterMessage, new_state: CounterState, timeout_duration: Duration, @@ -234,7 +234,7 @@ async fn run_gossip_round( } async fn run_intersperse_many_rounds( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { for i in 0..NUM_ROUNDS as u32 { @@ -250,21 +250,18 @@ async fn run_intersperse_many_rounds( } async fn run_dht_many_rounds( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { run_dht_rounds(&handles, timeout, 0, NUM_ROUNDS).await; } -async fn run_dht_one_round( - handles: Vec>>, - timeout: Duration, -) { +async fn run_dht_one_round(handles: Vec>>, timeout: Duration) { run_dht_rounds(&handles, timeout, 0, 1).await; } async fn run_request_response_many_rounds( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { for _i in 0..NUM_ROUNDS { @@ -276,7 +273,7 @@ async fn run_request_response_many_rounds( } pub async fn run_request_response_one_round( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { run_request_response_increment_all(&handles, timeout).await; @@ -286,21 +283,21 @@ pub async fn run_request_response_one_round( } pub async fn run_gossip_many_rounds( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { run_gossip_rounds(&handles, NUM_ROUNDS, 0, timeout).await } async fn run_gossip_one_round( - handles: Vec>>, + handles: Vec>>, timeout: Duration, ) { run_gossip_rounds(&handles, 1, 0, timeout).await } async fn run_dht_rounds( - handles: &[Arc>], + handles: &[Arc>], timeout: Duration, starting_val: usize, num_rounds: usize, @@ -336,7 +333,7 @@ async fn run_dht_rounds( /// runs `num_rounds` of message broadcast, incrementing the state of all nodes each broadcast async fn run_gossip_rounds( - handles: &[Arc>], + handles: &[Arc>], num_rounds: usize, starting_state: CounterState, timeout: Duration, @@ -361,7 +358,7 @@ async fn run_gossip_rounds( /// then has all other peers request its state /// and update their state to the recv'ed state async fn run_request_response_increment_all( - handles: &[Arc>], + handles: &[Arc>], timeout: Duration, ) { let mut rng = rand::thread_rng(); diff --git a/crates/orchestrator/default-libp2p-run-config.toml b/crates/orchestrator/default-libp2p-run-config.toml index 5757f4d9f9..a353ed06f5 100644 --- a/crates/orchestrator/default-libp2p-run-config.toml +++ b/crates/orchestrator/default-libp2p-run-config.toml @@ -1,5 +1,5 @@ rounds = 10 -transactions_per_round = 10 +transactions_per_round = 12 node_index = 0 seed = [ 0, @@ -39,7 +39,6 @@ padding = 10 start_delay_seconds = 60 [libp2p_config] -num_bootstrap_nodes = 5 index_ports = true bootstrap_mesh_n_high = 4 bootstrap_mesh_n_low = 4 @@ -49,11 +48,7 @@ mesh_n_high = 4 mesh_n_low = 4 mesh_outbound_min = 2 mesh_n = 4 -next_view_timeout = 10 -propose_min_round_time = 0 -propose_max_round_time = 10 online_time = 10 -num_txn_per_round = 10 base_port = 9000 [config] @@ -68,12 +63,14 @@ timeout_ratio = [ ] round_start_delay = 1 start_delay = 1 -num_bootstrap = 4 +num_bootstrap = 5 [config.propose_min_round_time] secs = 0 nanos = 0 +# TODO (Keyao) Clean up configuration parameters. +# [config.propose_max_round_time] -secs = 1 +secs = 2 nanos = 0 diff --git a/crates/orchestrator/default-run-config.toml b/crates/orchestrator/default-run-config.toml index fe34d75811..ee8333f80a 100644 --- a/crates/orchestrator/default-run-config.toml +++ b/crates/orchestrator/default-run-config.toml @@ -55,8 +55,10 @@ num_bootstrap = 4 secs = 0 nanos = 0 +# TODO (Keyao) Clean up configuration parameters. +# [config.propose_max_round_time] -secs = 1 +secs = 2 nanos = 0 [web_server_config] diff --git a/crates/orchestrator/default-web-server-run-config.toml b/crates/orchestrator/default-web-server-run-config.toml index 0ea0f86ccf..c5d0bd0253 100644 --- a/crates/orchestrator/default-web-server-run-config.toml +++ b/crates/orchestrator/default-web-server-run-config.toml @@ -56,8 +56,10 @@ num_bootstrap = 4 secs = 0 nanos = 0 +# TODO (Keyao) Clean up configuration parameters. +# [config.propose_max_round_time] -secs = 1 +secs = 2 nanos = 0 [web_server_config] diff --git a/crates/orchestrator/src/config.rs b/crates/orchestrator/src/config.rs index 05a38b615c..f61911d944 100644 --- a/crates/orchestrator/src/config.rs +++ b/crates/orchestrator/src/config.rs @@ -8,7 +8,7 @@ use std::{ #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] pub struct Libp2pConfig { pub bootstrap_nodes: Vec<(SocketAddr, Vec)>, - pub num_bootstrap_nodes: u64, + pub num_bootstrap_nodes: usize, pub public_ip: IpAddr, pub base_port: u16, pub node_index: u64, @@ -22,15 +22,14 @@ pub struct Libp2pConfig { pub mesh_outbound_min: usize, pub mesh_n: usize, pub next_view_timeout: u64, - pub propose_min_round_time: u64, - pub propose_max_round_time: u64, + pub propose_min_round_time: Duration, + pub propose_max_round_time: Duration, pub online_time: u64, - pub num_txn_per_round: u64, + pub num_txn_per_round: usize, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] pub struct Libp2pConfigFile { - pub num_bootstrap_nodes: u64, pub index_ports: bool, pub bootstrap_mesh_n_high: usize, pub bootstrap_mesh_n_low: usize, @@ -40,11 +39,7 @@ pub struct Libp2pConfigFile { pub mesh_n_low: usize, pub mesh_outbound_min: usize, pub mesh_n: usize, - pub next_view_timeout: u64, - pub propose_min_round_time: u64, - pub propose_max_round_time: u64, pub online_time: u64, - pub num_txn_per_round: u64, pub base_port: u16, } @@ -59,6 +54,10 @@ pub struct WebServerConfig { pub struct NetworkConfig { pub rounds: usize, pub transactions_per_round: usize, + pub num_bootrap: usize, + pub next_view_timeout: u64, + pub propose_min_round_time: Duration, + pub propose_max_round_time: Duration, pub node_index: u64, pub seed: [u8; 32], pub padding: usize, @@ -88,6 +87,10 @@ impl Default for NetworkConfig { web_server_config: None, da_web_server_config: None, _key_type_phantom: PhantomData, + next_view_timeout: 10, + num_bootrap: 5, + propose_min_round_time: Duration::from_secs(0), + propose_max_round_time: Duration::from_secs(10), } } } @@ -126,10 +129,14 @@ impl From for NetworkConfig { rounds: val.rounds, transactions_per_round: val.transactions_per_round, node_index: 0, + num_bootrap: val.config.num_bootstrap, + next_view_timeout: val.config.next_view_timeout, + propose_max_round_time: val.config.propose_max_round_time, + propose_min_round_time: val.config.propose_min_round_time, seed: val.seed, padding: val.padding, libp2p_config: val.libp2p_config.map(|libp2p_config| Libp2pConfig { - num_bootstrap_nodes: libp2p_config.num_bootstrap_nodes, + num_bootstrap_nodes: val.config.num_bootstrap, index_ports: libp2p_config.index_ports, bootstrap_nodes: Vec::new(), public_ip: IpAddr::V4(Ipv4Addr::UNSPECIFIED), @@ -143,11 +150,11 @@ impl From for NetworkConfig { mesh_n_low: libp2p_config.mesh_n_low, mesh_outbound_min: libp2p_config.mesh_outbound_min, mesh_n: libp2p_config.mesh_n, - next_view_timeout: libp2p_config.next_view_timeout, - propose_min_round_time: libp2p_config.propose_min_round_time, - propose_max_round_time: libp2p_config.propose_max_round_time, + next_view_timeout: val.config.next_view_timeout, + propose_min_round_time: val.config.propose_min_round_time, + propose_max_round_time: val.config.propose_max_round_time, online_time: libp2p_config.online_time, - num_txn_per_round: libp2p_config.num_txn_per_round, + num_txn_per_round: val.transactions_per_round, }), config: val.config.into(), key_type_name: std::any::type_name::().to_string(), @@ -223,7 +230,7 @@ fn default_config() -> HotShotConfigFile { total_nodes: NonZeroUsize::new(10).unwrap(), committee_nodes: 5, max_transactions: NonZeroUsize::new(100).unwrap(), - min_transactions: 0, + min_transactions: 1, next_view_timeout: 10000, timeout_ratio: (11, 10), round_start_delay: 1, diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs index 93efa501e7..d3faa7d410 100644 --- a/crates/orchestrator/src/lib.rs +++ b/crates/orchestrator/src/lib.rs @@ -116,9 +116,7 @@ where if self.config.libp2p_config.clone().is_some() { let libp2p_config_clone = self.config.libp2p_config.clone().unwrap(); // Designate node as bootstrap node and store its identity information - if libp2p_config_clone.bootstrap_nodes.len() - < libp2p_config_clone.num_bootstrap_nodes.try_into().unwrap() - { + if libp2p_config_clone.bootstrap_nodes.len() < libp2p_config_clone.num_bootstrap_nodes { let port_index = match libp2p_config_clone.index_ports { true => node_index, false => 0, @@ -145,9 +143,7 @@ where ) -> Result, ServerError> { if self.config.libp2p_config.is_some() { let libp2p_config = self.config.clone().libp2p_config.unwrap(); - if libp2p_config.bootstrap_nodes.len() - < libp2p_config.num_bootstrap_nodes.try_into().unwrap() - { + if libp2p_config.bootstrap_nodes.len() < libp2p_config.num_bootstrap_nodes { return Err(ServerError { status: tide_disco::StatusCode::BadRequest, message: "Not enough bootstrap nodes have registered".to_string(), diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index eae7854352..88fb9e91db 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -4,7 +4,7 @@ use async_lock::{RwLock, RwLockUpgradableReadGuard}; #[cfg(async_executor_impl = "async-std")] use async_std::task::JoinHandle; use bitvec::prelude::*; -use commit::Committable; +use commit::{Commitment, Committable}; use core::time::Duration; use either::{Either, Left, Right}; use futures::FutureExt; @@ -64,14 +64,14 @@ pub struct SequencingConsensusTaskState< TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, CommitteeEx: ConsensusExchange< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// The global task registry @@ -126,7 +126,7 @@ pub struct SequencingConsensusTaskState< pub id: u64, /// The most Recent QC we've formed from votes, if we've formed it. - pub qc: Option>, + pub qc: Option>>, } /// State for the vote collection task. This handles the building of a QC from a votes received @@ -138,8 +138,8 @@ pub struct VoteCollectionTaskState< TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, { /// the quorum exchange @@ -147,13 +147,13 @@ pub struct VoteCollectionTaskState< #[allow(clippy::type_complexity)] /// Accumulator for votes pub accumulator: Either< - > as SignedCertificate< + >> as SignedCertificate< TYPES, TYPES::Time, TYPES::VoteTokenType, - SequencingLeaf, + Commitment>, >>::VoteAccumulator, - QuorumCertificate>, + QuorumCertificate>>, >, /// View which this vote collection task is collecting votes in pub cur_view: TYPES::Time, @@ -170,8 +170,8 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, { } @@ -190,8 +190,8 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, { match event { @@ -272,14 +272,14 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, CommitteeEx: ConsensusExchange< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "Consensus genesis leaf", level = "error")] @@ -1110,7 +1110,7 @@ where /// Sends a proposal if possible from the high qc we have pub async fn publish_proposal_if_able( &self, - _qc: QuorumCertificate, + _qc: QuorumCertificate>, view: TYPES::Time, ) -> bool { if !self.quorum_exchange.is_leader(view) { @@ -1174,9 +1174,6 @@ where } let block_commitment = self.block.commit(); - if block_commitment == TYPES::BlockType::new().commit() { - debug!("BlockPayload is generic block! {:?}", self.cur_view); - } let leaf = SequencingLeaf { view_number: view, @@ -1236,14 +1233,14 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, CommitteeEx: ConsensusExchange< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { } @@ -1285,14 +1282,14 @@ where TYPES, Message, Proposal = QuorumProposal>, - Certificate = QuorumCertificate>, - Commitment = SequencingLeaf, + Certificate = QuorumCertificate>>, + Commitment = Commitment>, >, CommitteeEx: ConsensusExchange< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { if let SequencingHotShotEvent::Shutdown = event { diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index c7df2854ef..5aadf0e162 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -3,7 +3,7 @@ use async_compatibility_layer::art::async_spawn; use async_lock::RwLock; use bitvec::prelude::*; -use commit::Committable; +use commit::{Commitment, Committable}; use either::{Either, Left, Right}; use futures::FutureExt; use hotshot_task::{ @@ -18,7 +18,7 @@ use hotshot_types::vote::VoteType; use hotshot_types::{ certificate::DACertificate, consensus::{Consensus, View}, - data::{DAProposal, ProposalType, SequencingLeaf, VidDisperse, VidScheme, VidSchemeTrait}, + data::{DAProposal, ProposalType, SequencingLeaf}, message::{Message, Proposal, SequencingMessage}, traits::{ consensus_api::SequencingConsensusApi, @@ -55,7 +55,7 @@ pub struct DATaskState< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// The state's api @@ -91,7 +91,7 @@ pub struct DAVoteCollectionTaskState< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// the committee exchange @@ -103,7 +103,7 @@ pub struct DAVoteCollectionTaskState< TYPES, TYPES::Time, TYPES::VoteTokenType, - TYPES::BlockType, + Commitment, >>::VoteAccumulator, DACertificate, >, @@ -122,7 +122,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { } @@ -140,7 +140,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { match event { @@ -252,7 +252,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// main task event handler @@ -652,39 +652,6 @@ where self.committee_exchange.public_key().clone(), )) .await; - - debug!("Prepare VID shares"); - { - /// TODO https://github.com/EspressoSystems/HotShot/issues/1693 - const NUM_STORAGE_NODES: usize = 10; - /// TODO https://github.com/EspressoSystems/HotShot/issues/1693 - const NUM_CHUNKS: usize = 5; - - // TODO https://github.com/EspressoSystems/HotShot/issues/1686 - let srs = hotshot_types::data::test_srs(NUM_STORAGE_NODES); - - let vid = VidScheme::new(NUM_CHUNKS, NUM_STORAGE_NODES, &srs).unwrap(); - let message_bytes = bincode::serialize(&message).unwrap(); - let vid_disperse = vid.disperse(&message_bytes).unwrap(); - // TODO for now reuse the same block commitment and signature as DA committee - // https://github.com/EspressoSystems/jellyfish/issues/369 - - self.event_stream - .publish(SequencingHotShotEvent::VidDisperseSend( - Proposal { - data: VidDisperse { - view_number: view, - commitment: block.commit(), // TODO GG should be vid_disperse.commit but that's a big change - shares: vid_disperse.shares, - common: vid_disperse.common, - }, - signature: message.signature, - }, - // TODO don't send to committee, send to quorum (consensus.rs) https://github.com/EspressoSystems/HotShot/issues/1696 - self.committee_exchange.public_key().clone(), - )) - .await; - } } SequencingHotShotEvent::Timeout(view) => { @@ -735,7 +702,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { } diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index de8e31fd03..f9fc7f8ba3 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -1,3 +1,5 @@ +use crate::view_sync::ViewSyncPhase; +use commit::Commitment; use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, data::{DAProposal, VidDisperse}, @@ -8,8 +10,6 @@ use hotshot_types::{ vote::{DAVote, QuorumVote, ViewSyncVote}, }; -use crate::view_sync::ViewSyncPhase; - /// All of the possible events that can be passed between Sequecning `HotShot` tasks #[derive(Eq, Hash, PartialEq, Debug, Clone)] pub enum SequencingHotShotEvent> { @@ -18,7 +18,7 @@ pub enum SequencingHotShotEvent> { /// A quorum proposal has been received from the network; handled by the consensus task QuorumProposalRecv(Proposal>, TYPES::SignatureKey), /// A quorum vote has been received from the network; handled by the consensus task - QuorumVoteRecv(QuorumVote), + QuorumVoteRecv(QuorumVote>), /// A DA proposal has been received from the network; handled by the DA task DAProposalRecv(Proposal>, TYPES::SignatureKey), /// A DA vote has been received by the network; handled by the DA task @@ -28,13 +28,13 @@ pub enum SequencingHotShotEvent> { /// Send a quorum proposal to the network; emitted by the leader in the consensus task QuorumProposalSend(Proposal>, TYPES::SignatureKey), /// Send a quorum vote to the next leader; emitted by a replica in the consensus task after seeing a valid quorum proposal - QuorumVoteSend(QuorumVote), + QuorumVoteSend(QuorumVote>), /// Send a DA proposal to the DA committee; emitted by the DA leader (which is the same node as the leader of view v + 1) in the DA task DAProposalSend(Proposal>, TYPES::SignatureKey), /// Send a DA vote to the DA leader; emitted by DA committee members in the DA task after seeing a valid DA proposal DAVoteSend(DAVote), /// The next leader has collected enough votes to form a QC; emitted by the next leader in the consensus task; an internal event only - QCFormed(QuorumCertificate), + QCFormed(QuorumCertificate>), /// The DA leader has collected enough votes to form a DAC; emitted by the DA leader in the DA task; sent to the entire network via the networking task DACSend(DACertificate, TYPES::SignatureKey), /// The current view has changed; emitted by the replica in the consensus task or replica in the view sync task; received by almost all other tasks diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs index ee14e54d9c..b4936bc001 100644 --- a/crates/task-impls/src/transactions.rs +++ b/crates/task-impls/src/transactions.rs @@ -1,11 +1,12 @@ use crate::events::SequencingHotShotEvent; +use async_compatibility_layer::async_primitives::subscribable_rwlock::SubscribableRwLock; use async_compatibility_layer::{ art::async_timeout, async_primitives::subscribable_rwlock::ReadView, }; use async_lock::RwLock; use bincode::config::Options; -use commit::Committable; -use either::{Either, Left, Right}; +use commit::{Commitment, Committable}; +use either::{Left, Right}; use hotshot_task::{ event_stream::{ChannelStream, EventStream}, global_registry::GlobalRegistry, @@ -13,22 +14,30 @@ use hotshot_task::{ task_impls::HSTWithEvent, }; use hotshot_types::{ + block_impl::{VIDBlockPayload, VIDTransaction, NUM_CHUNKS, NUM_STORAGE_NODES}, certificate::DACertificate, consensus::Consensus, - data::SequencingLeaf, - message::{Message, SequencingMessage}, + data::{SequencingLeaf, VidDisperse, VidScheme, VidSchemeTrait}, + message::{Message, Proposal, SequencingMessage}, traits::{ consensus_api::SequencingConsensusApi, - election::ConsensusExchange, + election::{CommitteeExchangeType, ConsensusExchange}, node_implementation::{CommitteeEx, NodeImplementation, NodeType}, - BlockPayload, State, + BlockPayload, }, }; use hotshot_utils::bincode::bincode_opts; use snafu::Snafu; -use std::{collections::HashSet, sync::Arc, time::Instant}; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, + time::Instant, +}; use tracing::{debug, error, instrument, warn}; +/// A type alias for `HashMap, T>` +type CommitmentMap = HashMap, T>; + #[derive(Snafu, Debug)] /// Error type for consensus tasks pub struct ConsensusTaskError {} @@ -47,7 +56,7 @@ pub struct TransactionTaskState< TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// The state's api @@ -61,6 +70,12 @@ pub struct TransactionTaskState< /// Reference to consensus. Leader will require a read lock on this. pub consensus: Arc>>>, + /// A list of undecided transactions + pub transactions: Arc>>, + + /// A list of transactions we've seen decided, but didn't receive + pub seen_transactions: HashSet>, + /// the committee exchange pub committee_exchange: Arc>, @@ -71,8 +86,11 @@ pub struct TransactionTaskState< pub id: u64, } +// We have two `TransactionTaskState` implementations with different bounds. The implementation +// here requires `TYPES: NodeType`, +// whereas it's just `TYPES: NodeType` in the second implementation. impl< - TYPES: NodeType, + TYPES: NodeType, I: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -85,7 +103,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { /// main task event handler @@ -97,15 +115,14 @@ where ) -> Option { match event { SequencingHotShotEvent::TransactionsRecv(transactions) => { - let mut consensus = self.consensus.write().await; - consensus - .get_transactions() + let consensus = self.consensus.read().await; + self.transactions .modify(|txns| { for transaction in transactions { let size = bincode_opts().serialized_size(&transaction).unwrap_or(0); // If we didn't already know about this transaction, update our mempool metrics. - if !consensus.seen_transactions.remove(&transaction.commit()) + if !self.seen_transactions.remove(&transaction.commit()) && txns.insert(transaction.commit(), transaction).is_none() { consensus.metrics.outstanding_transactions.update(1); @@ -141,17 +158,16 @@ where Right(_) => {} } } - let mut consensus = self.consensus.write().await; - let txns = consensus.transactions.cloned().await; + let consensus = self.consensus.read().await; + let txns = self.transactions.cloned().await; let _ = included_txns.iter().map(|hash| { if !txns.contains_key(hash) { - consensus.seen_transactions.insert(*hash); + self.seen_transactions.insert(*hash); } }); drop(txns); - consensus - .transactions + self.transactions .modify(|txns| { *txns = txns .drain() @@ -223,17 +239,46 @@ where drop(consensus); - let mut block = ::StateType::next_block(None); let txns = self.wait_for_transactions(parent_leaf).await?; + // TODO (Keyao) Determine whether to allow empty transaction when proposing a block. + // - for txn in txns { - if let Ok(new_block) = block.add_transaction_raw(&txn) { - block = new_block; - continue; - } + debug!("Prepare VID shares"); + // TODO https://github.com/EspressoSystems/HotShot/issues/1686 + let srs = hotshot_types::data::test_srs(NUM_STORAGE_NODES); + let vid = VidScheme::new(NUM_CHUNKS, NUM_STORAGE_NODES, &srs).unwrap(); + // TODO https://github.com/EspressoSystems/jellyfish/issues/375 + let mut txns_flatten = Vec::new(); + for txn in &txns { + txns_flatten.extend(txn.0.clone()); } + let vid_disperse = vid.disperse(&txns_flatten).unwrap(); + let block = VIDBlockPayload { + transactions: txns, + commitment: vid_disperse.commit, + }; + + self.event_stream + .publish(SequencingHotShotEvent::BlockReady(block.clone(), view + 1)) + .await; + + // TODO (Keyao) Determine and update where to publish VidDisperseSend. + // self.event_stream - .publish(SequencingHotShotEvent::BlockReady(block, view + 1)) + .publish(SequencingHotShotEvent::VidDisperseSend( + Proposal { + data: VidDisperse { + view_number: view + 1, + commitment: block.commit(), + shares: vid_disperse.shares, + common: vid_disperse.common, + }, + // TODO (Keyao) This is also signed in DA task. + signature: self.committee_exchange.sign_da_proposal(&block.commit()), + }, + // TODO don't send to committee, send to quorum (consensus.rs) https://github.com/EspressoSystems/HotShot/issues/1696 + self.committee_exchange.public_key().clone(), + )) .await; return None; } @@ -244,31 +289,55 @@ where } None } +} +// We have two `TransactionTaskState` implementations with different bounds. The implementation +// above requires `TYPES: NodeType`, +// whereas here it's just `TYPES: NodeType`. +impl< + TYPES: NodeType, + I: NodeImplementation< + TYPES, + Leaf = SequencingLeaf, + ConsensusMessage = SequencingMessage, + >, + A: SequencingConsensusApi, I> + 'static, + > TransactionTaskState +where + CommitteeEx: ConsensusExchange< + TYPES, + Message, + Certificate = DACertificate, + Commitment = Commitment, + >, +{ #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "Transaction Handling Task", level = "error")] async fn wait_for_transactions( &self, - parent_leaf: SequencingLeaf, + _parent_leaf: SequencingLeaf, ) -> Option> { let task_start_time = Instant::now(); + // TODO (Keyao) Investigate the use of transaction hash + // // let parent_leaf = self.parent_leaf().await?; - let previous_used_txns = match parent_leaf.deltas { - Either::Left(block) => block.contained_transactions(), - Either::Right(_commitment) => HashSet::new(), - }; + // let previous_used_txns = match parent_leaf.deltas { + // Either::Left(block) => block.contained_transactions(), + // Either::Right(_commitment) => HashSet::new(), + // }; - let consensus = self.consensus.read().await; - - let receiver = consensus.transactions.subscribe().await; + let receiver = self.transactions.subscribe().await; loop { - let all_txns = consensus.transactions.cloned().await; + let all_txns = self.transactions.cloned().await; debug!("Size of transactions: {}", all_txns.len()); - let unclaimed_txns: Vec<_> = all_txns - .iter() - .filter(|(txn_hash, _txn)| !previous_used_txns.contains(txn_hash)) - .collect(); + // TODO (Keyao) Investigate the use of transaction hash + // + // let unclaimed_txns: Vec<_> = all_txns + // .iter() + // .filter(|(txn_hash, _txn)| !previous_used_txns.contains(txn_hash)) + // .collect(); + let unclaimed_txns = all_txns; let time_past = task_start_time.elapsed(); if unclaimed_txns.len() < self.api.min_transactions() @@ -293,17 +362,20 @@ where } break; } - let all_txns = consensus.transactions.cloned().await; - let txns: Vec = all_txns - .iter() - .filter_map(|(txn_hash, txn)| { - if previous_used_txns.contains(txn_hash) { - None - } else { - Some(txn.clone()) - } - }) - .collect(); + let all_txns = self.transactions.cloned().await; + // TODO (Keyao) Investigate the use of transaction hash + // + let txns: Vec = all_txns.values().cloned().collect(); + // let txns: Vec = all_txns + // .iter() + // .filter_map(|(txn_hash, txn)| { + // if previous_used_txns.contains(txn_hash) { + // None + // } else { + // Some(txn.clone()) + // } + // }) + // .collect(); Some(txns) } @@ -334,7 +406,7 @@ where TYPES, Message, Certificate = DACertificate, - Commitment = TYPES::BlockType, + Commitment = Commitment, >, { } diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index f92752f487..30f3df83b6 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -1,7 +1,7 @@ #![allow(clippy::module_name_repetitions)] use crate::events::SequencingHotShotEvent; use async_compatibility_layer::art::{async_sleep, async_spawn}; -use commit::Committable; +use commit::{Commitment, Committable}; use either::Either::{self, Left, Right}; use futures::FutureExt; use hotshot_task::{ @@ -75,7 +75,7 @@ pub struct ViewSyncTaskState< Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { /// Registry to register sub tasks @@ -124,7 +124,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { } @@ -152,7 +152,7 @@ pub struct ViewSyncReplicaTaskState< Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { /// Timeout for view sync rounds @@ -195,7 +195,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { } @@ -228,7 +228,7 @@ pub struct ViewSyncRelayTaskState< TYPES, TYPES::Time, TYPES::VoteTokenType, - ViewSyncData, + Commitment>, >>::VoteAccumulator, ViewSyncCertificate, >, @@ -270,7 +270,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { #[instrument(skip_all, fields(id = self.id, view = *self.current_view), name = "View Sync Main Task", level = "error")] @@ -620,7 +620,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { #[instrument(skip_all, fields(id = self.id, view = *self.current_view), name = "View Sync Replica Task", level = "error")] @@ -951,7 +951,7 @@ where Message, Proposal = ViewSyncCertificate, Certificate = ViewSyncCertificate, - Commitment = ViewSyncData, + Commitment = Commitment>, >, { /// Handles incoming events for the view sync relay task diff --git a/crates/testing/src/node_types.rs b/crates/testing/src/node_types.rs index f4b2f9db1a..4ffd74e2bf 100644 --- a/crates/testing/src/node_types.rs +++ b/crates/testing/src/node_types.rs @@ -1,7 +1,4 @@ -use hotshot::{ - block_impl::{VIDBlockPayload, VIDTransaction}, - traits::implementations::CombinedNetworks, -}; +use hotshot::traits::implementations::CombinedNetworks; use std::{marker::PhantomData, sync::Arc}; use hotshot::{ @@ -17,6 +14,7 @@ use hotshot::{ types::bn254::BLSPubKey, }; use hotshot_types::{ + block_impl::{VIDBlockPayload, VIDTransaction}, certificate::ViewSyncCertificate, data::{QuorumProposal, SequencingLeaf, ViewNumber}, message::{Message, SequencingMessage}, diff --git a/crates/testing/src/overall_safety_task.rs b/crates/testing/src/overall_safety_task.rs index 4bda48adcc..b2a9a88451 100644 --- a/crates/testing/src/overall_safety_task.rs +++ b/crates/testing/src/overall_safety_task.rs @@ -90,9 +90,13 @@ impl> TS for OverallSafety pub struct RoundResult> { /// Transactions that were submitted // pub txns: Vec, + /// Nodes that committed this round /// id -> (leaf, qc) - pub success_nodes: HashMap, QuorumCertificate)>, + // TODO GG: isn't it infeasible to store a Vec? + #[allow(clippy::type_complexity)] + success_nodes: HashMap, QuorumCertificate>)>, + /// Nodes that failed to commit this round pub failed_nodes: HashMap>>>, @@ -185,7 +189,7 @@ impl> RoundResult pub fn insert_into_result( &mut self, idx: usize, - result: (Vec, QuorumCertificate), + result: (Vec, QuorumCertificate>), maybe_block_size: Option, ) -> Option { self.success_nodes.insert(idx as u64, result.clone()); diff --git a/crates/testing/src/task_helpers.rs b/crates/testing/src/task_helpers.rs index 7418028b53..2e935dab61 100644 --- a/crates/testing/src/task_helpers.rs +++ b/crates/testing/src/task_helpers.rs @@ -6,7 +6,7 @@ use commit::Committable; use either::Right; use hotshot::{ certificate::QuorumCertificate, - traits::{BlockPayload, NodeImplementation, TestableNodeImplementation}, + traits::{NodeImplementation, TestableNodeImplementation}, types::{bn254::BLSPubKey, SignatureKey, SystemContextHandle}, HotShotInitializer, HotShotSequencingConsensusApi, SystemContext, }; @@ -14,6 +14,7 @@ use hotshot_task::event_stream::ChannelStream; use hotshot_task_impls::events::SequencingHotShotEvent; use hotshot_types::{ consensus::ConsensusMetricsValue, + block_impl::{VIDBlockPayload, NUM_CHUNKS, NUM_STORAGE_NODES}, data::{QuorumProposal, SequencingLeaf, VidScheme, ViewNumber}, message::{Message, Proposal}, traits::{ @@ -21,7 +22,7 @@ use hotshot_types::{ election::{ConsensusExchange, Membership, SignedCertificate}, node_implementation::{CommitteeEx, ExchangesType, NodeType, QuorumEx}, signature_key::EncodedSignature, - state::ConsensusTime, + state::{ConsensusTime, TestableBlock}, }, }; @@ -116,8 +117,7 @@ async fn build_quorum_proposal_and_signature( let parent_leaf = leaf.clone(); // every event input is seen on the event stream in the output. - - let block_commitment = ::BlockType::new().commit(); + let block = ::genesis(); let leaf = SequencingLeaf { view_number: ViewNumber::new(view), height: parent_leaf.height + 1, @@ -125,14 +125,14 @@ async fn build_quorum_proposal_and_signature( parent_commitment: parent_leaf.commit(), // Use the block commitment rather than the block, so that the replica can construct // the same leaf with the commitment. - deltas: Right(block_commitment), + deltas: Right(block.commit()), rejected: vec![], timestamp: 0, proposer_id: api.public_key().to_bytes(), }; let signature = ::sign(private_key, leaf.commit().as_ref()); let proposal = QuorumProposal::> { - block_commitment, + block_commitment: block.commit(), view_number: ViewNumber::new(view), height: 1, justify_qc: QuorumCertificate::genesis(), @@ -165,8 +165,6 @@ pub fn key_pair_for_id(node_id: u64) -> (::PrivateKey } pub fn vid_init() -> VidScheme { - const NUM_STORAGE_NODES: usize = 10; - const NUM_CHUNKS: usize = 5; let srs = hotshot_types::data::test_srs(NUM_STORAGE_NODES); VidScheme::new(NUM_CHUNKS, NUM_STORAGE_NODES, &srs).unwrap() } diff --git a/crates/testing/src/test_builder.rs b/crates/testing/src/test_builder.rs index 326debb69b..3515f04368 100644 --- a/crates/testing/src/test_builder.rs +++ b/crates/testing/src/test_builder.rs @@ -124,7 +124,7 @@ impl TestMetadata { } } - /// Default setting with 20 nodes and 10 views of successful views. + /// Default setting with 20 nodes and 8 views of successful views. pub fn default_more_nodes_less_success() -> TestMetadata { TestMetadata { total_nodes: 20, @@ -139,11 +139,11 @@ impl TestMetadata { completion_task_description: CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { // Increase the duration to get the expected number of successful views. - duration: Duration::new(40, 0), + duration: Duration::new(200, 0), }, ), overall_safety_properties: OverallSafetyPropertiesDescription { - num_successful_views: 10, + num_successful_views: 8, ..Default::default() }, ..TestMetadata::default() diff --git a/crates/testing/tests/atomic_storage.rs b/crates/testing/tests/atomic_storage.rs index c8183af164..381db09d87 100644 --- a/crates/testing/tests/atomic_storage.rs +++ b/crates/testing/tests/atomic_storage.rs @@ -13,76 +13,6 @@ use rand::thread_rng; type AtomicStorage = hotshot::traits::implementations::AtomicStorage; -#[cfg_attr( - async_executor_impl = "tokio", - tokio::test(flavor = "multi_thread", worker_threads = 2) -)] -#[cfg_attr(async_executor_impl = "async-std", async_std::test)] -async fn test_happy_path_blocks() { - // This folder will be destroyed when the last handle to it closes - let file = tempfile::tempdir().expect("Could not create temp dir"); - let path = file.path(); - println!("Using store in {:?}", path); - let mut store = AtomicStorage::open(path).expect("Could not open atomic store"); - - let block = VDEntryBlock::default(); - let hash = block.hash(); - store - .update(|mut m| { - let block = block.clone(); - async move { m.insert_block(hash, block).await } - }) - .await - .unwrap(); - - // Make sure the data is still there after re-opening - drop(store); - store = AtomicStorage::open(path).expect("Could not open atomic store"); - assert_eq!( - store.get_block(&hash).await.unwrap(), - Some(DEntryBlock::default()) - ); - - // Add some transactions - let mut rng = thread_rng(); - let state = >::get_starting_state(); - let mut hashes = Vec::new(); - let mut block = block; - for _ in 0..10 { - let new = block - .add_transaction_raw(&random_transaction(&state, &mut rng)) - .expect("Could not add transaction"); - println!("Inserting {:?}: {:?}", new.hash(), new); - store - .update(|mut m| { - let new = new.clone(); - async move { m.insert_block(new.hash(), new.clone()).await } - }) - .await - .unwrap(); - hashes.push(new.hash()); - block = new; - } - - // read them all back 3 times - // 1st time: normal readback - // 2nd: after dropping and re-opening the store - for i in 0..3 { - if i == 1 { - drop(store); - store = AtomicStorage::open(path).expect("Could not open atomic store"); - } - - // read them all back - for (idx, hash) in hashes.iter().enumerate() { - match store.get_block(hash).await.expect("Could not read hash") { - Some(block) => println!("read {:?}", block), - None => panic!("Could not read hash {} {:?}", idx, hash), - } - } - } -} - #[cfg_attr( async_executor_impl = "tokio", tokio::test(flavor = "multi_thread", worker_threads = 2) diff --git a/crates/testing/tests/catchup.rs b/crates/testing/tests/catchup.rs index 9c6bd0bb38..01610f38f2 100644 --- a/crates/testing/tests/catchup.rs +++ b/crates/testing/tests/catchup.rs @@ -164,7 +164,7 @@ async fn test_catchup_one_node() { ..Default::default() }; // only alow for the view which the catchup node hasn't started to fail - metadata.overall_safety_properties.num_failed_views = 1; + metadata.overall_safety_properties.num_failed_views = 5; metadata .gen_launcher::() diff --git a/crates/testing/tests/da_task.rs b/crates/testing/tests/da_task.rs index 204038885c..e31c34e6aa 100644 --- a/crates/testing/tests/da_task.rs +++ b/crates/testing/tests/da_task.rs @@ -6,6 +6,7 @@ use hotshot_testing::{ task_helpers::vid_init, }; use hotshot_types::{ + block_impl::VIDTransaction, data::{DAProposal, VidDisperse, VidSchemeTrait, ViewNumber}, traits::{ consensus_api::ConsensusSharedApi, election::ConsensusExchange, @@ -20,10 +21,12 @@ use std::collections::HashMap; )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] async fn test_da_task() { - use hotshot::{block_impl::VIDBlockPayload, tasks::add_da_task}; + use hotshot::tasks::add_da_task; use hotshot_task_impls::harness::run_harness; use hotshot_testing::task_helpers::build_system_handle; - use hotshot_types::{message::Proposal, traits::election::CommitteeExchangeType}; + use hotshot_types::{ + block_impl::VIDBlockPayload, message::Proposal, traits::election::CommitteeExchangeType, + }; async_compatibility_layer::logging::setup_logging(); async_compatibility_layer::logging::setup_backtrace(); @@ -36,9 +39,16 @@ async fn test_da_task() { }; let committee_exchange = api.inner.exchanges.committee_exchange().clone(); let pub_key = *api.public_key(); - let block = VIDBlockPayload(Vec::new()); - let block_commitment = block.commit(); - let signature = committee_exchange.sign_da_proposal(&block_commitment); + let vid = vid_init(); + let txn = vec![0u8]; + let vid_disperse = vid.disperse(&txn).unwrap(); + let block_commitment = vid_disperse.commit; + let block = VIDBlockPayload { + transactions: vec![VIDTransaction(txn)], + commitment: block_commitment, + }; + + let signature = committee_exchange.sign_da_proposal(&block.commit()); let proposal = DAProposal { deltas: block.clone(), view_number: ViewNumber::new(2), @@ -47,13 +57,10 @@ async fn test_da_task() { data: proposal, signature, }; - let vid = vid_init(); - let message_bytes = bincode::serialize(&message).unwrap(); - let vid_disperse = vid.disperse(&message_bytes).unwrap(); let vid_proposal = Proposal { data: VidDisperse { view_number: message.data.view_number, - commitment: block_commitment, + commitment: block.commit(), shares: vid_disperse.shares, common: vid_disperse.common, }, @@ -88,7 +95,7 @@ async fn test_da_task() { SequencingHotShotEvent::BlockReady(block.clone(), ViewNumber::new(2)), 1, ); - output.insert(SequencingHotShotEvent::SendDABlockData(block), 1); + output.insert(SequencingHotShotEvent::SendDABlockData(block.clone()), 1); output.insert( SequencingHotShotEvent::DAProposalSend(message.clone(), pub_key), 1, @@ -98,19 +105,15 @@ async fn test_da_task() { .unwrap() .unwrap(); let da_vote = - committee_exchange.create_da_message(block_commitment, ViewNumber::new(2), vote_token); + committee_exchange.create_da_message(block.commit(), ViewNumber::new(2), vote_token); output.insert(SequencingHotShotEvent::DAVoteSend(da_vote), 1); - output.insert( - SequencingHotShotEvent::VidDisperseSend(vid_proposal.clone(), pub_key), - 1, - ); let vote_token = committee_exchange .make_vote_token(ViewNumber::new(2)) .unwrap() .unwrap(); let vid_vote = - committee_exchange.create_vid_message(block_commitment, ViewNumber::new(2), vote_token); + committee_exchange.create_vid_message(block.commit(), ViewNumber::new(2), vote_token); output.insert(SequencingHotShotEvent::VidVoteSend(vid_vote), 1); output.insert(SequencingHotShotEvent::DAProposalRecv(message, pub_key), 1); diff --git a/crates/testing/tests/memory_network.rs b/crates/testing/tests/memory_network.rs new file mode 100644 index 0000000000..d1a231a2c0 --- /dev/null +++ b/crates/testing/tests/memory_network.rs @@ -0,0 +1,368 @@ +use std::collections::BTreeSet; +use std::marker::PhantomData; +use std::sync::Arc; + +use async_compatibility_layer::logging::setup_logging; +use hotshot::demo::SDemoState; +use hotshot::traits::election::static_committee::{ + GeneralStaticCommittee, StaticElectionConfig, StaticVoteToken, +}; +use hotshot::traits::implementations::{ + MasterMap, MemoryCommChannel, MemoryNetwork, MemoryStorage, +}; +use hotshot::traits::NodeImplementation; +use hotshot::types::bn254::{BLSPrivKey, BLSPubKey}; +use hotshot::types::SignatureKey; +use hotshot_types::block_impl::{VIDBlockPayload, VIDTransaction}; +use hotshot_types::certificate::ViewSyncCertificate; +use hotshot_types::data::{DAProposal, QuorumProposal, SequencingLeaf}; +use hotshot_types::message::{Message, SequencingMessage}; +use hotshot_types::traits::election::{CommitteeExchange, QuorumExchange, ViewSyncExchange}; +use hotshot_types::traits::metrics::NoMetrics; +use hotshot_types::traits::network::TestableNetworkingImplementation; +use hotshot_types::traits::network::{ConnectedNetwork, TransmitType}; +use hotshot_types::traits::node_implementation::{ChannelMaps, NodeType, SequencingExchanges}; +use hotshot_types::vote::{DAVote, ViewSyncVote}; +use hotshot_types::{ + data::ViewNumber, + message::{DataMessage, MessageKind}, + traits::state::ConsensusTime, + vote::QuorumVote, +}; +use rand::rngs::StdRng; +use rand::{RngCore, SeedableRng}; +use serde::{Deserialize, Serialize}; +use tracing::instrument; +use tracing::trace; + +#[derive( + Copy, + Clone, + Debug, + Default, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Serialize, + serde::Deserialize, +)] +pub struct Test; + +impl NodeType for Test { + type Time = ViewNumber; + type BlockType = VIDBlockPayload; + type SignatureKey = BLSPubKey; + type VoteTokenType = StaticVoteToken; + type Transaction = VIDTransaction; + type ElectionConfigType = StaticElectionConfig; + type StateType = SDemoState; +} + +#[derive(Clone, Debug, Deserialize, Serialize, Hash, PartialEq, Eq)] +pub struct TestImpl {} + +pub type ThisLeaf = SequencingLeaf; +pub type ThisMembership = GeneralStaticCommittee::SignatureKey>; +pub type DANetwork = MemoryCommChannel; +pub type QuorumNetwork = MemoryCommChannel; +pub type ViewSyncNetwork = MemoryCommChannel; + +pub type ThisDAProposal = DAProposal; +pub type ThisDAVote = DAVote; + +pub type ThisQuorumProposal = QuorumProposal; +pub type ThisQuorumVote = QuorumVote; + +pub type ThisViewSyncProposal = ViewSyncCertificate; +pub type ThisViewSyncVote = ViewSyncVote; + +impl NodeImplementation for TestImpl { + type Storage = MemoryStorage; + type Leaf = SequencingLeaf; + type Exchanges = SequencingExchanges< + Test, + Message, + QuorumExchange< + Test, + Self::Leaf, + ThisQuorumProposal, + ThisMembership, + QuorumNetwork, + Message, + >, + CommitteeExchange>, + ViewSyncExchange< + Test, + ThisViewSyncProposal, + ThisMembership, + ViewSyncNetwork, + Message, + >, + >; + type ConsensusMessage = SequencingMessage; + + fn new_channel_maps( + start_view: ::Time, + ) -> (ChannelMaps, Option>) { + (ChannelMaps::new(start_view), None) + } +} + +/// fake Eq +/// we can't compare the votetokentype for equality, so we can't +/// derive EQ on `VoteType` and thereby message +/// we are only sending data messages, though so we compare key and +/// data message +fn fake_message_eq(message_1: Message, message_2: Message) { + assert_eq!(message_1.sender, message_2.sender); + if let MessageKind::Data(DataMessage::SubmitTransaction(d_1, _)) = message_1.kind { + if let MessageKind::Data(DataMessage::SubmitTransaction(d_2, _)) = message_2.kind { + assert_eq!(d_1, d_2); + } + } else { + panic!("Got unexpected message type in memory test!"); + } +} + +#[instrument] +fn get_pubkey() -> BLSPubKey { + // random 32 bytes + let mut bytes = [0; 32]; + rand::thread_rng().fill_bytes(&mut bytes); + BLSPubKey::from_private(&BLSPrivKey::generate_from_seed(bytes)) +} + +/// create a message +fn gen_messages(num_messages: u64, seed: u64, pk: BLSPubKey) -> Vec> { + let mut messages = Vec::new(); + for _ in 0..num_messages { + // create a random transaction from seed + let mut bytes = [0u8; 8]; + let mut rng = StdRng::seed_from_u64(seed); + rng.fill_bytes(&mut bytes); + + let message = Message { + sender: pk, + kind: MessageKind::Data(DataMessage::SubmitTransaction( + VIDTransaction(bytes.to_vec()), + ::new(0), + )), + _phantom: PhantomData, + }; + messages.push(message); + } + messages +} + +// Spawning a single MemoryNetwork should produce no errors +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[instrument] +async fn memory_network_spawn_single() { + setup_logging(); + let group: Arc, ::SignatureKey>> = + MasterMap::new(); + trace!(?group); + let pub_key = get_pubkey(); + let _network = MemoryNetwork::new(pub_key, NoMetrics::boxed(), group, Option::None); +} + +// // Spawning a two MemoryNetworks and connecting them should produce no errors +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[instrument] +async fn memory_network_spawn_double() { + setup_logging(); + let group: Arc, ::SignatureKey>> = + MasterMap::new(); + trace!(?group); + let pub_key_1 = get_pubkey(); + let _network_1 = MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); + let pub_key_2 = get_pubkey(); + let _network_2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); +} + +// Check to make sure direct queue works +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[instrument] +async fn memory_network_direct_queue() { + setup_logging(); + // Create some dummy messages + + // Make and connect the networking instances + let group: Arc, ::SignatureKey>> = + MasterMap::new(); + trace!(?group); + + let pub_key_1 = get_pubkey(); + let network1 = MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); + + let pub_key_2 = get_pubkey(); + let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); + + let first_messages: Vec> = gen_messages(5, 100, pub_key_1); + + // Test 1 -> 2 + // Send messages + for sent_message in first_messages { + network1 + .direct_message(sent_message.clone(), pub_key_2) + .await + .expect("Failed to message node"); + let mut recv_messages = network2 + .recv_msgs(TransmitType::Direct) + .await + .expect("Failed to receive message"); + let recv_message = recv_messages.pop().unwrap(); + assert!(recv_messages.is_empty()); + fake_message_eq(sent_message, recv_message); + } + + let second_messages: Vec> = gen_messages(5, 200, pub_key_2); + + // Test 2 -> 1 + // Send messages + for sent_message in second_messages { + network2 + .direct_message(sent_message.clone(), pub_key_1) + .await + .expect("Failed to message node"); + let mut recv_messages = network1 + .recv_msgs(TransmitType::Direct) + .await + .expect("Failed to receive message"); + let recv_message = recv_messages.pop().unwrap(); + assert!(recv_messages.is_empty()); + fake_message_eq(sent_message, recv_message); + } +} + +// Check to make sure direct queue works +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[instrument] +async fn memory_network_broadcast_queue() { + setup_logging(); + // Make and connect the networking instances + let group: Arc, ::SignatureKey>> = + MasterMap::new(); + trace!(?group); + let pub_key_1 = get_pubkey(); + let network1 = MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); + let pub_key_2 = get_pubkey(); + let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); + + let first_messages: Vec> = gen_messages(5, 100, pub_key_1); + + // Test 1 -> 2 + // Send messages + for sent_message in first_messages { + network1 + .broadcast_message( + sent_message.clone(), + vec![pub_key_2].into_iter().collect::>(), + ) + .await + .expect("Failed to message node"); + let mut recv_messages = network2 + .recv_msgs(TransmitType::Broadcast) + .await + .expect("Failed to receive message"); + let recv_message = recv_messages.pop().unwrap(); + assert!(recv_messages.is_empty()); + fake_message_eq(sent_message, recv_message); + } + + let second_messages: Vec> = gen_messages(5, 200, pub_key_2); + + // Test 2 -> 1 + // Send messages + for sent_message in second_messages { + network2 + .broadcast_message( + sent_message.clone(), + vec![pub_key_1].into_iter().collect::>(), + ) + .await + .expect("Failed to message node"); + let mut recv_messages = network1 + .recv_msgs(TransmitType::Broadcast) + .await + .expect("Failed to receive message"); + let recv_message = recv_messages.pop().unwrap(); + assert!(recv_messages.is_empty()); + fake_message_eq(sent_message, recv_message); + } +} + +#[cfg_attr( + async_executor_impl = "tokio", + tokio::test(flavor = "multi_thread", worker_threads = 2) +)] +#[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[instrument] +#[allow(deprecated)] +async fn memory_network_test_in_flight_message_count() { + setup_logging(); + + let group: Arc, ::SignatureKey>> = + MasterMap::new(); + trace!(?group); + let pub_key_1 = get_pubkey(); + let network1 = MemoryNetwork::new(pub_key_1, NoMetrics::boxed(), group.clone(), Option::None); + let pub_key_2 = get_pubkey(); + let network2 = MemoryNetwork::new(pub_key_2, NoMetrics::boxed(), group, Option::None); + + // Create some dummy messages + let messages: Vec> = gen_messages(5, 100, pub_key_1); + let broadcast_recipients = BTreeSet::from([pub_key_1, pub_key_2]); + + assert_eq!(network1.in_flight_message_count(), Some(0)); + assert_eq!(network2.in_flight_message_count(), Some(0)); + + for (count, message) in messages.iter().enumerate() { + network1 + .direct_message(message.clone(), pub_key_2) + .await + .unwrap(); + // network 2 has received `count` broadcast messages and `count + 1` direct messages + assert_eq!(network2.in_flight_message_count(), Some(count + count + 1)); + + network2 + .broadcast_message(message.clone(), broadcast_recipients.clone()) + .await + .unwrap(); + // network 1 has received `count` broadcast messages + assert_eq!(network1.in_flight_message_count(), Some(count + 1)); + + // network 2 has received `count + 1` broadcast messages and `count + 1` direct messages + assert_eq!(network2.in_flight_message_count(), Some((count + 1) * 2)); + } + + while network1.in_flight_message_count().unwrap() > 0 { + network1.recv_msgs(TransmitType::Broadcast).await.unwrap(); + } + + while network2.in_flight_message_count().unwrap() > 0 { + network2.recv_msgs(TransmitType::Broadcast).await.unwrap(); + network2.recv_msgs(TransmitType::Direct).await.unwrap(); + } + + assert_eq!(network1.in_flight_message_count(), Some(0)); + assert_eq!(network2.in_flight_message_count(), Some(0)); +} diff --git a/crates/testing/tests/network_task.rs b/crates/testing/tests/network_task.rs index 7ad17e2008..c4165a8c31 100644 --- a/crates/testing/tests/network_task.rs +++ b/crates/testing/tests/network_task.rs @@ -21,11 +21,13 @@ use std::collections::HashMap; #[cfg_attr(async_executor_impl = "async-std", async_std::test)] #[ignore] async fn test_network_task() { - use hotshot::block_impl::VIDBlockPayload; use hotshot_task_impls::harness::run_harness; use hotshot_testing::task_helpers::build_system_handle; use hotshot_types::{ - data::VidDisperse, message::Proposal, traits::election::CommitteeExchangeType, + block_impl::{VIDBlockPayload, VIDTransaction}, + data::VidDisperse, + message::Proposal, + traits::election::CommitteeExchangeType, }; async_compatibility_layer::logging::setup_logging(); @@ -40,9 +42,15 @@ async fn test_network_task() { let committee_exchange = api.inner.exchanges.committee_exchange().clone(); let pub_key = *api.public_key(); let priv_key = api.private_key(); - let block = VIDBlockPayload(Vec::new()); - let block_commitment = block.commit(); - let signature = committee_exchange.sign_da_proposal(&block_commitment); + let vid = vid_init(); + let txn = vec![0u8]; + let vid_disperse = vid.disperse(&txn).unwrap(); + let block_commitment = vid_disperse.commit; + let block = VIDBlockPayload { + transactions: vec![VIDTransaction(txn)], + commitment: block_commitment, + }; + let signature = committee_exchange.sign_da_proposal(&block.commit()); let da_proposal = Proposal { data: DAProposal { deltas: block.clone(), @@ -51,15 +59,12 @@ async fn test_network_task() { signature, }; let quorum_proposal = build_quorum_proposal(&handle, priv_key, 2).await; - let vid = vid_init(); - let da_proposal_bytes = bincode::serialize(&da_proposal).unwrap(); - let vid_disperse = vid.disperse(&da_proposal_bytes).unwrap(); // TODO for now reuse the same block commitment and signature as DA committee // https://github.com/EspressoSystems/jellyfish/issues/369 let da_vid_disperse = Proposal { data: VidDisperse { view_number: da_proposal.data.view_number, - commitment: block_commitment, + commitment: block.commit(), shares: vid_disperse.shares, common: vid_disperse.common, }, diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 9f65d06657..e0532c2e66 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -31,7 +31,7 @@ displaydoc = { version = "0.2.3", default-features = false } either = { workspace = true, features = ["serde"] } espresso-systems-common = { workspace = true } futures = { workspace = true } -generic-array = "0.14.7" +generic-array = { workspace = true } hex_fmt = "0.3.0" hotshot-constants = { path = "../constants" } hotshot-utils = { path = "../utils" } @@ -43,6 +43,7 @@ rand = { workspace = true } rand_chacha = { workspace = true } serde = { workspace = true } sha2 = { workspace = true } +sha3 = "^0.10" snafu = { workspace = true } tagged-base64 = { git = "https://github.com/EspressoSystems/tagged-base64", tag = "0.2.4" } time = { workspace = true } diff --git a/crates/hotshot/src/block_impl.rs b/crates/types/src/block_impl.rs similarity index 54% rename from crates/hotshot/src/block_impl.rs rename to crates/types/src/block_impl.rs index 8960d79158..2360cb3a24 100644 --- a/crates/hotshot/src/block_impl.rs +++ b/crates/types/src/block_impl.rs @@ -4,12 +4,22 @@ use std::{ fmt::{Debug, Display}, }; +use crate::{ + data::{test_srs, VidScheme, VidSchemeTrait}, + traits::{block_contents::Transaction, state::TestableBlock, BlockPayload}, +}; use commit::{Commitment, Committable}; -use hotshot_types::traits::{block_contents::Transaction, state::TestableBlock, BlockPayload}; use serde::{Deserialize, Serialize}; use sha3::{Digest, Keccak256}; use snafu::Snafu; +// TODO +/// Number of storage nodes for VID initiation. +pub const NUM_STORAGE_NODES: usize = 10; +// TODO +/// Number of chunks for VID initiation. +pub const NUM_CHUNKS: usize = 5; + /// The transaction in a [`VIDBlockPayload`]. #[derive(Default, PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)] pub struct VIDTransaction(pub Vec); @@ -30,14 +40,6 @@ impl Committable for VIDTransaction { impl Transaction for VIDTransaction {} -impl VIDTransaction { - /// create a new transaction - #[must_use] - pub fn new() -> Self { - Self(Vec::new()) - } -} - /// The error type for block payload. #[derive(Snafu, Debug)] pub enum BlockPayloadError { @@ -55,14 +57,39 @@ pub enum BlockPayloadError { /// A [`BlockPayload`] that contains a list of `VIDTransaction`. #[derive(PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)] -pub struct VIDBlockPayload(pub Vec); +pub struct VIDBlockPayload { + /// List of transactions. + pub transactions: Vec, + /// VID commitment. + pub commitment: ::Commit, +} + +impl VIDBlockPayload { + /// Create a genesis block payload with transaction bytes `vec![0]`, to be used for + /// consensus task initiation. + /// # Panics + /// If the `VidScheme` construction fails. + #[must_use] + pub fn genesis() -> Self { + // TODO + let srs = test_srs(NUM_STORAGE_NODES); + // TODO We are using constant numbers for now, but they will change as the quorum size + // changes. + // TODO + let vid = VidScheme::new(NUM_CHUNKS, NUM_STORAGE_NODES, &srs).unwrap(); + let txn = vec![0]; + let vid_disperse = vid.disperse(&txn).unwrap(); + VIDBlockPayload { + transactions: vec![VIDTransaction(txn)], + commitment: vid_disperse.commit, + } + } +} impl Committable for VIDBlockPayload { fn commit(&self) -> Commitment { - // TODO: Use use VID block commitment. - // let builder = commit::RawCommitmentBuilder::new("BlockPayload Comm"); - builder.finalize() + builder.generic_byte_array(&self.commitment).finalize() } fn tag() -> String { @@ -72,17 +99,17 @@ impl Committable for VIDBlockPayload { impl Display for VIDBlockPayload { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "BlockPayload #txns={}", self.0.len()) + write!(f, "BlockPayload #txns={}", self.transactions.len()) } } impl TestableBlock for VIDBlockPayload { fn genesis() -> Self { - VIDBlockPayload(Vec::new()) + Self::genesis() } fn txn_count(&self) -> u64 { - self.0.len() as u64 + self.transactions.len() as u64 } } @@ -91,20 +118,10 @@ impl BlockPayload for VIDBlockPayload { type Transaction = VIDTransaction; - fn new() -> Self { - ::genesis() - } - - fn add_transaction_raw( - &self, - tx: &Self::Transaction, - ) -> std::result::Result { - let mut new = self.0.clone(); - new.push(tx.clone()); - Ok(VIDBlockPayload(new)) - } - fn contained_transactions(&self) -> HashSet> { - self.0.iter().map(commit::Committable::commit).collect() + self.transactions + .iter() + .map(commit::Committable::commit) + .collect() } } diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 5309729eb7..09c3235744 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -6,7 +6,7 @@ use crate::vote::QuorumVoteAccumulator; use crate::vote::ViewSyncVoteAccumulator; use crate::vote::VoteType; use crate::{ - data::{fake_commitment, serialize_signature, LeafType}, + data::serialize_signature, traits::{ election::{SignedCertificate, VoteData, VoteToken}, node_implementation::NodeType, @@ -16,13 +16,14 @@ use crate::{ vote::{DAVote, ViewSyncData, ViewSyncVote}, }; use bincode::Options; -use commit::{Commitment, Committable}; +use commit::{Commitment, CommitmentBounds, Committable}; use espresso_systems_common::hotshot::tag; use hotshot_utils::bincode::bincode_opts; use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Debug, Display, Formatter}, + hash::Hash, ops::Deref, }; use tracing::debug; @@ -49,12 +50,12 @@ pub struct DACertificate { /// /// A Quorum Certificate is a threshold signature of the `Leaf` being proposed, as well as some /// metadata, such as the `Stage` of consensus the quorum certificate was generated during. -#[derive(custom_debug::Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Hash)] +#[derive(custom_debug::Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Hash, Eq)] #[serde(bound(deserialize = ""))] -pub struct QuorumCertificate> { +pub struct QuorumCertificate { /// commitment to previous leaf #[debug(skip)] - pub leaf_commitment: Commitment, + pub leaf_commitment: COMMITMENT, /// Which view this QC relates to pub view_number: TYPES::Time, /// assembled signature for certificate aggregation @@ -63,7 +64,9 @@ pub struct QuorumCertificate> pub is_genesis: bool, } -impl> Display for QuorumCertificate { +impl Display + for QuorumCertificate +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, @@ -138,15 +141,15 @@ pub enum AssembledSignature { } /// Data from a vote needed to accumulate into a `SignedCertificate` -pub struct VoteMetaData { +pub struct VoteMetaData { /// Voter's public key pub encoded_key: EncodedPublicKey, /// Votes signature pub encoded_signature: EncodedSignature, /// Commitment to what's voted on. E.g. the leaf for a `QuorumCertificate` - pub commitment: Commitment, + pub commitment: COMMITMENT, /// Data of the vote, yes, no, timeout, or DA - pub data: VoteData>, + pub data: VoteData, /// The votes's token pub vote_token: T, /// View number for the vote @@ -156,12 +159,12 @@ pub struct VoteMetaData, } -impl> - SignedCertificate - for QuorumCertificate +impl + SignedCertificate + for QuorumCertificate { - type Vote = QuorumVote; - type VoteAccumulator = QuorumVoteAccumulator; + type Vote = QuorumVote; + type VoteAccumulator = QuorumVoteAccumulator; fn from_signatures_and_commitment( signatures: AssembledSignature, @@ -191,11 +194,11 @@ impl> self.signatures.clone() } - fn leaf_commitment(&self) -> Commitment { + fn leaf_commitment(&self) -> COMMITMENT { self.leaf_commitment } - fn set_leaf_commitment(&mut self, commitment: Commitment) { + fn set_leaf_commitment(&mut self, commitment: COMMITMENT) { self.leaf_commitment = commitment; } @@ -204,8 +207,9 @@ impl> } fn genesis() -> Self { + // TODO GG need a new way to get fake commit now that we don't have Committable Self { - leaf_commitment: fake_commitment::(), + leaf_commitment: COMMITMENT::default_commitment_no_preimage(), view_number: ::genesis(), signatures: AssembledSignature::Genesis(), is_genesis: true, @@ -213,16 +217,14 @@ impl> } } -impl> Eq for QuorumCertificate {} - -impl> Committable - for QuorumCertificate +impl Committable + for QuorumCertificate { fn commit(&self) -> Commitment { let signatures_bytes = serialize_signature(&self.signatures); commit::RawCommitmentBuilder::new("Quorum Certificate Commitment") - .field("leaf commitment", self.leaf_commitment) + .var_size_field("leaf commitment", self.leaf_commitment.as_ref()) .u64_field("view number", *self.view_number.deref()) .constant_str("justify_qc signatures") .var_size_bytes(&signatures_bytes) @@ -234,11 +236,12 @@ impl> Committable } } -impl SignedCertificate +impl + SignedCertificate> for DACertificate { type Vote = DAVote; - type VoteAccumulator = DAVoteAccumulator; + type VoteAccumulator = DAVoteAccumulator, Self::Vote>; fn from_signatures_and_commitment( signatures: AssembledSignature, @@ -323,11 +326,12 @@ impl Committable for ViewSyncCertificate { } impl - SignedCertificate> + SignedCertificate>> for ViewSyncCertificate { type Vote = ViewSyncVote; - type VoteAccumulator = ViewSyncVoteAccumulator, Self::Vote>; + type VoteAccumulator = + ViewSyncVoteAccumulator>, Self::Vote>; /// Build a QC from the threshold signature and commitment fn from_signatures_and_commitment( signatures: AssembledSignature, diff --git a/crates/types/src/consensus.rs b/crates/types/src/consensus.rs index cdf4db9d85..987d8d52af 100644 --- a/crates/types/src/consensus.rs +++ b/crates/types/src/consensus.rs @@ -42,12 +42,6 @@ pub struct Consensus> { /// last view had a successful decide event pub last_decided_view: TYPES::Time, - /// A list of undecided transactions - pub transactions: Arc>>, - - /// A list of transactions we've seen decided, but didn't receive - pub seen_transactions: HashSet>, - /// Map of leaf hash -> leaf /// - contains undecided leaves /// - includes the MOST RECENT decided leaf @@ -62,7 +56,7 @@ pub struct Consensus> { pub locked_view: TYPES::Time, /// the highqc per spec - pub high_qc: QuorumCertificate, + pub high_qc: QuorumCertificate>, /// A reference to the metrics trait #[debug(skip)] @@ -360,12 +354,6 @@ impl> Consensus { self.state_map = self.state_map.split_off(&new_anchor_view); } - /// return a clone of the internal storage of unclaimed transactions - #[must_use] - pub fn get_transactions(&self) -> Arc>> { - self.transactions.clone() - } - /// Gets the last decided state /// # Panics /// if the last decided view's state does not exist in the state map diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index 14b180b039..1a02a6b9a7 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -9,7 +9,6 @@ use crate::{ ViewSyncCertificate, }, traits::{ - election::SignedCertificate, node_implementation::NodeType, signature_key::{EncodedPublicKey, SignatureKey}, state::{ConsensusTime, TestableBlock, TestableState}, @@ -136,7 +135,7 @@ where pub height: u64, /// Per spec, justification - pub justify_qc: QuorumCertificate, + pub justify_qc: QuorumCertificate>, /// The hash of the parent `Leaf` /// So we can ask if it extends @@ -177,8 +176,6 @@ pub struct VidDisperse { /// The view number for which this VID data is intended pub view_number: TYPES::Time, /// Block commitment - /// - /// TODO GG type should be `::Common` but that's a big change. pub commitment: Commitment, /// VID shares dispersed among storage nodes pub shares: Vec<::Share>, @@ -219,7 +216,7 @@ pub struct QuorumProposal> { pub height: u64, /// Per spec, justification - pub justify_qc: QuorumCertificate, + pub justify_qc: QuorumCertificate>, /// Possible timeout certificate. Only present if the justify_qc is not for the preceding view pub timeout_certificate: Option>, @@ -448,7 +445,7 @@ pub trait LeafType: /// Create a new leaf from its components. fn new( view_number: LeafTime, - justify_qc: QuorumCertificate, + justify_qc: QuorumCertificate>, deltas: LeafBlock, state: LeafState, ) -> Self; @@ -461,7 +458,7 @@ pub trait LeafType: /// Change the height of this leaf. fn set_height(&mut self, height: u64); /// The QC linking this leaf to its parent in the chain. - fn get_justify_qc(&self) -> QuorumCertificate; + fn get_justify_qc(&self) -> QuorumCertificate>; /// Commitment to this leaf's parent. fn get_parent_commitment(&self) -> Commitment; /// The block contained in this leaf. @@ -535,11 +532,11 @@ pub struct ValidatingLeaf { pub height: u64, /// Per spec, justification - pub justify_qc: QuorumCertificate, + pub justify_qc: QuorumCertificate>, /// The hash of the parent `Leaf` /// So we can ask if it extends - pub parent_commitment: Commitment>, + pub parent_commitment: Commitment, /// BlockPayload leaf wants to apply pub deltas: TYPES::BlockType, @@ -574,11 +571,11 @@ pub struct SequencingLeaf { pub height: u64, /// Per spec, justification - pub justify_qc: QuorumCertificate, + pub justify_qc: QuorumCertificate>, /// The hash of the parent `SequencingLeaf` /// So we can ask if it extends - pub parent_commitment: Commitment>, + pub parent_commitment: Commitment, /// The block or block commitment to be applied pub deltas: Either>, @@ -648,7 +645,7 @@ impl LeafType for ValidatingLeaf { fn new( view_number: ::Time, - justify_qc: QuorumCertificate, + justify_qc: QuorumCertificate>, deltas: ::BlockType, state: ::StateType, ) -> Self { @@ -677,7 +674,7 @@ impl LeafType for ValidatingLeaf { self.height = height; } - fn get_justify_qc(&self) -> QuorumCertificate { + fn get_justify_qc(&self) -> QuorumCertificate> { self.justify_qc.clone() } @@ -765,7 +762,7 @@ impl LeafType for SequencingLeaf { fn new( view_number: ::Time, - justify_qc: QuorumCertificate, + justify_qc: QuorumCertificate>, deltas: ::BlockType, _state: ::StateType, ) -> Self { @@ -793,7 +790,7 @@ impl LeafType for SequencingLeaf { self.height = height; } - fn get_justify_qc(&self) -> QuorumCertificate { + fn get_justify_qc(&self) -> QuorumCertificate> { self.justify_qc.clone() } @@ -938,7 +935,7 @@ impl Committable for ValidatingLeaf { .u64(*self.justify_qc.view_number) .field( "justify_qc leaf commitment", - self.justify_qc.leaf_commitment(), + self.justify_qc.leaf_commitment, ) .constant_str("justify_qc signatures") .var_size_bytes(&signatures_bytes) @@ -970,7 +967,7 @@ impl Committable for SequencingLeaf { .u64(*self.justify_qc.view_number) .field( "justify_qc leaf commitment", - self.justify_qc.leaf_commitment(), + self.justify_qc.leaf_commitment, ) .constant_str("justify_qc signatures") .var_size_bytes(&signatures_bytes) diff --git a/crates/types/src/event.rs b/crates/types/src/event.rs index f33a39f8cd..5d091cd400 100644 --- a/crates/types/src/event.rs +++ b/crates/types/src/event.rs @@ -4,6 +4,7 @@ use crate::{ certificate::QuorumCertificate, data::LeafType, error::HotShotError, traits::node_implementation::NodeType, }; +use commit::Commitment; use std::sync::Arc; /// A status event emitted by a `HotShot` instance /// @@ -42,7 +43,7 @@ pub enum EventType> { /// /// Note that the QC for each additional leaf in the chain can be obtained from the leaf /// before it using - qc: Arc>, + qc: Arc>>, /// Optional information of the number of transactions in the block, for logging purposes. block_size: Option, }, diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 6152689acf..54739106c3 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -11,6 +11,7 @@ use std::{num::NonZeroUsize, time::Duration}; +pub mod block_impl; pub mod certificate; pub mod consensus; pub mod data; diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index d5897c0ef8..c26de8bcef 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -15,6 +15,7 @@ use crate::{ }, vote::{DAVote, QuorumVote, ViewSyncVote, VoteType}, }; +use commit::Commitment; use derivative::Derivative; use either::Either::{self, Left, Right}; use serde::{Deserialize, Serialize}; @@ -149,7 +150,7 @@ where /// Message with a quorum proposal. Proposal(Proposal>, TYPES::SignatureKey), /// Message with a quorum vote. - Vote(QuorumVote, TYPES::SignatureKey), + Vote(QuorumVote>, TYPES::SignatureKey), /// Message with a view sync vote. ViewSyncVote(ViewSyncVote), /// Message with a view sync certificate. @@ -314,7 +315,7 @@ where Proposal(Proposal>), /// Message with a quorum vote. - Vote(QuorumVote), + Vote(QuorumVote>), /// Message with a view sync vote. ViewSyncVote(ViewSyncVote), diff --git a/crates/types/src/traits/block_contents.rs b/crates/types/src/traits/block_contents.rs index a365dd7144..2fc3383a14 100644 --- a/crates/types/src/traits/block_contents.rs +++ b/crates/types/src/traits/block_contents.rs @@ -13,6 +13,8 @@ use std::{ hash::Hash, }; +// TODO (Keyao) Determine whether we can refactor BlockPayload and Transaction from traits to structs. +// /// Abstraction over the full contents of a block /// /// This trait encapsulates the behaviors that the transactions of a block must have in order to be @@ -20,8 +22,6 @@ use std::{ /// * Must have a predefined error type ([`BlockPayload::Error`]) /// * Must have a transaction type that can be compared for equality, serialized and serialized, /// sent between threads, and can have a hash produced of it -/// * Must be able to be produced incrementally by appending transactions -/// ([`add_transaction_raw`](BlockPayload::add_transaction_raw)) /// * Must be hashable pub trait BlockPayload: Serialize @@ -42,23 +42,13 @@ pub trait BlockPayload: /// The type of the transitions we are applying type Transaction: Transaction; - /// Construct an empty or genesis block. - fn new() -> Self; - - /// Attempts to add a transaction, returning an Error if it would result in a structurally - /// invalid block - /// - /// # Errors - /// - /// Should return an error if this transaction leads to an invalid block - fn add_transaction_raw(&self, tx: &Self::Transaction) - -> std::result::Result; - /// returns hashes of all the transactions in this block /// TODO make this ordered with a vec fn contained_transactions(&self) -> HashSet>; } +// TODO (Keyao) Determine whether we can refactor BlockPayload and Transaction from traits to structs. +// /// Abstraction over any type of transaction. Used by [`BlockPayload`]. pub trait Transaction: Clone + Serialize + DeserializeOwned + Debug + PartialEq + Eq + Sync + Send + Committable + Hash @@ -76,6 +66,8 @@ pub mod dummy { pub use crate::traits::state::dummy::DummyState; use crate::traits::state::TestableBlock; + // TODO (Keyao) Investigate the use of DummyBlock. + // /// The dummy block #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub struct DummyBlock { @@ -133,19 +125,6 @@ pub mod dummy { type Transaction = DummyTransaction; - fn new() -> Self { - Self { nonce: 0 } - } - - fn add_transaction_raw( - &self, - _tx: &Self::Transaction, - ) -> std::result::Result { - Ok(Self { - nonce: self.nonce + 1, - }) - } - fn contained_transactions(&self) -> HashSet> { HashSet::new() } diff --git a/crates/types/src/traits/consensus_api.rs b/crates/types/src/traits/consensus_api.rs index ab3f899944..08716a3d73 100644 --- a/crates/types/src/traits/consensus_api.rs +++ b/crates/types/src/traits/consensus_api.rs @@ -14,7 +14,7 @@ use crate::{ }, }; use async_trait::async_trait; - +use commit::Commitment; use std::{num::NonZeroUsize, sync::Arc, time::Duration}; /// The API that [`HotStuff`] needs to talk to the system, implemented for both validating and @@ -97,7 +97,7 @@ pub trait ConsensusSharedApi< &self, view_number: TYPES::Time, leaf_views: Vec, - decide_qc: QuorumCertificate, + decide_qc: QuorumCertificate>, ) { self.send_event(Event { view_number, diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 3d152cc85e..6b3ef7e790 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -34,7 +34,7 @@ use crate::{ }, }; use bincode::Options; -use commit::{Commitment, Committable}; +use commit::{Commitment, CommitmentBounds, Committable}; use derivative::Derivative; use either::Either; use ethereum_types::U256; @@ -76,7 +76,7 @@ pub enum Checked { #[serde(bound(deserialize = ""))] pub enum VoteData where - COMMITMENT: for<'a> Deserialize<'a>, + COMMITMENT: CommitmentBounds, { /// Vote to provide availability for a block. DA(COMMITMENT), @@ -96,7 +96,7 @@ where impl VoteData where - COMMITMENT: for<'a> Deserialize<'a> + Clone, + COMMITMENT: CommitmentBounds, { /// Return the underlying commitment. #[must_use] @@ -105,14 +105,14 @@ where use VoteData::*; match self { DA(c) | Yes(c) | No(c) | Timeout(c) | ViewSyncPreCommit(c) | ViewSyncCommit(c) - | ViewSyncFinalize(c) => c.clone(), + | ViewSyncFinalize(c) => *c, } } } impl VoteData where - COMMITMENT: Serialize + for<'a> Deserialize<'a>, + COMMITMENT: CommitmentBounds, { #[must_use] /// Convert vote data into bytes. @@ -135,7 +135,6 @@ pub trait VoteToken: + PartialEq + Hash + Eq - + Committable { // type StakeTable; // type KeyPair: SignatureKey; @@ -158,17 +157,17 @@ pub trait ElectionConfig: } /// A certificate of some property which has been signed by a quroum of nodes. -pub trait SignedCertificate +pub trait SignedCertificate where Self: Send + Sync + Clone + Serialize + for<'a> Deserialize<'a>, - COMMITTABLE: Committable + Serialize + Clone, + COMMITMENT: CommitmentBounds, TOKEN: VoteToken, { /// `VoteType` that is used in this certificate - type Vote: VoteType; + type Vote: VoteType; /// `Accumulator` type to accumulate votes. - type VoteAccumulator: Accumulator2; + type VoteAccumulator: Accumulator2; /// Build a QC from the threshold signature and commitment // TODO ED Rename this function and rework this function parameters @@ -189,10 +188,10 @@ where // TODO ED Make an issue for this /// Get the leaf commitment. - fn leaf_commitment(&self) -> Commitment; + fn leaf_commitment(&self) -> COMMITMENT; /// Set the leaf commitment. - fn set_leaf_commitment(&mut self, commitment: Commitment); + fn set_leaf_commitment(&mut self, commitment: COMMITMENT); /// Get whether the certificate is for the genesis block. fn is_genesis(&self) -> bool; @@ -277,7 +276,7 @@ pub trait ConsensusExchange: Send + Sync { /// Network used by [`Membership`](Self::Membership) to communicate. type Networking: CommunicationChannel; /// Commitments to items which are the subject of proposals and decisions. - type Commitment: Committable + Serialize + for<'a> Deserialize<'a> + Clone; + type Commitment: CommitmentBounds; /// Join a [`ConsensusExchange`] with the given identity (`pk` and `sk`). fn create( @@ -330,13 +329,10 @@ pub trait ConsensusExchange: Send + Sync { } /// The contents of a vote on `commit`. - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData>; + fn vote_data(&self, commit: Self::Commitment) -> VoteData; /// Validate a QC. - fn is_valid_cert(&self, qc: &Self::Certificate, commit: Commitment) -> bool { + fn is_valid_cert(&self, qc: &Self::Certificate, commit: Self::Commitment) -> bool { if qc.is_genesis() && qc.view_number() == TYPES::Time::genesis() { return true; } @@ -387,7 +383,7 @@ pub trait ConsensusExchange: Send + Sync { &self, encoded_key: &EncodedPublicKey, encoded_signature: &EncodedSignature, - data: VoteData>, + data: VoteData, vote_token: Checked, ) -> bool { let mut is_valid_vote_token = false; @@ -412,7 +408,7 @@ pub trait ConsensusExchange: Send + Sync { &self, key: &TYPES::SignatureKey, encoded_signature: &EncodedSignature, - data: &VoteData>, + data: &VoteData, vote_token: &Checked, ) -> bool { let is_valid_signature = key.validate(encoded_signature, data.get_commit().as_ref()); @@ -449,8 +445,8 @@ pub trait ConsensusExchange: Send + Sync { &self, encoded_key: &EncodedPublicKey, encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData>, + leaf_commitment: Self::Commitment, + vote_data: VoteData, vote_token: TYPES::VoteTokenType, view_number: TYPES::Time, accumlator: VoteAccumulator, @@ -476,7 +472,7 @@ pub trait ConsensusExchange: Send + Sync { TYPES::VoteTokenType, Self::Commitment, >>::Vote, - _commit: &Commitment, + _commit: &Self::Commitment, ) -> Either< <>::Certificate as SignedCertificate< TYPES, @@ -685,7 +681,7 @@ impl< type Certificate = DACertificate; type Membership = MEMBERSHIP; type Networking = NETWORK; - type Commitment = TYPES::BlockType; + type Commitment = Commitment; fn create( entries: Vec<::StakeTableEntry>, @@ -717,10 +713,7 @@ impl< .make_vote_token(view_number, &self.private_key) } - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData> { + fn vote_data(&self, commit: Self::Commitment) -> VoteData { VoteData::DA(commit) } @@ -730,8 +723,8 @@ impl< &self, encoded_key: &EncodedPublicKey, encoded_signature: &EncodedSignature, - leaf_commitment: Commitment, - vote_data: VoteData>, + leaf_commitment: Self::Commitment, + vote_data: VoteData, vote_token: TYPES::VoteTokenType, view_number: TYPES::Time, accumlator: VoteAccumulator, @@ -815,7 +808,7 @@ pub trait QuorumExchangeType, /// Create a message with a negative vote on validating or commitment proposal. fn create_no_message>( &self, - justify_qc_commitment: Commitment>, + justify_qc_commitment: Commitment>>, leaf_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, @@ -826,7 +819,7 @@ pub trait QuorumExchangeType, /// Create a message with a timeout vote on validating or commitment proposal. fn create_timeout_message>( &self, - justify_qc: QuorumCertificate, + justify_qc: QuorumCertificate>, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, ) -> GeneralConsensusMessage @@ -873,7 +866,7 @@ impl< /// Create a message with a positive vote on validating or commitment proposal. fn create_yes_message>( &self, - justify_qc_commitment: Commitment>, + justify_qc_commitment: Commitment>>, leaf_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, @@ -952,7 +945,7 @@ impl< /// Create a message with a negative vote on validating or commitment proposal. fn create_no_message>( &self, - justify_qc_commitment: Commitment>, + justify_qc_commitment: Commitment>>, leaf_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, @@ -974,7 +967,7 @@ impl< /// Create a message with a timeout vote on validating or commitment proposal. fn create_timeout_message>( &self, - high_qc: QuorumCertificate, + high_qc: QuorumCertificate>, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, ) -> GeneralConsensusMessage @@ -1003,11 +996,11 @@ impl< for QuorumExchange { type Proposal = PROPOSAL; - type Vote = QuorumVote; - type Certificate = QuorumCertificate; + type Vote = QuorumVote>; + type Certificate = QuorumCertificate>; type Membership = MEMBERSHIP; type Networking = NETWORK; - type Commitment = LEAF; + type Commitment = Commitment; fn create( entries: Vec<::StakeTableEntry>, @@ -1033,10 +1026,7 @@ impl< &self.network } - fn vote_data( - &self, - commit: Commitment, - ) -> VoteData> { + fn vote_data(&self, commit: Self::Commitment) -> VoteData { VoteData::Yes(commit) } @@ -1047,12 +1037,13 @@ impl< encoded_key: &EncodedPublicKey, encoded_signature: &EncodedSignature, leaf_commitment: Commitment, - vote_data: VoteData>, + vote_data: VoteData, vote_token: TYPES::VoteTokenType, view_number: TYPES::Time, - accumlator: VoteAccumulator, + accumlator: VoteAccumulator, TYPES>, _relay: Option, - ) -> Either, Self::Certificate> { + ) -> Either, TYPES>, Self::Certificate> + { let meta = VoteMetaData { encoded_key: encoded_key.clone(), encoded_signature: encoded_signature.clone(), @@ -1369,7 +1360,7 @@ impl< type Certificate = ViewSyncCertificate; type Membership = MEMBERSHIP; type Networking = NETWORK; - type Commitment = ViewSyncData; + type Commitment = Commitment>; fn create( entries: Vec<::StakeTableEntry>, @@ -1395,10 +1386,7 @@ impl< &self.network } - fn vote_data( - &self, - _commit: Commitment, - ) -> VoteData> { + fn vote_data(&self, _commit: Self::Commitment) -> VoteData { unimplemented!() } @@ -1407,13 +1395,15 @@ impl< encoded_key: &EncodedPublicKey, encoded_signature: &EncodedSignature, leaf_commitment: Commitment>, - vote_data: VoteData>, + vote_data: VoteData, vote_token: TYPES::VoteTokenType, view_number: TYPES::Time, - accumlator: VoteAccumulator, TYPES>, + accumlator: VoteAccumulator>, TYPES>, relay: Option, - ) -> Either, TYPES>, Self::Certificate> - { + ) -> Either< + VoteAccumulator>, TYPES>, + Self::Certificate, + > { let meta = VoteMetaData { encoded_key: encoded_key.clone(), encoded_signature: encoded_signature.clone(), diff --git a/crates/types/src/traits/state.rs b/crates/types/src/traits/state.rs index 8e6c3758ae..1db9e0754e 100644 --- a/crates/types/src/traits/state.rs +++ b/crates/types/src/traits/state.rs @@ -20,8 +20,6 @@ use std::{ /// This trait represents the behaviors that the 'global' ledger state must have: /// * A defined error type ([`Error`](State::Error)) /// * The type of block that modifies this type of state ([`BlockPayload`](State::BlockType)) -/// * A method to get a template (empty) next block from the current state -/// ([`next_block`](State::next_block)) /// * The ability to validate that a block is actually a valid extension of this state /// ([`validate_block`](State::validate_block)) /// * The ability to produce a new state, with the modifications from the block applied @@ -46,9 +44,6 @@ pub trait State: /// Time compatibility needed for reward collection type Time: ConsensusTime; - /// Returns an empty, template next block given this current state - fn next_block(prev_commitment: Option) -> Self::BlockType; - /// Returns true if and only if the provided block is valid and can extend this state fn validate_block(&self, block: &Self::BlockType, view_number: &Self::Time) -> bool; @@ -167,13 +162,6 @@ pub mod dummy { type BlockType = DummyBlock; type Time = ViewNumber; - fn next_block(state: Option) -> Self::BlockType { - match state { - Some(state) => DummyBlock { nonce: state.nonce }, - None => unimplemented!(), - } - } - fn validate_block(&self, _block: &Self::BlockType, _view_number: &Self::Time) -> bool { false } diff --git a/crates/types/src/traits/storage.rs b/crates/types/src/traits/storage.rs index 20c8f87f9a..122698486c 100644 --- a/crates/types/src/traits/storage.rs +++ b/crates/types/src/traits/storage.rs @@ -132,7 +132,7 @@ pub struct StoredView> { /// The parent of this view pub parent: Commitment, /// The justify QC of this view. See the hotstuff paper for more information on this. - pub justify_qc: QuorumCertificate, + pub justify_qc: QuorumCertificate>, /// The state of this view pub state: LEAF::MaybeState, /// The deltas of this view @@ -156,7 +156,7 @@ where /// /// Note that this will set the `parent` to `LeafHash::default()`, so this will not have a parent. pub fn from_qc_block_and_state( - qc: QuorumCertificate, + qc: QuorumCertificate>, deltas: LEAF::DeltasType, state: LEAF::MaybeState, height: u64, diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index ef53bc4018..9e232b755a 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -5,7 +5,6 @@ use crate::{ certificate::{AssembledSignature, QuorumCertificate}, - data::LeafType, traits::{ election::{VoteData, VoteToken}, node_implementation::NodeType, @@ -14,7 +13,7 @@ use crate::{ }; use bincode::Options; use bitvec::prelude::*; -use commit::{Commitment, Committable}; +use commit::{Commitment, CommitmentBounds, Committable}; use either::Either; use ethereum_types::U256; use hotshot_utils::bincode::bincode_opts; @@ -22,13 +21,14 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashMap}, fmt::Debug, + hash::Hash, marker::PhantomData, num::NonZeroU64, }; use tracing::error; /// The vote sent by consensus messages. -pub trait VoteType: +pub trait VoteType: Debug + Clone + 'static + Serialize + for<'a> Deserialize<'a> + Send + Sync + PartialEq { /// Get the view this vote was cast for @@ -38,7 +38,7 @@ pub trait VoteType EncodedSignature; /// Get the data this vote was signed over - fn get_data(&self) -> VoteData>; + fn get_data(&self) -> VoteData; /// Get the vote token of this vote fn get_vote_token(&self) -> TYPES::VoteTokenType; } @@ -62,29 +62,29 @@ pub struct DAVote { /// A positive or negative vote on validating or commitment proposal. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] -pub struct YesOrNoVote> { +pub struct YesOrNoVote { /// TODO we should remove this /// this is correct, but highly inefficient /// we should check a cache, and if that fails request the qc - pub justify_qc_commitment: Commitment>, + pub justify_qc_commitment: Commitment>, /// The signature share associated with this vote pub signature: (EncodedPublicKey, EncodedSignature), /// The leaf commitment being voted on. - pub leaf_commitment: Commitment, + pub leaf_commitment: COMMITMENT, /// The view this vote was cast for pub current_view: TYPES::Time, /// The vote token generated by this replica pub vote_token: TYPES::VoteTokenType, /// The vote data this vote is signed over - pub vote_data: VoteData>, + pub vote_data: VoteData, } /// A timeout vote. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] -pub struct TimeoutVote> { +pub struct TimeoutVote { /// The highest valid QC this node knows about - pub high_qc: QuorumCertificate, + pub high_qc: QuorumCertificate, /// The signature share associated with this vote pub signature: (EncodedPublicKey, EncodedSignature), /// The view this vote was cast for @@ -187,16 +187,16 @@ impl ViewSyncVote { /// Votes on validating or commitment proposal. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)] #[serde(bound(deserialize = ""))] -pub enum QuorumVote> { +pub enum QuorumVote { /// Posivite vote. - Yes(YesOrNoVote), + Yes(YesOrNoVote), /// Negative vote. - No(YesOrNoVote), + No(YesOrNoVote), /// Timeout vote. - Timeout(TimeoutVote), + Timeout(TimeoutVote), } -impl VoteType for DAVote { +impl VoteType> for DAVote { fn get_view(&self) -> TYPES::Time { self.current_view } @@ -223,8 +223,8 @@ impl DAVote { } } -impl> VoteType - for QuorumVote +impl VoteType + for QuorumVote { fn get_view(&self) -> TYPES::Time { match self { @@ -239,7 +239,7 @@ impl> VoteType fn get_signature(&self) -> EncodedSignature { self.signature() } - fn get_data(&self) -> VoteData> { + fn get_data(&self) -> VoteData { match self { QuorumVote::Yes(v) | QuorumVote::No(v) => v.vote_data.clone(), QuorumVote::Timeout(_) => unimplemented!(), @@ -253,7 +253,7 @@ impl> VoteType } } -impl> QuorumVote { +impl QuorumVote { /// Get the encoded signature. pub fn signature(&self) -> EncodedSignature { @@ -275,7 +275,7 @@ impl> QuorumVote } } -impl VoteType> for ViewSyncVote { +impl VoteType>> for ViewSyncVote { fn get_view(&self) -> TYPES::Time { match self { ViewSyncVote::PreCommit(v) | ViewSyncVote::Commit(v) | ViewSyncVote::Finalize(v) => { @@ -319,8 +319,8 @@ pub trait Accumulator: Sized { /// Accumulator trait used to accumulate votes into an `AssembledSignature` pub trait Accumulator2< TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, + COMMITMENT: CommitmentBounds, + VOTE: VoteType, >: Sized { /// Append 1 vote to the accumulator. If the threshold is not reached, return @@ -337,11 +337,11 @@ pub trait Accumulator2< /// Accumulates DA votes pub struct DAVoteAccumulator< TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, + COMMITMENT: CommitmentBounds, + VOTE: VoteType, > { /// Map of all da signatures accumlated so far - pub da_vote_outcomes: VoteMap, + pub da_vote_outcomes: VoteMap, /// A quorum's worth of stake, generally 2f + 1 pub success_threshold: NonZeroU64, /// A list of valid signatures for certificate aggregation @@ -352,11 +352,8 @@ pub struct DAVoteAccumulator< pub phantom: PhantomData, } -impl< - TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, - > Accumulator2 for DAVoteAccumulator +impl> + Accumulator2 for DAVoteAccumulator { fn append( mut self, @@ -428,15 +425,15 @@ impl< /// Accumulate quorum votes pub struct QuorumVoteAccumulator< TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, + COMMITMENT: CommitmentBounds, + VOTE: VoteType, > { /// Map of all signatures accumlated so far - pub total_vote_outcomes: VoteMap, + pub total_vote_outcomes: VoteMap, /// Map of all yes signatures accumlated so far - pub yes_vote_outcomes: VoteMap, + pub yes_vote_outcomes: VoteMap, /// Map of all no signatures accumlated so far - pub no_vote_outcomes: VoteMap, + pub no_vote_outcomes: VoteMap, /// A quorum's worth of stake, generally 2f + 1 pub success_threshold: NonZeroU64, @@ -450,11 +447,8 @@ pub struct QuorumVoteAccumulator< pub phantom: PhantomData, } -impl< - TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, - > Accumulator2 for QuorumVoteAccumulator +impl> + Accumulator2 for QuorumVoteAccumulator { fn append( mut self, @@ -559,15 +553,15 @@ impl< /// Accumulates view sync votes pub struct ViewSyncVoteAccumulator< TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, + COMMITMENT: CommitmentBounds, + VOTE: VoteType, > { /// Map of all pre_commit signatures accumlated so far - pub pre_commit_vote_outcomes: VoteMap, + pub pre_commit_vote_outcomes: VoteMap, /// Map of all ommit signatures accumlated so far - pub commit_vote_outcomes: VoteMap, + pub commit_vote_outcomes: VoteMap, /// Map of all finalize signatures accumlated so far - pub finalize_vote_outcomes: VoteMap, + pub finalize_vote_outcomes: VoteMap, /// A quorum's worth of stake, generally 2f + 1 pub success_threshold: NonZeroU64, @@ -581,11 +575,8 @@ pub struct ViewSyncVoteAccumulator< pub phantom: PhantomData, } -impl< - TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, - > Accumulator2 for ViewSyncVoteAccumulator +impl> + Accumulator2 for ViewSyncVoteAccumulator { #[allow(clippy::too_many_lines)] fn append( @@ -733,18 +724,15 @@ impl< /// Placeholder accumulator; will be replaced by accumulator for each certificate type pub struct AccumulatorPlaceholder< TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, + COMMITMENT: CommitmentBounds, + VOTE: VoteType, > { /// Phantom data to make compiler happy - pub phantom: PhantomData<(TYPES, VOTE, COMMITTABLE)>, + pub phantom: PhantomData<(TYPES, VOTE, COMMITMENT)>, } -impl< - TYPES: NodeType, - COMMITTABLE: Committable + Serialize + Clone, - VOTE: VoteType, - > Accumulator2 for AccumulatorPlaceholder +impl> + Accumulator2 for AccumulatorPlaceholder { fn append( self, @@ -758,17 +746,19 @@ impl< /// Mapping of commitments to vote tokens by key. // TODO ED Remove this whole token generic -type VoteMap = HashMap< - Commitment, +type VoteMap = HashMap< + COMMITMENT, ( u64, - BTreeMap>, TOKEN)>, + BTreeMap, TOKEN)>, ), >; /// Describe the process of collecting signatures on block or leaf commitment, to form a DAC or QC, /// respectively. -pub struct VoteAccumulator { +/// +/// TODO GG used only in election.rs; move this to there and make it private? +pub struct VoteAccumulator { /// Map of all signatures accumlated so far pub total_vote_outcomes: VoteMap, /// Map of all da signatures accumlated so far @@ -793,23 +783,23 @@ pub struct VoteAccumulator +impl Accumulator< ( - Commitment, + COMMITMENT, ( EncodedPublicKey, ( EncodedSignature, Vec<::StakeTableEntry>, usize, - VoteData>, + VoteData, TOKEN, ), ), ), AssembledSignature, - > for VoteAccumulator + > for VoteAccumulator where TOKEN: Clone + VoteToken, { @@ -817,14 +807,14 @@ where fn append( mut self, val: ( - Commitment, + COMMITMENT, ( EncodedPublicKey, ( EncodedSignature, Vec<::StakeTableEntry>, usize, - VoteData>, + VoteData, TOKEN, ), ), diff --git a/docs/espresso-sequencer-paper.pdf b/docs/espresso-sequencer-paper.pdf index 6f0976f7c9..a5aeb7eac6 100644 Binary files a/docs/espresso-sequencer-paper.pdf and b/docs/espresso-sequencer-paper.pdf differ diff --git a/flake.lock b/flake.lock index ec130ae4cd..99bccee207 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "cargo-careful": { "flake": false, "locked": { - "lastModified": 1693403352, - "narHash": "sha256-Ra2HtHzJvYoYslT7ryLi6rFoa/37gVJZ92/92zXtd7o=", + "lastModified": 1695361840, + "narHash": "sha256-PEoVeYjyy6xMp0T5HNYKgZPIsthMB3/SRT782D8YWdI=", "owner": "RalfJung", "repo": "cargo-careful", - "rev": "7e172de9330960658f48752c685ef34acc2e47a8", + "rev": "a338cfcfe817c6a488425db9a789fbfad09968b8", "type": "github" }, "original": { @@ -41,11 +41,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1695018106, - "narHash": "sha256-kZd7//cau0fRFqokaC9olL82j60c02yQH3sGAPSAm6Q=", + "lastModified": 1696054802, + "narHash": "sha256-VTON/WlYeyzFoYwwsb8KveqJJCfWEI6NtZYHcAFKBuo=", "owner": "nix-community", "repo": "fenix", - "rev": "529f3af3d0e02a7b474c427464b166c36c531c4e", + "rev": "3116ee073ab3931c78328ca126224833c95e6227", "type": "github" }, "original": { @@ -72,11 +72,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694948089, - "narHash": "sha256-d2B282GmQ9o8klc22/Rbbbj6r99EnELQpOQjWMyv0rU=", + "lastModified": 1695978539, + "narHash": "sha256-lta5HToBZMWZ2hl5CautNSUgIZViR41QxN7JKbMAjgQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5148520bfab61f99fd25fb9ff7bfbb50dad3c9db", + "rev": "bd9b686c0168041aea600222be0805a0de6e6ab8", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1694973195, - "narHash": "sha256-+d/GW4MyksvJmNoQLJMpKxApuZTVwaT+yuxxR/Cc/BE=", + "lastModified": 1696014495, + "narHash": "sha256-TcDFXRK9weJ1yCQyo6zxRhLfYHe/GcXDbSUpiszNCuw=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "05666441bafd6010787a4097a6bd44266ad21018", + "rev": "4791a5de21735e3d9414d131a4f973da9bae0537", "type": "github" }, "original": {