diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1aa16ecae..c8cbe47c53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,13 +72,13 @@ jobs: with: images: ghcr.io/espressosystems/hotshot/web-server - - name: Build and push web-server docker - uses: docker/build-push-action@v5 - if: matrix.just_variants == 'async_std' - with: - context: ./ - file: ./deploy/web-server.Dockerfile - platforms: linux/amd64 - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.web-server.outputs.tags }} - labels: ${{ steps.web-server.outputs.labels }} + # - name: Build and push web-server docker + # uses: docker/build-push-action@v5 + # if: matrix.just_variants == 'async_std' + # with: + # context: ./ + # file: ./deploy/web-server.Dockerfile + # platforms: linux/amd64 + # push: ${{ github.event_name != 'pull_request' }} + # tags: ${{ steps.web-server.outputs.tags }} + # labels: ${{ steps.web-server.outputs.labels }} diff --git a/Cargo.lock b/Cargo.lock index f69a93f19a..e577bc0d70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,9 +178,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -801,7 +801,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -818,7 +818,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -854,7 +854,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures-sink", "futures-util", "memchr", @@ -893,7 +893,7 @@ dependencies = [ "async-trait", "axum-core", "bitflags 1.3.2", - "bytes 1.4.0", + "bytes 1.5.0", "futures-util", "http", "http-body", @@ -919,7 +919,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", - "bytes 1.4.0", + "bytes 1.5.0", "futures-util", "http", "http-body", @@ -964,9 +964,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.3" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -1097,9 +1097,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byte-slice-cast" @@ -1121,9 +1121,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ "serde", ] @@ -1194,9 +1194,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.28" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed24df0632f708f5f6d8082675bef2596f7084dee3dd55f632290bf35bfe0f" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1265,7 +1265,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -1572,7 +1572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "704722d1d929489c8528bb1882805700f1ba20f54325704973e786352320b1ed" dependencies = [ "blake2", - "curve25519-dalek 4.0.0", + "curve25519-dalek 4.1.0", "rand_core 0.6.4", "serdect", ] @@ -1658,9 +1658,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" +checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" dependencies = [ "cfg-if", "cpufeatures", @@ -1681,7 +1681,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -1749,7 +1749,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -1771,7 +1771,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core 0.20.3", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -1865,7 +1865,7 @@ checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -1987,7 +1987,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -2046,7 +2046,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ - "curve25519-dalek 4.0.0", + "curve25519-dalek 4.1.0", "ed25519", "rand_core 0.6.4", "serde", @@ -2202,9 +2202,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.1.20" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" [[package]] name = "fixed-hash" @@ -2344,7 +2344,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -2489,7 +2489,7 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "fnv", "futures-core", "futures-sink", @@ -2645,6 +2645,7 @@ dependencies = [ "hotshot-types", "hotshot-utils", "hotshot-web-server", + "jf-primitives", "libp2p", "libp2p-identity", "libp2p-networking", @@ -2822,6 +2823,7 @@ dependencies = [ "async-lock", "async-std", "async-trait", + "bincode", "bitvec", "blake3", "commit", @@ -2848,6 +2850,7 @@ name = "hotshot-types" version = "0.1.0" dependencies = [ "arbitrary", + "ark-bls12-381", "ark-serialize 0.3.0", "ark-std 0.4.0", "async-compatibility-layer", @@ -2874,12 +2877,14 @@ dependencies = [ "hotshot-task", "hotshot-utils", "jf-primitives", + "jf-utils", "libp2p-networking", "nll", "rand 0.8.5", "rand_chacha 0.3.1", "serde", "serde_json", + "sha2 0.10.7", "snafu", "tagged-base64 0.2.4", "time 0.3.28", @@ -2931,7 +2936,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "fnv", "itoa 1.0.9", ] @@ -2942,7 +2947,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "http", "pin-project-lite 0.2.13", ] @@ -3007,7 +3012,7 @@ version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures-channel", "futures-core", "futures-util", @@ -3223,7 +3228,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", ] [[package]] @@ -3252,7 +3257,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.3", + "socket2 0.5.4", "widestring", "windows-sys", "winreg", @@ -3320,7 +3325,7 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jf-primitives" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#ef404b72acc63179b56a08c99c0cc4c6604b6669" +source = "git+https://github.com/EspressoSystems/jellyfish?rev=2fc42af#2fc42af31c955088f7ff4775cbd405a2f42c9322" dependencies = [ "anyhow", "ark-bls12-377", @@ -3365,7 +3370,7 @@ dependencies = [ [[package]] name = "jf-relation" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#ef404b72acc63179b56a08c99c0cc4c6604b6669" +source = "git+https://github.com/EspressoSystems/jellyfish?rev=2fc42af#2fc42af31c955088f7ff4775cbd405a2f42c9322" dependencies = [ "ark-bls12-377", "ark-bls12-381", @@ -3391,7 +3396,7 @@ dependencies = [ [[package]] name = "jf-utils" version = "0.4.0-pre.0" -source = "git+https://github.com/EspressoSystems/jellyfish#ef404b72acc63179b56a08c99c0cc4c6604b6669" +source = "git+https://github.com/EspressoSystems/jellyfish?rev=2fc42af#2fc42af31c955088f7ff4775cbd405a2f42c9322" dependencies = [ "ark-ec", "ark-ff", @@ -3450,9 +3455,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libnghttp2-sys" @@ -3470,7 +3475,7 @@ version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32d07d1502a027366d55afe187621c2d7895dc111a3df13b35fed698049681d7" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures", "futures-timer", "getrandom 0.2.10", @@ -3633,9 +3638,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d157562dba6017193e5285acf6b1054759e83540bfd79f75b69d6ce774c88da" dependencies = [ "asynchronous-codec", - "base64 0.21.3", + "base64 0.21.4", "byteorder", - "bytes 1.4.0", + "bytes 1.5.0", "either", "fnv", "futures", @@ -3710,7 +3715,7 @@ checksum = "fc125f83d8f75322c79e4ade74677d299b34aa5c9d9b5251c03ec28c683cb765" dependencies = [ "arrayvec", "asynchronous-codec", - "bytes 1.4.0", + "bytes 1.5.0", "either", "fnv", "futures", @@ -3747,7 +3752,7 @@ dependencies = [ "log", "rand 0.8.5", "smallvec", - "socket2 0.5.3", + "socket2 0.5.4", "tokio", "trust-dns-proto", "void", @@ -3821,8 +3826,8 @@ version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71ce70757f2c0d82e9a3ef738fb10ea0723d16cec37f078f719e2c247704c1bb" dependencies = [ - "bytes 1.4.0", - "curve25519-dalek 4.0.0", + "bytes 1.5.0", + "curve25519-dalek 4.1.0", "futures", "libp2p-core", "libp2p-identity", @@ -3865,7 +3870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37266c683a757df713f7dcda0cdcb5ad4681355ffa1b37b77c113c176a531195" dependencies = [ "asynchronous-codec", - "bytes 1.4.0", + "bytes 1.5.0", "futures", "libp2p-core", "libp2p-identity", @@ -3895,7 +3900,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cb763e88f9a043546bfebd3575f340e7dd3d6c1b2cf2629600ec8965360c63a" dependencies = [ "async-std", - "bytes 1.4.0", + "bytes 1.5.0", "futures", "futures-timer", "if-watch", @@ -3907,7 +3912,7 @@ dependencies = [ "quinn", "rand 0.8.5", "rustls", - "socket2 0.5.3", + "socket2 0.5.4", "thiserror", "tokio", ] @@ -3919,7 +3924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb07202cdf103486709fda5d9d10a0297a8ba01c212b1e19b7943c45c1bd7d6" dependencies = [ "asynchronous-codec", - "bytes 1.4.0", + "bytes 1.5.0", "either", "futures", "futures-timer", @@ -4012,7 +4017,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4029,7 +4034,7 @@ dependencies = [ "libp2p-core", "libp2p-identity", "log", - "socket2 0.5.3", + "socket2 0.5.4", "tokio", ] @@ -4413,9 +4418,9 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd59dcc2bbe70baabeac52cd22ae52c55eefe6c38ff11a9439f16a350a939f2" +checksum = "076d548d76a0e2a0d4ab471d0b1c36c577786dfc4471242035d97a12a735c492" dependencies = [ "core2", "serde", @@ -4428,7 +4433,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures", "log", "pin-project", @@ -4537,7 +4542,7 @@ name = "netlink-proto" version = "0.9.2" source = "git+https://github.com/espressosystems/netlink.git#1347bed011eeae7ece9851773906c4a3c80a5d77" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures", "log", "netlink-packet-core 0.4.2 (git+https://github.com/espressosystems/netlink.git)", @@ -4551,7 +4556,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures", "log", "netlink-packet-core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4566,7 +4571,7 @@ version = "0.8.2" source = "git+https://github.com/espressosystems/netlink.git#1347bed011eeae7ece9851773906c4a3c80a5d77" dependencies = [ "async-io", - "bytes 1.4.0", + "bytes 1.5.0", "futures", "libc", "log", @@ -4579,7 +4584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" dependencies = [ "async-io", - "bytes 1.4.0", + "bytes 1.5.0", "futures", "libc", "log", @@ -4730,9 +4735,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.92" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -4881,7 +4886,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -4912,7 +4917,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -5098,14 +5103,14 @@ checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -5130,7 +5135,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -5139,7 +5144,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "prost-derive", ] @@ -5187,7 +5192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98" dependencies = [ "asynchronous-codec", - "bytes 1.4.0", + "bytes 1.5.0", "quick-protobuf", "thiserror", "unsigned-varint", @@ -5212,7 +5217,7 @@ checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "async-io", "async-std", - "bytes 1.4.0", + "bytes 1.5.0", "futures-io", "pin-project-lite 0.2.13", "quinn-proto", @@ -5230,7 +5235,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13f81c9a9d574310b8351f8666f5a93ac3b0069c45c28ad52c10291389a7cf9" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "rand 0.8.5", "ring", "rustc-hash", @@ -5247,9 +5252,9 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "libc", - "socket2 0.5.3", + "socket2 0.5.4", "tracing", "windows-sys", ] @@ -5478,7 +5483,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "rustc-hex", ] @@ -5626,9 +5631,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.101.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" dependencies = [ "ring", "untrusted", @@ -5735,7 +5740,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -5749,9 +5754,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa 1.0.9", "ryu", @@ -5812,7 +5817,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" dependencies = [ - "base64 0.21.3", + "base64 0.21.4", "chrono", "hex", "indexmap 1.9.3", @@ -5832,7 +5837,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -5844,7 +5849,7 @@ dependencies = [ "darling 0.20.3", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -6087,7 +6092,7 @@ dependencies = [ "aes-gcm 0.9.2", "blake2", "chacha20poly1305 0.9.1", - "curve25519-dalek 4.0.0", + "curve25519-dalek 4.1.0", "rand_core 0.6.4", "ring", "rustc_version 0.4.0", @@ -6107,9 +6112,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ "libc", "windows-sys", @@ -6122,7 +6127,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" dependencies = [ "base64 0.13.1", - "bytes 1.4.0", + "bytes 1.5.0", "futures", "httparse", "log", @@ -6269,7 +6274,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -6399,9 +6404,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.31" +version = "2.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" dependencies = [ "proc-macro2", "quote", @@ -6543,7 +6548,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -6784,14 +6789,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ "backtrace", - "bytes 1.4.0", + "bytes 1.5.0", "libc", "mio", "num_cpus", "parking_lot", "pin-project-lite 0.2.13", "signal-hook-registry", - "socket2 0.5.3", + "socket2 0.5.4", "tokio-macros", "tracing", "windows-sys", @@ -6815,7 +6820,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -6835,7 +6840,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ - "bytes 1.4.0", + "bytes 1.5.0", "futures-core", "futures-sink", "pin-project-lite 0.2.13", @@ -6894,8 +6899,8 @@ checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-trait", "axum", - "base64 0.21.3", - "bytes 1.4.0", + "base64 0.21.4", + "bytes 1.5.0", "futures-core", "futures-util", "h2", @@ -6967,7 +6972,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] [[package]] @@ -7114,7 +7119,7 @@ checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.4.0", + "bytes 1.5.0", "http", "httparse", "input_buffer", @@ -7134,7 +7139,7 @@ checksum = "983d40747bce878d2fb67d910dcb8bd3eca2b2358540c3cc1b98c027407a3ae3" dependencies = [ "base64 0.13.1", "byteorder", - "bytes 1.4.0", + "bytes 1.5.0", "http", "httparse", "log", @@ -7147,9 +7152,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" @@ -7186,9 +7191,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -7227,12 +7232,12 @@ dependencies = [ [[package]] name = "unsigned-varint" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" dependencies = [ "asynchronous-codec", - "bytes 1.4.0", + "bytes 1.5.0", ] [[package]] @@ -7375,7 +7380,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", "wasm-bindgen-shared", ] @@ -7409,7 +7414,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7688,5 +7693,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.31", + "syn 2.0.33", ] diff --git a/Cargo.toml b/Cargo.toml index 312ef0fa73..e83b00d2ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ "crates/hotshot-qc", "crates/hotshot-signature-key", "crates/hotshot-stake-table", - "crates/hotshot" + "crates/hotshot", ] resolver = "2" @@ -52,9 +52,9 @@ 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" -jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish" } -jf-relation = { git = "https://github.com/EspressoSystems/jellyfish" } -jf-utils = { git = "https://github.com/espressosystems/jellyfish" } +jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish", rev = "2fc42af" } +jf-relation = { git = "https://github.com/EspressoSystems/jellyfish", rev = "2fc42af" } +jf-utils = { git = "https://github.com/espressosystems/jellyfish", rev = "2fc42af" } libp2p-identity = "0.2" libp2p-networking = { path = "./crates/libp2p-networking", version = "0.1.0", default-features = false } libp2p-swarm-derive = { version = "=0.33.0" } @@ -62,6 +62,7 @@ nll = { git = "https://github.com/EspressoSystems/nll.git" } rand = "0.8.5" rand_chacha = { version = "0.3.1", default-features = false } 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" @@ -69,46 +70,46 @@ toml = "0.7.8" tracing = "0.1.37" typenum = "1.16.0" libp2p = { package = "libp2p", version = "0.52.3", features = [ - "macros", - "autonat", - "deflate", - "dns", - "floodsub", - "gossipsub", - "identify", - "kad", - "mdns", - "noise", - "ping", - "plaintext", - "pnet", - "quic", - "relay", - "request-response", - "rendezvous", - "secp256k1", - "serde", - "tcp", - "uds", - "wasm-ext", - "websocket", - "yamux", + "macros", + "autonat", + "deflate", + "dns", + "floodsub", + "gossipsub", + "identify", + "kad", + "mdns", + "noise", + "ping", + "plaintext", + "pnet", + "quic", + "relay", + "request-response", + "rendezvous", + "secp256k1", + "serde", + "tcp", + "uds", + "wasm-ext", + "websocket", + "yamux", ] } async-std = { version = "1.12.0", features = ["attributes"] } tokio = { version = "1.32.0", features = [ - "fs", - "io-util", - "io-std", - "macros", - "net", - "parking_lot", - "process", - "rt", - "rt-multi-thread", - "signal", - "sync", - "time", - "tracing", + "fs", + "io-util", + "io-std", + "macros", + "net", + "parking_lot", + "process", + "rt", + "rt-multi-thread", + "signal", + "sync", + "time", + "tracing", ] } ### Profiles diff --git a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs index c25fad4be0..714ee98687 100644 --- a/crates/hotshot-signature-key/src/bn254/bn254_pub.rs +++ b/crates/hotshot-signature-key/src/bn254/bn254_pub.rs @@ -134,6 +134,12 @@ impl SignatureKey for BN254Pub { } } + fn get_public_key(entry: &Self::StakeTableEntry) -> Self { + Self { + pub_key: entry.stake_key, + } + } + fn get_public_parameter( stake_entries: Vec, threshold: U256, diff --git a/crates/hotshot/Cargo.toml b/crates/hotshot/Cargo.toml index 048bfabbb6..bb587595d2 100644 --- a/crates/hotshot/Cargo.toml +++ b/crates/hotshot/Cargo.toml @@ -101,6 +101,7 @@ hotshot-types = { path = "../types", version = "0.1.0", default-features = false hotshot-utils = { path = "../utils" } hotshot-task = { path = "../task", version = "0.1.0", default-features = false } hotshot-task-impls = { path = "../task-impls", version = "0.1.0", default-features = false } +jf-primitives = { workspace = true } libp2p = { workspace = true } libp2p-identity = { workspace = true } libp2p-networking = { workspace = true } @@ -122,6 +123,6 @@ async-std = { workspace = true } [dev-dependencies] blake3 = { workspace = true } clap = { version = "4.4", features = ["derive", "env"] } -serde_json = "1.0.106" +serde_json = "1.0.107" toml = { workspace = true } diff --git a/crates/hotshot/examples/infra/mod.rs b/crates/hotshot/examples/infra/mod.rs index 6a9414e79d..06d7d9deb3 100644 --- a/crates/hotshot/examples/infra/mod.rs +++ b/crates/hotshot/examples/infra/mod.rs @@ -52,7 +52,7 @@ pub fn load_config_from_file( > = config_toml.into(); // Generate network's public keys - config.config.known_nodes = (0..config.config.total_nodes.get()) + let known_nodes: Vec<_> = (0..config.config.total_nodes.get()) .map(|node_id| { TYPES::SignatureKey::generated_from_seed_indexed( config.seed, @@ -63,7 +63,7 @@ pub fn load_config_from_file( .collect(); config.config.known_nodes_with_stake = (0..config.config.total_nodes.get()) - .map(|node_id| config.config.known_nodes[node_id].get_stake_table_entry(1u64)) + .map(|node_id| known_nodes[node_id].get_stake_table_entry(1u64)) .collect(); config diff --git a/crates/hotshot/examples/infra/modDA.rs b/crates/hotshot/examples/infra/modDA.rs index 78b9fc2bf5..77d5578341 100644 --- a/crates/hotshot/examples/infra/modDA.rs +++ b/crates/hotshot/examples/infra/modDA.rs @@ -22,7 +22,7 @@ use hotshot_orchestrator::{ use hotshot_task::task::FilterEvent; use hotshot_types::{ certificate::ViewSyncCertificate, - data::{DAProposal, QuorumProposal, SequencingLeaf, TestableLeaf}, + data::{QuorumProposal, SequencingLeaf, TestableLeaf}, event::{Event, EventType}, message::{Message, SequencingMessage}, traits::{ @@ -36,7 +36,6 @@ use hotshot_types::{ }, state::{ConsensusTime, TestableBlock, TestableState}, }, - vote::{DAVote, QuorumVote, ViewSyncVote}, HotShotConfig, }; use libp2p_identity::{ @@ -80,27 +79,9 @@ use tracing::{debug, error, info, warn}; pub async fn run_orchestrator_da< TYPES: NodeType, MEMBERSHIP: Membership + Debug, - DANETWORK: CommunicationChannel< - TYPES, - Message, - DAProposal, - DAVote, - MEMBERSHIP, - > + Debug, - QUORUMNETWORK: CommunicationChannel< - TYPES, - Message, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - > + Debug, - VIEWSYNCNETWORK: CommunicationChannel< - TYPES, - Message, - ViewSyncCertificate, - ViewSyncVote, - MEMBERSHIP, - > + Debug, + DANETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + QUORUMNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + VIEWSYNCNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, NODE: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -148,27 +129,9 @@ pub async fn run_orchestrator_da< pub trait RunDA< TYPES: NodeType, MEMBERSHIP: Membership + Debug, - DANETWORK: CommunicationChannel< - TYPES, - Message, - DAProposal, - DAVote, - MEMBERSHIP, - > + Debug, - QUORUMNETWORK: CommunicationChannel< - TYPES, - Message, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - > + Debug, - VIEWSYNCNETWORK: CommunicationChannel< - TYPES, - Message, - ViewSyncCertificate, - ViewSyncVote, - MEMBERSHIP, - > + Debug, + DANETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + QUORUMNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + VIEWSYNCNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, NODE: NodeImplementation< TYPES, Leaf = SequencingLeaf, @@ -228,7 +191,6 @@ pub trait RunDA< // Get KeyPair for certificate Aggregation let (pk, sk) = TYPES::SignatureKey::generated_from_seed_indexed(config.seed, config.node_index); - let known_nodes = config.config.known_nodes.clone(); let known_nodes_with_stake = config.config.known_nodes_with_stake.clone(); let entry = pk.get_stake_table_entry(1u64); @@ -253,7 +215,6 @@ pub trait RunDA< let exchanges = NODE::Exchanges::create( known_nodes_with_stake.clone(), - known_nodes.clone(), (quorum_election_config, committee_election_config), ( quorum_network.clone(), @@ -476,32 +437,20 @@ impl< SequencingLeaf, QuorumProposal>, MEMBERSHIP, - WebCommChannel< - TYPES, - NODE, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - >, + WebCommChannel, Message, >, CommitteeExchange< TYPES, MEMBERSHIP, - WebCommChannel, DAVote, MEMBERSHIP>, + WebCommChannel, Message, >, ViewSyncExchange< TYPES, ViewSyncCertificate, MEMBERSHIP, - WebCommChannel< - TYPES, - NODE, - ViewSyncCertificate, - ViewSyncVote, - MEMBERSHIP, - >, + WebCommChannel, Message, >, >, @@ -553,21 +502,11 @@ where ); // Create the network - let quorum_network: WebCommChannel< - TYPES, - NODE, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - > = WebCommChannel::new(underlying_quorum_network.clone().into()); + let quorum_network: WebCommChannel = + WebCommChannel::new(underlying_quorum_network.clone().into()); - let view_sync_network: WebCommChannel< - TYPES, - NODE, - ViewSyncCertificate, - ViewSyncVote, - MEMBERSHIP, - > = WebCommChannel::new(underlying_quorum_network.into()); + let view_sync_network: WebCommChannel = + WebCommChannel::new(underlying_quorum_network.into()); let WebServerConfig { host, @@ -576,17 +515,10 @@ where }: WebServerConfig = config.clone().da_web_server_config.unwrap(); // Each node runs the DA network so that leaders have access to transactions and DA votes - let da_network: WebCommChannel, DAVote, MEMBERSHIP> = - WebCommChannel::new( - WebServerNetwork::create( - &host.to_string(), - port, - wait_between_polls, - pub_key, - true, - ) + let da_network: WebCommChannel = WebCommChannel::new( + WebServerNetwork::create(&host.to_string(), port, wait_between_polls, pub_key, true) .into(), - ); + ); WebServerDARun { config, @@ -596,28 +528,15 @@ where } } - fn get_da_network( - &self, - ) -> WebCommChannel, DAVote, MEMBERSHIP> { + fn get_da_network(&self) -> WebCommChannel { self.da_network.clone() } - fn get_quorum_network( - &self, - ) -> WebCommChannel< - TYPES, - NODE, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - > { + fn get_quorum_network(&self) -> WebCommChannel { self.quorum_network.clone() } - fn get_view_sync_network( - &self, - ) -> WebCommChannel, ViewSyncVote, MEMBERSHIP> - { + fn get_view_sync_network(&self) -> WebCommChannel { self.view_sync_network.clone() } @@ -922,27 +841,9 @@ where pub async fn main_entry_point< TYPES: NodeType, MEMBERSHIP: Membership + Debug, - DANETWORK: CommunicationChannel< - TYPES, - Message, - DAProposal, - DAVote, - MEMBERSHIP, - > + Debug, - QUORUMNETWORK: CommunicationChannel< - TYPES, - Message, - QuorumProposal>, - QuorumVote>, - MEMBERSHIP, - > + Debug, - VIEWSYNCNETWORK: CommunicationChannel< - TYPES, - Message, - ViewSyncCertificate, - ViewSyncVote, - MEMBERSHIP, - > + Debug, + DANETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + QUORUMNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, + VIEWSYNCNETWORK: CommunicationChannel, MEMBERSHIP> + Debug, NODE: NodeImplementation< TYPES, Leaf = SequencingLeaf, diff --git a/crates/hotshot/examples/web-server-da/types.rs b/crates/hotshot/examples/web-server-da/types.rs index 9ea1f9a694..6827a00fe8 100644 --- a/crates/hotshot/examples/web-server-da/types.rs +++ b/crates/hotshot/examples/web-server-da/types.rs @@ -25,12 +25,9 @@ pub struct NodeImpl {} pub type ThisLeaf = SequencingLeaf; pub type ThisMembership = GeneralStaticCommittee::SignatureKey>; -pub type DANetwork = - WebCommChannel; -pub type QuorumNetwork = - WebCommChannel; -pub type ViewSyncNetwork = - WebCommChannel; +pub type DANetwork = WebCommChannel; +pub type QuorumNetwork = WebCommChannel; +pub type ViewSyncNetwork = WebCommChannel; pub type ThisDAProposal = DAProposal; pub type ThisDAVote = DAVote; diff --git a/crates/hotshot/src/demos/sdemo.rs b/crates/hotshot/src/demos/sdemo.rs index 67df1c7aea..30d12bc579 100644 --- a/crates/hotshot/src/demos/sdemo.rs +++ b/crates/hotshot/src/demos/sdemo.rs @@ -28,7 +28,7 @@ use hotshot_types::{ election::Membership, node_implementation::NodeType, state::{ConsensusTime, TestableBlock, TestableState}, - Block, State, + BlockPayload, State, }, }; use rand::Rng; @@ -84,7 +84,7 @@ pub struct SDemoGenesisBlock {} /// Any block after genesis #[derive(PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)] pub struct SDemoNormalBlock { - /// Block state commitment + /// BlockPayload state commitment pub previous_state: (), /// Transaction vector pub transactions: Vec, @@ -176,10 +176,14 @@ impl Display for SDemoBlock { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SDemoBlock::Genesis(_) => { - write!(f, "SDemo Genesis Block") + write!(f, "SDemo Genesis BlockPayload") } SDemoBlock::Normal(block) => { - write!(f, "SDemo Normal Block #txns={}", block.transactions.len()) + write!( + f, + "SDemo Normal BlockPayload #txns={}", + block.transactions.len() + ) } } } @@ -198,7 +202,7 @@ impl TestableBlock for SDemoBlock { } } -impl Block for SDemoBlock { +impl BlockPayload for SDemoBlock { type Error = SDemoError; type Transaction = SDemoTransaction; @@ -280,7 +284,7 @@ impl TestableState for SDemoState { _state: Option<&Self>, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction { + ) -> ::Transaction { SDemoTransaction { id: rng.gen_range(0..10), padding: vec![0; padding as usize], diff --git a/crates/hotshot/src/lib.rs b/crates/hotshot/src/lib.rs index 4c2a42a0b4..b09ea6235d 100644 --- a/crates/hotshot/src/lib.rs +++ b/crates/hotshot/src/lib.rs @@ -36,7 +36,7 @@ use crate::{ certificate::QuorumCertificate, tasks::{ add_consensus_task, add_da_task, add_network_event_task, add_network_message_task, - add_view_sync_task, + add_transaction_task, add_view_sync_task, }, traits::{NodeImplementation, Storage}, types::{Event, SystemContextHandle}, @@ -59,7 +59,7 @@ use hotshot_task_impls::{events::SequencingHotShotEvent, network::NetworkTaskKin use hotshot_types::{ certificate::{DACertificate, ViewSyncCertificate}, consensus::{BlockStore, Consensus, ConsensusMetrics, View, ViewInner, ViewQueue}, - data::{DAProposal, DeltasType, LeafType, ProposalType, QuorumProposal, SequencingLeaf}, + data::{DAProposal, DeltasType, LeafType, QuorumProposal, SequencingLeaf}, error::StorageSnafu, message::{ ConsensusMessageType, DataMessage, InternalTrigger, Message, MessageKind, @@ -79,7 +79,7 @@ use hotshot_types::{ storage::StoredView, State, }, - vote::{ViewSyncData, VoteType}, + vote::ViewSyncData, HotShotConfig, }; use snafu::ResultExt; @@ -114,7 +114,6 @@ pub struct SystemContextInner> { /// Configuration items for this hotshot instance config: HotShotConfig< - TYPES::SignatureKey, ::StakeTableEntry, TYPES::ElectionConfigType, >, @@ -175,7 +174,6 @@ impl> SystemContext { private_key: ::PrivateKey, nonce: u64, config: HotShotConfig< - TYPES::SignatureKey, ::StakeTableEntry, TYPES::ElectionConfigType, >, @@ -400,7 +398,6 @@ impl> SystemContext { private_key: ::PrivateKey, node_id: u64, config: HotShotConfig< - TYPES::SignatureKey, ::StakeTableEntry, TYPES::ElectionConfigType, >, @@ -770,6 +767,13 @@ where handle.clone(), ) .await; + let task_runner = add_transaction_task( + task_runner, + internal_event_stream.clone(), + committee_exchange.clone(), + handle.clone(), + ) + .await; let task_runner = add_view_sync_task::( task_runner, internal_event_stream.clone(), @@ -931,10 +935,7 @@ impl< I: NodeImplementation>, > SequencingConsensusApi for HotShotSequencingConsensusApi { - async fn send_direct_message< - PROPOSAL: ProposalType, - VOTE: VoteType, - >( + async fn send_direct_message( &self, recipient: TYPES::SignatureKey, message: SequencingMessage, @@ -959,10 +960,7 @@ impl< Ok(()) } - async fn send_direct_da_message< - PROPOSAL: ProposalType, - VOTE: VoteType, - >( + async fn send_direct_da_message( &self, recipient: TYPES::SignatureKey, message: SequencingMessage, @@ -989,10 +987,7 @@ impl< // TODO (DA) Refactor ConsensusApi and HotShot to use SystemContextInner directly. // - async fn send_broadcast_message< - PROPOSAL: ProposalType, - VOTE: VoteType, - >( + async fn send_broadcast_message( &self, message: SequencingMessage, ) -> std::result::Result<(), NetworkError> { diff --git a/crates/hotshot/src/tasks/mod.rs b/crates/hotshot/src/tasks/mod.rs index e0dc9be155..619fdb6d59 100644 --- a/crates/hotshot/src/tasks/mod.rs +++ b/crates/hotshot/src/tasks/mod.rs @@ -5,6 +5,7 @@ use crate::{ QuorumCertificate, SequencingQuorumEx, }; use async_compatibility_layer::art::async_sleep; +use commit::Committable; use futures::FutureExt; use hotshot_task::{ boxed_sync, @@ -22,6 +23,7 @@ use hotshot_task_impls::{ NetworkEventTaskState, NetworkEventTaskTypes, NetworkMessageTaskState, NetworkMessageTaskTypes, NetworkTaskKind, }, + transactions::{TransactionTaskState, TransactionsTaskTypes}, view_sync::{ViewSyncTaskState, ViewSyncTaskStateTypes}, }; use hotshot_types::{ @@ -36,10 +38,11 @@ use hotshot_types::{ CommitteeEx, ExchangesType, NodeImplementation, NodeType, ViewSyncEx, }, state::ConsensusTime, - Block, + BlockPayload, }, vote::{ViewSyncData, VoteType}, }; +use serde::Serialize; use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; /// event for global event stream @@ -61,8 +64,9 @@ pub async fn add_network_message_task< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, + COMMITTABLE: Committable + Serialize + Clone, PROPOSAL: ProposalType, - VOTE: VoteType, + VOTE: VoteType, MEMBERSHIP: Membership, EXCHANGE: ConsensusExchange< TYPES, @@ -78,8 +82,7 @@ pub async fn add_network_message_task< ) -> TaskRunner // This bound is required so that we can call the `recv_msgs` function of `CommunicationChannel`. where - EXCHANGE::Networking: - CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP>, + EXCHANGE::Networking: CommunicationChannel, MEMBERSHIP>, { let channel = exchange.network().clone(); let broadcast_stream = GeneratedStream::>::new(Arc::new(move || { @@ -171,8 +174,9 @@ pub async fn add_network_event_task< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, + COMMITTABLE: Committable + Serialize + Clone, PROPOSAL: ProposalType, - VOTE: VoteType, + VOTE: VoteType, MEMBERSHIP: Membership, EXCHANGE: ConsensusExchange< TYPES, @@ -189,19 +193,16 @@ pub async fn add_network_event_task< ) -> TaskRunner // This bound is required so that we can call the `recv_msgs` function of `CommunicationChannel`. where - EXCHANGE::Networking: - CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP>, + EXCHANGE::Networking: CommunicationChannel, MEMBERSHIP>, { let filter = NetworkEventTaskState::< TYPES, I, - PROPOSAL, - VOTE, MEMBERSHIP, >::Networking, >::filter(task_kind); let channel = exchange.network().clone(); - let network_state: NetworkEventTaskState<_, _, _, _, _, _> = NetworkEventTaskState { + let network_state: NetworkEventTaskState<_, _, _, _> = NetworkEventTaskState { channel, event_stream: event_stream.clone(), view: TYPES::Time::genesis(), @@ -209,7 +210,7 @@ where }; let registry = task_runner.registry.clone(); let network_event_handler = HandleEvent(Arc::new( - move |event, mut state: NetworkEventTaskState<_, _, _, _, MEMBERSHIP, _>| { + move |event, mut state: NetworkEventTaskState<_, _, MEMBERSHIP, _>| { let membership = exchange.membership().clone(); async move { let completion_status = state.handle_event(event, &membership).await; @@ -221,7 +222,7 @@ where let networking_name = "Networking Task"; let networking_task_builder = - TaskBuilder::>::new(networking_name.to_string()) + TaskBuilder::>::new(networking_name.to_string()) .register_event_stream(event_stream.clone(), filter) .await .register_registry(&mut registry.clone()) @@ -407,6 +408,78 @@ where task_runner.add_task(da_task_id, da_name.to_string(), da_task) } +/// add the Transaction Handling task +/// # Panics +/// Is unable to panic. This section here is just to satisfy clippy +pub async fn add_transaction_task< + TYPES: NodeType, + I: NodeImplementation< + TYPES, + Leaf = SequencingLeaf, + ConsensusMessage = SequencingMessage, + >, +>( + task_runner: TaskRunner, + event_stream: ChannelStream>, + committee_exchange: CommitteeEx, + handle: SystemContextHandle, +) -> TaskRunner +where + CommitteeEx: ConsensusExchange< + TYPES, + Message, + Certificate = DACertificate, + Commitment = TYPES::BlockType, + >, +{ + // build the transactions task + let c_api: HotShotSequencingConsensusApi = HotShotSequencingConsensusApi { + inner: handle.hotshot.inner.clone(), + }; + let registry = task_runner.registry.clone(); + let transactions_state = TransactionTaskState { + registry: registry.clone(), + api: c_api.clone(), + consensus: handle.hotshot.get_consensus(), + cur_view: TYPES::Time::new(0), + committee_exchange: committee_exchange.into(), + event_stream: event_stream.clone(), + id: handle.hotshot.inner.id, + }; + let transactions_event_handler = HandleEvent(Arc::new( + move |event, + mut state: TransactionTaskState< + TYPES, + I, + HotShotSequencingConsensusApi, + >| { + async move { + let completion_status = state.handle_event(event).await; + (completion_status, state) + } + .boxed() + }, + )); + let transactions_name = "Transactions Task"; + let transactions_event_filter = FilterEvent(Arc::new( + TransactionTaskState::>::filter, + )); + + let transactions_task_builder = TaskBuilder::< + TransactionsTaskTypes>, + >::new(transactions_name.to_string()) + .register_event_stream(event_stream.clone(), transactions_event_filter) + .await + .register_registry(&mut registry.clone()) + .await + .register_state(transactions_state) + .register_event_handler(transactions_event_handler); + // impossible for unwrap to fail + // we *just* registered + let da_task_id = transactions_task_builder.get_task_id().unwrap(); + let da_task = TransactionsTaskTypes::build(transactions_task_builder).launch(); + task_runner.add_task(da_task_id, transactions_name.to_string(), da_task) +} /// add the view sync task /// # Panics /// Is unable to panic. This section here is just to satisfy clippy diff --git a/crates/hotshot/src/traits.rs b/crates/hotshot/src/traits.rs index 0f751912c5..a0e56cf86d 100644 --- a/crates/hotshot/src/traits.rs +++ b/crates/hotshot/src/traits.rs @@ -4,7 +4,7 @@ mod networking; mod node_implementation; mod storage; -pub use hotshot_types::traits::{Block, State}; +pub use hotshot_types::traits::{BlockPayload, State}; pub use networking::{NetworkError, NetworkReliability}; pub use node_implementation::{NodeImplementation, TestableNodeImplementation}; pub use storage::{Result as StorageResult, Storage}; diff --git a/crates/hotshot/src/traits/election/static_committee.rs b/crates/hotshot/src/traits/election/static_committee.rs index 2c95746d4b..44c4075e6f 100644 --- a/crates/hotshot/src/traits/election/static_committee.rs +++ b/crates/hotshot/src/traits/election/static_committee.rs @@ -19,12 +19,8 @@ use tracing::debug; #[derive(Clone, Debug, Eq, PartialEq)] pub struct GeneralStaticCommittee, PUBKEY: SignatureKey> { - /// All the nodes participating - nodes: Vec, /// All the nodes participating and their stake nodes_with_stake: Vec, - /// The nodes on the static committee - committee_nodes: Vec, /// The nodes on the static committee and their stake committee_nodes_with_stake: Vec, /// Node type phantom @@ -41,11 +37,9 @@ impl, PUBKEY: SignatureKey> { /// Creates a new dummy elector #[must_use] - pub fn new(nodes: Vec, nodes_with_stake: Vec) -> Self { + pub fn new(_nodes: &[PUBKEY], nodes_with_stake: Vec) -> Self { Self { - nodes: nodes.clone(), nodes_with_stake: nodes_with_stake.clone(), - committee_nodes: nodes, committee_nodes_with_stake: nodes_with_stake, _type_phantom: PhantomData, _leaf_phantom: PhantomData, @@ -107,8 +101,9 @@ where /// Index the vector of public keys with the current view number fn get_leader(&self, view_number: TYPES::Time) -> PUBKEY { - let index = (*view_number % self.nodes.len() as u64) as usize; - self.nodes[index].clone() + let index = (*view_number % self.nodes_with_stake.len() as u64) as usize; + let res = self.nodes_with_stake[index].clone(); + TYPES::SignatureKey::get_public_key(&res) } /// Simply make the partial signature @@ -117,9 +112,9 @@ where view_number: TYPES::Time, private_key: &::PrivateKey, ) -> std::result::Result>, ElectionError> { - // TODO ED Below let pub_key = PUBKEY::from_private(private_key); - if !self.committee_nodes.contains(&pub_key) { + let entry = pub_key.get_stake_table_entry(1u64); + if !self.committee_nodes_with_stake.contains(&entry) { return Ok(None); } let mut message: Vec = vec![]; @@ -137,7 +132,8 @@ where ) -> Result, ElectionError> { match token { Checked::Valid(t) | Checked::Unchecked(t) => { - if self.committee_nodes.contains(&pub_key) { + let entry = pub_key.get_stake_table_entry(1u64); + if self.committee_nodes_with_stake.contains(&entry) { Ok(Checked::Valid(t)) } else { Ok(Checked::Inval(t)) @@ -153,18 +149,13 @@ where fn create_election( keys_qc: Vec, - keys: Vec, config: TYPES::ElectionConfigType, ) -> Self { - let mut committee_nodes = keys.clone(); let mut committee_nodes_with_stake = keys_qc.clone(); - committee_nodes.truncate(config.num_nodes.try_into().unwrap()); debug!("Election Membership Size: {}", config.num_nodes); committee_nodes_with_stake.truncate(config.num_nodes.try_into().unwrap()); Self { nodes_with_stake: keys_qc, - nodes: keys, - committee_nodes, committee_nodes_with_stake, _type_phantom: PhantomData, _leaf_phantom: PhantomData, @@ -172,21 +163,28 @@ where } fn total_nodes(&self) -> usize { - self.committee_nodes.len() + self.committee_nodes_with_stake.len() } fn success_threshold(&self) -> NonZeroU64 { - NonZeroU64::new(((self.committee_nodes.len() as u64 * 2) / 3) + 1).unwrap() + NonZeroU64::new(((self.committee_nodes_with_stake.len() as u64 * 2) / 3) + 1).unwrap() } fn failure_threshold(&self) -> NonZeroU64 { - NonZeroU64::new(((self.committee_nodes.len() as u64) / 3) + 1).unwrap() + NonZeroU64::new(((self.committee_nodes_with_stake.len() as u64) / 3) + 1).unwrap() } fn get_committee( &self, _view_number: ::Time, ) -> std::collections::BTreeSet<::SignatureKey> { - self.committee_nodes.clone().into_iter().collect() + // Transfer from committee_nodes_with_stake to pure committee_nodes + (0..self.committee_nodes_with_stake.len()) + .map(|node_id| { + ::SignatureKey::get_public_key( + &self.committee_nodes_with_stake[node_id], + ) + }) + .collect() } } diff --git a/crates/hotshot/src/traits/networking/libp2p_network.rs b/crates/hotshot/src/traits/networking/libp2p_network.rs index ddc13b9571..da8784e0a7 100644 --- a/crates/hotshot/src/traits/networking/libp2p_network.rs +++ b/crates/hotshot/src/traits/networking/libp2p_network.rs @@ -15,7 +15,7 @@ use bincode::Options; use hotshot_constants::{KAD_DEFAULT_REPUB_INTERVAL_SEC, LOOK_AHEAD}; use hotshot_task::{boxed_sync, BoxSyncFuture}; use hotshot_types::{ - data::{ProposalType, ViewNumber}, + data::ViewNumber, message::{Message, MessageKind}, traits::{ election::Membership, @@ -29,7 +29,6 @@ use hotshot_types::{ signature_key::SignatureKey, state::ConsensusTime, }, - vote::VoteType, }; use hotshot_utils::bincode::bincode_opts; use libp2p_identity::PeerId; @@ -608,7 +607,7 @@ impl ConnectedNetwork for Libp2p source: NetworkNodeHandleError::NoSuchTopic, })? .clone(); - error!("Broadcasting to topic {}", topic); + info!("broadcasting to topic: {}", topic); // gossip doesn't broadcast from itself, so special case if recipients.contains(&self.inner.pk) { @@ -759,21 +758,14 @@ impl ConnectedNetwork for Libp2p pub struct Libp2pCommChannel< TYPES: NodeType, I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, >( Arc, TYPES::SignatureKey>>, - PhantomData<(TYPES, I, PROPOSAL, VOTE, MEMBERSHIP)>, + PhantomData<(TYPES, I, MEMBERSHIP)>, ); -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > Libp2pCommChannel +impl, MEMBERSHIP: Membership> + Libp2pCommChannel { /// create a new libp2p communication channel #[must_use] @@ -782,14 +774,9 @@ impl< } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > TestableNetworkingImplementation> - for Libp2pCommChannel +impl, MEMBERSHIP: Membership> + TestableNetworkingImplementation> + for Libp2pCommChannel where MessageKind: ViewMessage, { @@ -831,14 +818,9 @@ where // top // we don't really want to make this the default implementation because that forces it to require ConnectedNetwork to be implemented. The struct we implement over might use multiple ConnectedNetworks #[async_trait] -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP> - for Libp2pCommChannel +impl, MEMBERSHIP: Membership> + CommunicationChannel, MEMBERSHIP> + for Libp2pCommChannel where MessageKind: ViewMessage, { @@ -912,21 +894,13 @@ where } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > +impl, MEMBERSHIP: Membership> TestableChannelImplementation< TYPES, Message, - PROPOSAL, - VOTE, MEMBERSHIP, Libp2pNetwork, TYPES::SignatureKey>, - > for Libp2pCommChannel + > for Libp2pCommChannel { fn generate_network( ) -> Box, TYPES::SignatureKey>>) -> Self + 'static> diff --git a/crates/hotshot/src/traits/networking/memory_network.rs b/crates/hotshot/src/traits/networking/memory_network.rs index d18b938f06..51f7bbbdf7 100644 --- a/crates/hotshot/src/traits/networking/memory_network.rs +++ b/crates/hotshot/src/traits/networking/memory_network.rs @@ -16,7 +16,6 @@ use dashmap::DashMap; use futures::StreamExt; use hotshot_task::{boxed_sync, BoxSyncFuture}; use hotshot_types::{ - data::ProposalType, message::{Message, MessageKind}, traits::{ election::Membership, @@ -28,7 +27,6 @@ use hotshot_types::{ node_implementation::NodeType, signature_key::SignatureKey, }, - vote::VoteType, }; use hotshot_utils::bincode::bincode_opts; use rand::Rng; @@ -454,21 +452,14 @@ impl ConnectedNetwork for Memory pub struct MemoryCommChannel< TYPES: NodeType, I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, >( Arc, TYPES::SignatureKey>>, - PhantomData<(I, PROPOSAL, VOTE, MEMBERSHIP)>, + PhantomData<(I, MEMBERSHIP)>, ); -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > MemoryCommChannel +impl, MEMBERSHIP: Membership> + MemoryCommChannel { /// create new communication channel #[must_use] @@ -477,14 +468,9 @@ impl< } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > TestableNetworkingImplementation> - for MemoryCommChannel +impl, MEMBERSHIP: Membership> + TestableNetworkingImplementation> + for MemoryCommChannel where MessageKind: ViewMessage, { @@ -514,14 +500,9 @@ where } #[async_trait] -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP> - for MemoryCommChannel +impl, MEMBERSHIP: Membership> + CommunicationChannel, MEMBERSHIP> + for MemoryCommChannel where MessageKind: ViewMessage, { @@ -579,21 +560,13 @@ where } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > +impl, MEMBERSHIP: Membership> TestableChannelImplementation< TYPES, Message, - PROPOSAL, - VOTE, MEMBERSHIP, MemoryNetwork, TYPES::SignatureKey>, - > for MemoryCommChannel + > for MemoryCommChannel { fn generate_network( ) -> Box, TYPES::SignatureKey>>) -> Self + 'static> diff --git a/crates/hotshot/src/traits/networking/web_server_libp2p_fallback.rs b/crates/hotshot/src/traits/networking/web_server_libp2p_fallback.rs index 170ad630f5..b3324681a5 100644 --- a/crates/hotshot/src/traits/networking/web_server_libp2p_fallback.rs +++ b/crates/hotshot/src/traits/networking/web_server_libp2p_fallback.rs @@ -13,7 +13,7 @@ use futures::join; use async_compatibility_layer::channel::UnboundedSendError; use hotshot_task::{boxed_sync, BoxSyncFuture}; use hotshot_types::{ - data::{ProposalType, ViewNumber}, + data::ViewNumber, message::Message, traits::{ election::Membership, @@ -24,7 +24,6 @@ use hotshot_types::{ }, node_implementation::NodeType, }, - vote::VoteType, }; use std::{marker::PhantomData, sync::Arc}; use tracing::error; @@ -156,13 +155,8 @@ impl, MEMBERSHIP: Membership, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP> +impl, MEMBERSHIP: Membership> + CommunicationChannel, MEMBERSHIP> for WebServerWithFallbackCommChannel { type NETWORK = CombinedNetworks; @@ -292,18 +286,10 @@ impl< } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > +impl, MEMBERSHIP: Membership> TestableChannelImplementation< TYPES, Message, - PROPOSAL, - VOTE, MEMBERSHIP, CombinedNetworks, > for WebServerWithFallbackCommChannel diff --git a/crates/hotshot/src/traits/networking/web_server_network.rs b/crates/hotshot/src/traits/networking/web_server_network.rs index 374c8a40e7..72cd62cc8d 100644 --- a/crates/hotshot/src/traits/networking/web_server_network.rs +++ b/crates/hotshot/src/traits/networking/web_server_network.rs @@ -13,7 +13,6 @@ use async_lock::RwLock; use async_trait::async_trait; use hotshot_task::{boxed_sync, BoxSyncFuture}; use hotshot_types::{ - data::ProposalType, message::{Message, MessagePurpose}, traits::{ election::Membership, @@ -25,7 +24,6 @@ use hotshot_types::{ node_implementation::{NodeImplementation, NodeType}, signature_key::SignatureKey, }, - vote::VoteType, }; use hotshot_web_server::{self, config}; use rand::random; @@ -48,21 +46,14 @@ use tracing::{debug, error, info}; pub struct WebCommChannel< TYPES: NodeType, I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, >( Arc, TYPES::SignatureKey, TYPES>>, - PhantomData<(MEMBERSHIP, I, PROPOSAL, VOTE)>, + PhantomData<(MEMBERSHIP, I)>, ); -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > WebCommChannel +impl, MEMBERSHIP: Membership> + WebCommChannel { /// Create new communication channel #[must_use] @@ -167,6 +158,7 @@ impl Inner { while self.running.load(Ordering::Relaxed) { let endpoint = match message_purpose { MessagePurpose::Proposal => config::get_proposal_route(view_number), + MessagePurpose::CurrentProposal => config::get_recent_proposal_route(), MessagePurpose::Vote => config::get_vote_route(view_number, vote_index), MessagePurpose::Data => config::get_transactions_route(tx_index), MessagePurpose::Internal => unimplemented!(), @@ -177,6 +169,9 @@ impl Inner { config::get_view_sync_vote_route(view_number, vote_index) } MessagePurpose::DAC => config::get_da_certificate_route(view_number), + MessagePurpose::VidDisperse => config::get_vid_disperse_route(view_number), // like `Proposal` + MessagePurpose::VidVote => config::get_vid_vote_route(view_number, vote_index), // like `Vote` + MessagePurpose::VidCert => config::get_vid_cert_route(view_number), // like `DAC` }; if message_purpose == MessagePurpose::Data { @@ -227,6 +222,15 @@ impl Inner { // } // } } + MessagePurpose::CurrentProposal => { + // Only pushing the first proposal since we will soon only be allowing 1 proposal per view + self.broadcast_poll_queue + .write() + .await + .push(deserialized_messages[0].clone()); + + return Ok(()); + } MessagePurpose::Vote => { // error!( // "Received {} votes from web server for view {} is da {}", @@ -240,6 +244,14 @@ impl Inner { direct_poll_queue.push(vote.clone()); } } + MessagePurpose::VidVote => { + // TODO copy-pasted from `MessagePurpose::Vote` https://github.com/EspressoSystems/HotShot/issues/1690 + let mut direct_poll_queue = self.direct_poll_queue.write().await; + for vote in &deserialized_messages { + vote_index += 1; + direct_poll_queue.push(vote.clone()); + } + } MessagePurpose::DAC => { debug!( "Received DAC from web server for view {} {}", @@ -255,6 +267,41 @@ impl Inner { // In future we should check to make sure DAC is valid return Ok(()); } + MessagePurpose::VidCert => { + // TODO copy-pasted from `MessagePurpose::DAC` https://github.com/EspressoSystems/HotShot/issues/1690 + debug!( + "Received VID cert from web server for view {} {}", + view_number, self.is_da + ); + // Only pushing the first proposal since we will soon only be allowing 1 proposal per view + self.broadcast_poll_queue + .write() + .await + .push(deserialized_messages[0].clone()); + + // return if we found a VID cert, since there will only be 1 per view + // In future we should check to make sure VID cert is valid + return Ok(()); + } + MessagePurpose::VidDisperse => { + // TODO copy-pasted from `MessagePurpose::Proposal` https://github.com/EspressoSystems/HotShot/issues/1690 + + // Only pushing the first proposal since we will soon only be allowing 1 proposal per view + self.broadcast_poll_queue + .write() + .await + .push(deserialized_messages[0].clone()); + + return Ok(()); + // Wait for the view to change before polling for proposals again + // let event = receiver.recv().await; + // match event { + // Ok(event) => view_number = event.view_number(), + // Err(_r) => { + // error!("Proposal receiver error! It was likely shutdown") + // } + // } + } MessagePurpose::ViewSyncVote => { // error!( // "Received {} view sync votes from web server for view {} is da {}", @@ -506,13 +553,18 @@ impl< MessagePurpose::Proposal => config::post_proposal_route(*view_number), MessagePurpose::Vote => config::post_vote_route(*view_number), MessagePurpose::Data => config::post_transactions_route(), - MessagePurpose::Internal => return Err(WebServerNetworkError::EndpointError), + MessagePurpose::Internal | MessagePurpose::CurrentProposal => { + return Err(WebServerNetworkError::EndpointError) + } MessagePurpose::ViewSyncProposal => { // error!("Posting view sync proposal route is: {}", config::post_view_sync_proposal_route(*view_number)); config::post_view_sync_proposal_route(*view_number) } MessagePurpose::ViewSyncVote => config::post_view_sync_vote_route(*view_number), MessagePurpose::DAC => config::post_da_certificate_route(*view_number), + MessagePurpose::VidVote => config::post_vid_vote_route(*view_number), + MessagePurpose::VidDisperse => config::post_vid_disperse_route(*view_number), + MessagePurpose::VidCert => config::post_vid_cert_route(*view_number), }; let network_msg: SendMsg = SendMsg { @@ -524,14 +576,9 @@ impl< } #[async_trait] -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP> - for WebCommChannel +impl, MEMBERSHIP: Membership> + CommunicationChannel, MEMBERSHIP> + for WebCommChannel { type NETWORK = WebServerNetwork, TYPES::SignatureKey, TYPES>; /// Blocks until node is successfully initialized @@ -777,6 +824,25 @@ impl< .await; } } + ConsensusIntentEvent::PollForCurrentProposal => { + // create new task + let (_, receiver) = unbounded(); + + async_spawn({ + let inner_clone = self.inner.clone(); + async move { + if let Err(e) = inner_clone + .poll_web_server(receiver, MessagePurpose::CurrentProposal, 1) + .await + { + error!( + "Background receive proposal polling encountered an error: {:?}", + e + ); + } + } + }); + } ConsensusIntentEvent::PollForVotes(view_number) => { let mut task_map = self.inner.vote_task_map.write().await; if let Entry::Vacant(e) = task_map.entry(view_number) { @@ -1046,14 +1112,9 @@ impl> } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > TestableNetworkingImplementation> - for WebCommChannel +impl, MEMBERSHIP: Membership> + TestableNetworkingImplementation> + for WebCommChannel { fn generator( expected_node_count: usize, @@ -1081,21 +1142,13 @@ impl< } } -impl< - TYPES: NodeType, - I: NodeImplementation, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, - > +impl, MEMBERSHIP: Membership> TestableChannelImplementation< TYPES, Message, - PROPOSAL, - VOTE, MEMBERSHIP, WebServerNetwork, TYPES::SignatureKey, TYPES>, - > for WebCommChannel + > for WebCommChannel { fn generate_network() -> Box< dyn Fn(Arc, TYPES::SignatureKey, TYPES>>) -> Self diff --git a/crates/hotshot/src/traits/storage/atomic_storage.rs b/crates/hotshot/src/traits/storage/atomic_storage.rs index 9d054fe1ac..4654d65ca7 100644 --- a/crates/hotshot/src/traits/storage/atomic_storage.rs +++ b/crates/hotshot/src/traits/storage/atomic_storage.rs @@ -33,7 +33,7 @@ where atomic_store: Mutex, /// The Blocks stored by this [`AtomicStorage`] - blocks: HashMapStore, STATE::Block>, + blocks: HashMapStore, STATE::BlockPayload>, /// The [`QuorumCertificate`]s stored by this [`AtomicStorage`] qcs: DualKeyValueStore>, @@ -142,15 +142,15 @@ impl Storage for AtomicStorage { #[instrument(name = "AtomicStorage::get_block", skip_all)] async fn get_block( &self, - hash: &Commitment, - ) -> StorageResult> { + hash: &Commitment, + ) -> StorageResult> { Ok(self.inner.blocks.get(hash).await) } #[instrument(name = "AtomicStorage::get_qc", skip_all)] async fn get_qc( &self, - hash: &Commitment, + hash: &Commitment, ) -> StorageResult>> { Ok(self.inner.qcs.load_by_key_1_ref(hash).await) } @@ -176,7 +176,7 @@ impl Storage for AtomicStorage { #[instrument(name = "AtomicStorage::get_leaf_by_block", skip_all)] async fn get_leaf_by_block( &self, - hash: &Commitment, + hash: &Commitment, ) -> StorageResult>> { Ok(self.inner.leaves.load_by_key_2_ref(hash).await) } @@ -187,7 +187,7 @@ impl Storage for AtomicStorage { } async fn get_internal_state(&self) -> StorageState { - let mut blocks: Vec<(Commitment, STATE::Block)> = + let mut blocks: Vec<(Commitment, STATE::BBlockPayloadlock)> = self.inner.blocks.load_all().await.into_iter().collect(); blocks.sort_by_key(|(hash, _)| *hash); @@ -226,8 +226,8 @@ impl<'a, STATE: StateContents + 'static> StorageUpdater<'a, STATE> #[instrument(name = "AtomicStorage::get_block", skip_all)] async fn insert_block( &mut self, - hash: Commitment, - block: STATE::Block, + hash: Commitment, + block: STATE::BlockPayload, ) -> StorageResult { trace!(?block, "inserting block"); self.inner diff --git a/crates/hotshot/src/traits/storage/atomic_storage/dual_key_value_store.rs b/crates/hotshot/src/traits/storage/atomic_storage/dual_key_value_store.rs index 70431af012..84cf8c76a2 100644 --- a/crates/hotshot/src/traits/storage/atomic_storage/dual_key_value_store.rs +++ b/crates/hotshot/src/traits/storage/atomic_storage/dual_key_value_store.rs @@ -181,7 +181,7 @@ pub trait DualKeyValue: Serialize + DeserializeOwned + Clone { } impl DualKeyValue for QuorumCertificate { - type Key1 = Commitment; + type Key1 = Commitment; type Key2 = ViewNumber; const KEY_1_NAME: &'static str = "block_commitment"; @@ -200,7 +200,7 @@ where STATE: StateContents, { type Key1 = Commitment>; - type Key2 = Commitment; + type Key2 = Commitment; const KEY_1_NAME: &'static str = "leaf_commitment"; const KEY_2_NAME: &'static str = "block_commitment"; @@ -210,6 +210,6 @@ where } fn key_2(&self) -> Self::Key2 { - ::commit(&self.deltas) + ::commit(&self.deltas) } } diff --git a/crates/hotshot/src/traits/storage/memory_storage.rs b/crates/hotshot/src/traits/storage/memory_storage.rs index 42c1487e53..dbc631d379 100644 --- a/crates/hotshot/src/traits/storage/memory_storage.rs +++ b/crates/hotshot/src/traits/storage/memory_storage.rs @@ -125,7 +125,7 @@ mod test { block_contents::dummy::{DummyBlock, DummyState}, node_implementation::NodeType, state::ConsensusTime, - Block, + BlockPayload, }, }; use std::{fmt::Debug, hash::Hash}; @@ -151,7 +151,7 @@ mod test { type BlockType = DummyBlock; type SignatureKey = BN254Pub; type VoteTokenType = StaticVoteToken; - type Transaction = ::Transaction; + type Transaction = ::Transaction; type ElectionConfigType = StaticElectionConfig; type StateType = DummyState; } diff --git a/crates/libp2p-networking/Cargo.toml b/crates/libp2p-networking/Cargo.toml index 9e0efc24d3..82b5ed7d87 100644 --- a/crates/libp2p-networking/Cargo.toml +++ b/crates/libp2p-networking/Cargo.toml @@ -42,7 +42,7 @@ libp2p-noise = { version = "0.43.0", default-features = false } parking_lot = "0.12.1" rand = { workspace = true } serde = { workspace = true } -serde_json = "1.0.106" +serde_json = "1.0.107" snafu = { workspace = true } tide = { version = "0.16", optional = true, default-features = false, features = [ "h1-server", diff --git a/crates/libp2p-networking/src/network/behaviours/gossip.rs b/crates/libp2p-networking/src/network/behaviours/gossip.rs index 1119ad27f3..bfc946c9b5 100644 --- a/crates/libp2p-networking/src/network/behaviours/gossip.rs +++ b/crates/libp2p-networking/src/network/behaviours/gossip.rs @@ -4,13 +4,13 @@ use std::{ }; use libp2p::{ - gossipsub::{Behaviour, Event, IdentTopic, TopicHash}, + gossipsub::{Behaviour, Event, IdentTopic, PublishError::Duplicate, TopicHash}, swarm::{NetworkBehaviour, PollParameters, THandlerInEvent, THandlerOutEvent, ToSwarm}, Multiaddr, }; use libp2p_identity::PeerId; -use tracing::{error, info, warn}; +use tracing::{debug, error, info, warn}; use super::exponential_backoff::ExponentialBackoff; @@ -214,8 +214,12 @@ impl GossipBehaviour { /// Publish a given gossip pub fn publish_gossip(&mut self, topic: IdentTopic, contents: Vec) { let res = self.gossipsub.publish(topic.clone(), contents.clone()); - if res.is_err() { - error!("error publishing gossip message {:?}", res); + if let Err(e) = res { + if matches!(e, Duplicate) { + debug!("duplicate gossip message"); + } else { + error!("error publishing gossip message {:?}", e); + } self.in_progress_gossip.push_back((topic, contents)); } } diff --git a/crates/libp2p-networking/tests/counter.rs b/crates/libp2p-networking/tests/counter.rs index e7dad7c8c6..85b7cb2a7f 100644 --- a/crates/libp2p-networking/tests/counter.rs +++ b/crates/libp2p-networking/tests/counter.rs @@ -12,7 +12,7 @@ use libp2p_networking::network::{ use serde::{Deserialize, Serialize}; use snafu::ResultExt; use std::{fmt::Debug, sync::Arc, time::Duration}; -use tracing::{error, info, instrument, warn}; +use tracing::{debug, error, info, instrument, warn}; #[cfg(async_executor_impl = "async-std")] use async_std::prelude::StreamExt; @@ -307,7 +307,7 @@ async fn run_dht_rounds( ) { let mut rng = rand::thread_rng(); for i in 0..num_rounds { - error!("round: {:?}", i); + debug!("begin round {}", i); let msg_handle = get_random_handle(handles, &mut rng); let mut key = vec![0; DHT_KV_PADDING]; key.push((starting_val + i) as u8); diff --git a/crates/orchestrator/Cargo.toml b/crates/orchestrator/Cargo.toml index 3febbbce65..f3f57ada3a 100644 --- a/crates/orchestrator/Cargo.toml +++ b/crates/orchestrator/Cargo.toml @@ -23,7 +23,8 @@ tracing = { workspace = true } serde = { workspace = true } serde_json = "1.0.96" snafu = { workspace = true } -toml = "0.5.9" # TODO GG upgrade to toml = { workspace = true } +# TODO upgrade to toml = { workspace = true } https://github.com/EspressoSystems/HotShot/issues/1698 +toml = "0.5.9" [target.'cfg(all(async_executor_impl = "tokio"))'.dependencies] tokio = { workspace = true } diff --git a/crates/orchestrator/src/config.rs b/crates/orchestrator/src/config.rs index 061ab256d5..05a38b615c 100644 --- a/crates/orchestrator/src/config.rs +++ b/crates/orchestrator/src/config.rs @@ -1,4 +1,5 @@ use hotshot_types::{ExecutionType, HotShotConfig}; +use std::marker::PhantomData; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr}, num::NonZeroUsize, @@ -65,9 +66,10 @@ pub struct NetworkConfig { pub key_type_name: String, pub election_config_type_name: String, pub libp2p_config: Option, - pub config: HotShotConfig, + pub config: HotShotConfig, pub web_server_config: Option, pub da_web_server_config: Option, + _key_type_phantom: PhantomData, } impl Default for NetworkConfig { @@ -85,6 +87,7 @@ impl Default for NetworkConfig { election_config_type_name: std::any::type_name::().to_string(), web_server_config: None, da_web_server_config: None, + _key_type_phantom: PhantomData, } } } @@ -152,6 +155,7 @@ impl From for NetworkConfig { start_delay_seconds: val.start_delay_seconds, web_server_config: val.web_server_config, da_web_server_config: val.da_web_server_config, + _key_type_phantom: PhantomData, } } } @@ -183,14 +187,13 @@ pub struct HotShotConfigFile { pub propose_max_round_time: Duration, } -impl From for HotShotConfig { +impl From for HotShotConfig { fn from(val: HotShotConfigFile) -> Self { HotShotConfig { execution_type: ExecutionType::Continuous, total_nodes: val.total_nodes, max_transactions: val.max_transactions, min_transactions: val.min_transactions, - known_nodes: Vec::new(), known_nodes_with_stake: Vec::new(), da_committee_size: val.committee_nodes, next_view_timeout: val.next_view_timeout, diff --git a/crates/orchestrator/src/lib.rs b/crates/orchestrator/src/lib.rs index 021ae756eb..93efa501e7 100644 --- a/crates/orchestrator/src/lib.rs +++ b/crates/orchestrator/src/lib.rs @@ -173,7 +173,15 @@ where fn post_ready(&mut self) -> Result<(), ServerError> { self.nodes_connected += 1; println!("Nodes connected: {}", self.nodes_connected); - if self.nodes_connected >= self.config.config.known_nodes.len().try_into().unwrap() { + if self.nodes_connected + >= self + .config + .config + .known_nodes_with_stake + .len() + .try_into() + .unwrap() + { self.start = true; } Ok(()) diff --git a/crates/task-impls/src/consensus.rs b/crates/task-impls/src/consensus.rs index 8767e96af2..dccf7919c3 100644 --- a/crates/task-impls/src/consensus.rs +++ b/crates/task-impls/src/consensus.rs @@ -1,12 +1,8 @@ use crate::events::SequencingHotShotEvent; -use async_compatibility_layer::{ - art::{async_sleep, async_spawn}, - async_primitives::subscribable_rwlock::ReadView, -}; +use async_compatibility_layer::art::{async_sleep, async_spawn}; use async_lock::{RwLock, RwLockUpgradableReadGuard}; #[cfg(async_executor_impl = "async-std")] use async_std::task::JoinHandle; -use bincode::Options; use bitvec::prelude::*; use commit::Committable; use core::time::Duration; @@ -19,6 +15,7 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; +use hotshot_types::vote::QuorumVoteAccumulator; use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, consensus::{Consensus, View}, @@ -32,12 +29,12 @@ use hotshot_types::{ node_implementation::{CommitteeEx, NodeImplementation, NodeType, SequencingQuorumEx}, signature_key::SignatureKey, state::ConsensusTime, - Block, + BlockPayload, }, utils::{Terminator, ViewInner}, - vote::{QuorumVote, VoteAccumulator, VoteType}, + vote::{QuorumVote, VoteType}, }; -use hotshot_utils::bincode::bincode_opts; + use snafu::Snafu; use std::{ collections::{HashMap, HashSet}, @@ -149,8 +146,15 @@ pub struct VoteCollectionTaskState< pub quorum_exchange: Arc>, #[allow(clippy::type_complexity)] /// Accumulator for votes - pub accumulator: - Either, QuorumCertificate>, + pub accumulator: Either< + > as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + SequencingLeaf, + >>::VoteAccumulator, + QuorumCertificate>, + >, /// View which this vote collection task is collecting votes in pub cur_view: TYPES::Time, /// The event stream shared by all tasks @@ -190,33 +194,28 @@ where Commitment = SequencingLeaf, >, { - // TODO ED Emit a view change event upon new proposal? match event { - SequencingHotShotEvent::QuorumVoteRecv(vote) => match vote { - QuorumVote::Yes(vote) => { + SequencingHotShotEvent::QuorumVoteRecv(vote) => match vote.clone() { + QuorumVote::Yes(vote_internal) => { // For the case where we receive votes after we've made a certificate if state.accumulator.is_right() { return (None, state); } - if vote.current_view != state.cur_view { + if vote_internal.current_view != state.cur_view { error!( "Vote view does not match! vote view is {} current view is {}", - *vote.current_view, *state.cur_view + *vote_internal.current_view, *state.cur_view ); return (None, state); } let accumulator = state.accumulator.left().unwrap(); - match state.quorum_exchange.accumulate_vote( - &vote.signature.0, - &vote.signature.1, - vote.leaf_commitment, - vote.vote_data, - vote.vote_token.clone(), - state.cur_view, + + match state.quorum_exchange.accumulate_vote_2( accumulator, - None, + &vote, + &vote_internal.leaf_commitment, ) { Either::Left(acc) => { state.accumulator = Either::Left(acc); @@ -367,10 +366,7 @@ where ); if let GeneralConsensusMessage::Vote(vote) = message { - debug!( - "Sending vote to next quorum leader {:?}", - vote.current_view() - ); + debug!("Sending vote to next quorum leader {:?}", vote.get_view()); self.event_stream .publish(SequencingHotShotEvent::QuorumVoteSend(vote)) .await; @@ -443,12 +439,8 @@ where }; - // TODO ED Only publish event in vote if able if let GeneralConsensusMessage::Vote(vote) = message { - debug!( - "Sending vote to next quorum leader {:?}", - vote.current_view() - ); + debug!("Sending vote to next quorum leader {:?}", vote.get_view()); self.event_stream .publish(SequencingHotShotEvent::QuorumVoteSend(vote)) .await; @@ -489,6 +481,13 @@ where self.cur_view = new_view; self.current_proposal = None; + if new_view == TYPES::Time::new(1) { + self.quorum_exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::PollForCurrentProposal) + .await; + } + // Poll the future leader for lookahead let lookahead_view = new_view + LOOK_AHEAD; if !self.quorum_exchange.is_leader(lookahead_view) { @@ -556,7 +555,7 @@ where let view = proposal.data.get_view_number(); if view < self.cur_view { - error!("view too high"); + debug!("Proposal is from an older view {:?}", proposal.data.clone()); return; } @@ -800,46 +799,10 @@ where } #[allow(clippy::cast_precision_loss)] if new_decide_reached { - let mut included_txn_size = 0; - let mut included_txn_count = 0; - let txns = consensus.transactions.cloned().await; - // store transactions in this block we never added to our transactions. - let _ = included_txns_set.iter().map(|hash| { - if !txns.contains_key(hash) { - consensus.seen_transactions.insert(*hash); - } - }); - drop(txns); - consensus - .transactions - .modify(|txns| { - *txns = txns - .drain() - .filter(|(txn_hash, txn)| { - if included_txns_set.contains(txn_hash) { - included_txn_count += 1; - included_txn_size += bincode_opts() - .serialized_size(txn) - .unwrap_or_default(); - false - } else { - true - } - }) - .collect(); - }) - .await; - - consensus - .metrics - .outstanding_transactions - .update(-included_txn_count); - consensus - .metrics - .outstanding_transactions_memory_size - .update(-(i64::try_from(included_txn_size).unwrap_or(i64::MAX))); - debug!("about to publish decide"); + self.event_stream + .publish(SequencingHotShotEvent::LeafDecided(leaf_views.clone())) + .await; let decide_sent = self.output_event_stream.publish(Event { view_number: consensus.last_decided_view, event: EventType::Decide { @@ -880,7 +843,8 @@ where "Attempting to publish proposal after voting; now in view: {}", *new_view ); - self.publish_proposal_if_able(qc).await; + self.publish_proposal_if_able(qc.clone(), qc.view_number + 1) + .await; } if !self.vote_if_able().await { // TOOD ED This means we publish the proposal without updating our own view, which doesn't seem right @@ -903,26 +867,26 @@ where } } SequencingHotShotEvent::QuorumVoteRecv(vote) => { - debug!("Received quroum vote: {:?}", vote.current_view()); + debug!("Received quroum vote: {:?}", vote.get_view()); - if !self.quorum_exchange.is_leader(vote.current_view() + 1) { + if !self.quorum_exchange.is_leader(vote.get_view() + 1) { error!( "We are not the leader for view {} are we the leader for view + 1? {}", - *vote.current_view() + 1, - self.quorum_exchange.is_leader(vote.current_view() + 2) + *vote.get_view() + 1, + self.quorum_exchange.is_leader(vote.get_view() + 2) ); return; } - match vote { - QuorumVote::Yes(vote) => { + match vote.clone() { + QuorumVote::Yes(vote_internal) => { let handle_event = HandleEvent(Arc::new(move |event, state| { async move { vote_handle(state, event).await }.boxed() })); let collection_view = if let Some((collection_view, collection_task, _)) = &self.vote_collector { - if vote.current_view > *collection_view { + if vote_internal.current_view > *collection_view { // ED I think we'd want to let that task timeout to avoid a griefing vector self.registry.shutdown_task(*collection_task).await; } @@ -931,37 +895,31 @@ where TYPES::Time::new(0) }; - let acc = VoteAccumulator { + // Todo check if we are the leader + let new_accumulator = QuorumVoteAccumulator { total_vote_outcomes: HashMap::new(), - da_vote_outcomes: HashMap::new(), yes_vote_outcomes: HashMap::new(), no_vote_outcomes: HashMap::new(), - viewsync_precommit_vote_outcomes: HashMap::new(), - viewsync_commit_vote_outcomes: HashMap::new(), - viewsync_finalize_vote_outcomes: HashMap::new(), + success_threshold: self.quorum_exchange.success_threshold(), failure_threshold: self.quorum_exchange.failure_threshold(), + sig_lists: Vec::new(), signers: bitvec![0; self.quorum_exchange.total_nodes()], + phantom: PhantomData, }; - // Todo check if we are the leader - let accumulator = self.quorum_exchange.accumulate_vote( - &vote.clone().signature.0, - &vote.clone().signature.1, - vote.clone().leaf_commitment, - vote.clone().vote_data.clone(), - vote.clone().vote_token.clone(), - vote.clone().current_view, - acc, - None, + let accumulator = self.quorum_exchange.accumulate_vote_2( + new_accumulator, + &vote, + &vote_internal.clone().leaf_commitment, ); - if vote.current_view > collection_view { + if vote_internal.current_view > collection_view { let state = VoteCollectionTaskState { quorum_exchange: self.quorum_exchange.clone(), accumulator, - cur_view: vote.current_view, + cur_view: vote_internal.current_view, event_stream: self.event_stream.clone(), id: self.id, }; @@ -981,17 +939,22 @@ where let id = builder.get_task_id().unwrap(); let stream_id = builder.get_stream_id().unwrap(); - self.vote_collector = Some((vote.current_view, id, stream_id)); + self.vote_collector = Some((vote_internal.current_view, id, stream_id)); let _task = async_spawn(async move { VoteCollectionTypes::build(builder).launch().await; }); - debug!("Starting vote handle for view {:?}", vote.current_view); + debug!( + "Starting vote handle for view {:?}", + vote_internal.current_view + ); } else if let Some((_, _, stream_id)) = self.vote_collector { self.event_stream .direct_message( stream_id, - SequencingHotShotEvent::QuorumVoteRecv(QuorumVote::Yes(vote)), + SequencingHotShotEvent::QuorumVoteRecv(QuorumVote::Yes( + vote_internal, + )), ) .await; } @@ -1032,7 +995,10 @@ where *qc.view_number ); - if self.publish_proposal_if_able(qc.clone()).await { + if self + .publish_proposal_if_able(qc.clone(), qc.view_number + 1) + .await + { self.update_view(qc.view_number + 1).await; } } @@ -1047,7 +1013,17 @@ where self.update_view(view + 1).await; } } + SequencingHotShotEvent::VidCertRecv(cert) => { + debug!("VID cert received for view ! {}", *cert.view_number); + let view = cert.view_number; + self.certs.insert(view, cert); // TODO new cert type for VID https://github.com/EspressoSystems/HotShot/issues/1701 + + // TODO Make sure we aren't voting for an arbitrarily old round for no reason + if self.vote_if_able().await { + self.update_view(view + 1).await; + } + } SequencingHotShotEvent::ViewChange(new_view) => { debug!("View Change event for view {}", *new_view); @@ -1081,7 +1057,7 @@ where let consensus = self.consensus.read().await; let qc = consensus.high_qc.clone(); drop(consensus); - if !self.publish_proposal_if_able(qc).await { + if !self.publish_proposal_if_able(qc, self.cur_view).await { error!( "Failed to publish proposal on view change. View = {:?}", self.cur_view @@ -1109,12 +1085,15 @@ where } /// Sends a proposal if possible from the high qc we have - pub async fn publish_proposal_if_able(&self, qc: QuorumCertificate) -> bool { - // TODO ED This should not be qc view number + 1 - if !self.quorum_exchange.is_leader(qc.view_number + 1) { + pub async fn publish_proposal_if_able( + &self, + _qc: QuorumCertificate, + view: TYPES::Time, + ) -> bool { + if !self.quorum_exchange.is_leader(view) { error!( "Somehow we formed a QC but are not the leader for the next view {:?}", - qc.view_number + 1 + view ); return false; } @@ -1173,11 +1152,11 @@ where let block_commitment = self.block.commit(); if block_commitment == TYPES::BlockType::new().commit() { - debug!("Block is generic block! {:?}", self.cur_view); + debug!("BlockPayload is generic block! {:?}", self.cur_view); } let leaf = SequencingLeaf { - view_number: *parent_view_number + 1, + view_number: view, height: parent_leaf.height + 1, justify_qc: consensus.high_qc.clone(), parent_commitment: parent_leaf.commit(), @@ -1311,6 +1290,7 @@ pub fn consensus_event_filter>( | SequencingHotShotEvent::QuorumVoteRecv(_) | SequencingHotShotEvent::QCFormed(_) | SequencingHotShotEvent::DACRecv(_) + | SequencingHotShotEvent::VidCertRecv(_) | SequencingHotShotEvent::ViewChange(_) | SequencingHotShotEvent::SendDABlockData(_) | SequencingHotShotEvent::Timeout(_) diff --git a/crates/task-impls/src/da.rs b/crates/task-impls/src/da.rs index c7d20786ab..c24982980f 100644 --- a/crates/task-impls/src/da.rs +++ b/crates/task-impls/src/da.rs @@ -1,10 +1,7 @@ use crate::events::SequencingHotShotEvent; -use async_compatibility_layer::{ - art::{async_spawn, async_timeout}, - async_primitives::subscribable_rwlock::ReadView, -}; +use async_compatibility_layer::art::async_spawn; use async_lock::RwLock; -use bincode::config::Options; + use bitvec::prelude::*; use commit::Committable; use either::{Either, Left, Right}; @@ -15,11 +12,14 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; +use hotshot_types::traits::election::SignedCertificate; +use hotshot_types::vote::DAVoteAccumulator; +use hotshot_types::vote::VoteType; use hotshot_types::{ certificate::DACertificate, consensus::{Consensus, View}, - data::{DAProposal, ProposalType, SequencingLeaf}, - message::{CommitteeConsensusMessage, Message, Proposal, SequencingMessage}, + data::{DAProposal, ProposalType, SequencingLeaf, VidDisperse, VidScheme, VidSchemeTrait}, + message::{Message, Proposal, SequencingMessage}, traits::{ consensus_api::SequencingConsensusApi, election::{CommitteeExchangeType, ConsensusExchange, Membership}, @@ -27,18 +27,14 @@ use hotshot_types::{ node_implementation::{CommitteeEx, NodeImplementation, NodeType}, signature_key::SignatureKey, state::ConsensusTime, - Block, State, + BlockPayload, }, utils::ViewInner, - vote::VoteAccumulator, }; -use hotshot_utils::bincode::bincode_opts; + use snafu::Snafu; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, - time::Instant, -}; +use std::marker::PhantomData; +use std::{collections::HashMap, sync::Arc}; use tracing::{debug, error, instrument, warn}; #[derive(Snafu, Debug)] @@ -70,7 +66,6 @@ pub struct DATaskState< /// View number this view is executing in. pub cur_view: TYPES::Time, - // pub transactions: Arc>>, /// Reference to consensus. Leader will require a read lock on this. pub consensus: Arc>>>, @@ -101,10 +96,18 @@ pub struct DAVoteCollectionTaskState< { /// the committee exchange pub committee_exchange: Arc>, - /// the vote accumulator - pub accumulator: - Either, DACertificate>, - // TODO ED Make this just "view" since it is only for this task + + #[allow(clippy::type_complexity)] + /// Accumulates DA votes + pub accumulator: Either< + as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + TYPES::BlockType, + >>::VoteAccumulator, + DACertificate, + >, /// the current view pub cur_view: TYPES::Time, /// event stream for channel events @@ -153,21 +156,16 @@ where } let accumulator = state.accumulator.left().unwrap(); - match state.committee_exchange.accumulate_vote( - &vote.signature.0, - &vote.signature.1, - vote.block_commitment, - vote.vote_data, - vote.vote_token.clone(), - state.cur_view, + + match state.committee_exchange.accumulate_vote_2( accumulator, - None, + &vote, + &vote.block_commitment, ) { - Left(acc) => { - state.accumulator = Either::Left(acc); - // debug!("Not enough DA votes! "); - return (None, state); + Left(new_accumulator) => { + state.accumulator = either::Left(new_accumulator); } + Right(dac) => { debug!("Sending DAC! {:?}", dac.view_number); state @@ -192,8 +190,51 @@ where } } } + SequencingHotShotEvent::VidVoteRecv(vote) => { + // TODO copy-pasted from DAVoteRecv https://github.com/EspressoSystems/HotShot/issues/1690 + + debug!("VID vote recv, collection task {:?}", vote.get_view()); + // panic!("Vote handle received DA vote for view {}", *vote.current_view); + + let accumulator = state.accumulator.left().unwrap(); + + match state.committee_exchange.accumulate_vote_2( + accumulator, + &vote, + &vote.block_commitment, + ) { + Left(new_accumulator) => { + state.accumulator = either::Left(new_accumulator); + } + + Right(vid_cert) => { + debug!("Sending VID cert! {:?}", vid_cert.view_number); + state + .event_stream + .publish(SequencingHotShotEvent::VidCertSend( + vid_cert.clone(), + state.committee_exchange.public_key().clone(), + )) + .await; + + state.accumulator = Right(vid_cert.clone()); + state + .committee_exchange + .network() + .inject_consensus_info(ConsensusIntentEvent::CancelPollForVotes( + *vid_cert.view_number, + )) + .await; + + // Return completed at this point + return (Some(HotShotTaskCompleted::ShutDown), state); + } + } + } SequencingHotShotEvent::Shutdown => return (Some(HotShotTaskCompleted::ShutDown), state), - _ => {} + _ => { + error!("unexpected event {:?}", event); + } } (None, state) } @@ -217,41 +258,11 @@ where { /// main task event handler #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "DA Main Task", level = "error")] - pub async fn handle_event( &mut self, event: SequencingHotShotEvent, ) -> Option { match event { - SequencingHotShotEvent::TransactionsRecv(transactions) => { - // TODO ED Add validation checks - - let mut consensus = self.consensus.write().await; - consensus - .get_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()) - && txns.insert(transaction.commit(), transaction).is_none() - { - consensus.metrics.outstanding_transactions.update(1); - consensus - .metrics - .outstanding_transactions_memory_size - .update(i64::try_from(size).unwrap_or_else(|e| { - warn!("Conversion failed: {e}. Using the max value."); - i64::MAX - })); - } - } - }) - .await; - - return None; - } SequencingHotShotEvent::DAProposalRecv(proposal, sender) => { debug!( "DA proposal received for view: {:?}", @@ -298,7 +309,7 @@ where } Ok(Some(vote_token)) => { // Generate and send vote - let message = self.committee_exchange.create_da_message( + let vote = self.committee_exchange.create_da_message( block_commitment, view, vote_token, @@ -307,12 +318,10 @@ where // ED Don't think this is necessary? // self.cur_view = view; - if let CommitteeConsensusMessage::DAVote(vote) = message { - debug!("Sending vote to the DA leader {:?}", vote.current_view); - self.event_stream - .publish(SequencingHotShotEvent::DAVoteSend(vote)) - .await; - } + debug!("Sending vote to the DA leader {:?}", vote.current_view); + self.event_stream + .publish(SequencingHotShotEvent::DAVoteSend(vote)) + .await; let mut consensus = self.consensus.write().await; // Ensure this view is in the view map for garbage collection, but do not overwrite if @@ -356,32 +365,25 @@ where } else { TYPES::Time::new(0) }; - let acc = VoteAccumulator { - total_vote_outcomes: HashMap::new(), + + let new_accumulator = DAVoteAccumulator { da_vote_outcomes: HashMap::new(), - yes_vote_outcomes: HashMap::new(), - no_vote_outcomes: HashMap::new(), - viewsync_precommit_vote_outcomes: HashMap::new(), - viewsync_commit_vote_outcomes: HashMap::new(), - viewsync_finalize_vote_outcomes: HashMap::new(), success_threshold: self.committee_exchange.success_threshold(), - failure_threshold: self.committee_exchange.failure_threshold(), sig_lists: Vec::new(), signers: bitvec![0; self.committee_exchange.total_nodes()], + phantom: PhantomData, }; - let accumulator = self.committee_exchange.accumulate_vote( - &vote.clone().signature.0, - &vote.clone().signature.1, - vote.clone().block_commitment, - vote.clone().vote_data.clone(), - vote.clone().vote_token.clone(), - vote.clone().current_view, - acc, - None, + + let accumulator = self.committee_exchange.accumulate_vote_2( + new_accumulator, + &vote, + &vote.clone().block_commitment, ); + if view > collection_view { let state = DAVoteCollectionTaskState { committee_exchange: self.committee_exchange.clone(), + accumulator, cur_view: view, event_stream: self.event_stream.clone(), @@ -412,7 +414,163 @@ where .await; }; } - // TODO ED Update high QC through QCFormed event + SequencingHotShotEvent::VidVoteRecv(vote) => { + // TODO copy-pasted from DAVoteRecv https://github.com/EspressoSystems/HotShot/issues/1690 + + // warn!( + // "VID vote recv, Main Task {:?}, key: {:?}", + // vote.current_view, + // self.committee_exchange.public_key() + // ); + // Check if we are the leader and the vote is from the sender. + let view = vote.current_view; + if !self.committee_exchange.is_leader(view) { + error!( + "We are not the VID leader for view {} are we leader for next view? {}", + *view, + self.committee_exchange.is_leader(view + 1) + ); + return None; + } + + let handle_event = HandleEvent(Arc::new(move |event, state| { + async move { vote_handle(state, event).await }.boxed() + })); + let collection_view = + if let Some((collection_view, collection_id, _)) = &self.vote_collector { + // TODO: Is this correct for consecutive leaders? + if view > *collection_view { + // warn!("shutting down for view {:?}", collection_view); + self.registry.shutdown_task(*collection_id).await; + } + *collection_view + } else { + TYPES::Time::new(0) + }; + + let new_accumulator = DAVoteAccumulator { + da_vote_outcomes: HashMap::new(), + success_threshold: self.committee_exchange.success_threshold(), + sig_lists: Vec::new(), + signers: bitvec![0; self.committee_exchange.total_nodes()], + phantom: PhantomData, + }; + + let accumulator = self.committee_exchange.accumulate_vote_2( + new_accumulator, + &vote, + &vote.clone().block_commitment, + ); + + if view > collection_view { + let state = DAVoteCollectionTaskState { + committee_exchange: self.committee_exchange.clone(), + + accumulator, + cur_view: view, + event_stream: self.event_stream.clone(), + id: self.id, + }; + let name = "VID Vote Collection"; + let filter = FilterEvent(Arc::new(|event| { + matches!(event, SequencingHotShotEvent::VidVoteRecv(_)) + })); + let builder = + TaskBuilder::>::new(name.to_string()) + .register_event_stream(self.event_stream.clone(), filter) + .await + .register_registry(&mut self.registry.clone()) + .await + .register_state(state) + .register_event_handler(handle_event); + let id = builder.get_task_id().unwrap(); + let stream_id = builder.get_stream_id().unwrap(); + let _task = + async_spawn( + async move { DAVoteCollectionTypes::build(builder).launch().await }, + ); + self.vote_collector = Some((view, id, stream_id)); + } else if let Some((_, _, stream_id)) = self.vote_collector { + self.event_stream + .direct_message(stream_id, SequencingHotShotEvent::VidVoteRecv(vote)) + .await; + }; + } + SequencingHotShotEvent::VidDisperseRecv(disperse, sender) => { + // TODO copy-pasted from DAProposalRecv https://github.com/EspressoSystems/HotShot/issues/1690 + debug!( + "VID disperse received for view: {:?}", + disperse.data.get_view_number() + ); + + // ED NOTE: Assuming that the next view leader is the one who sends DA proposal for this view + let view = disperse.data.get_view_number(); + + // Allow a DA proposal that is one view older, in case we have voted on a quorum + // proposal and updated the view. + // `self.cur_view` should be at least 1 since there is a view change before getting + // the `DAProposalRecv` event. Otherewise, the view number subtraction below will + // cause an overflow error. + if view < self.cur_view - 1 { + warn!("Throwing away VID disperse data that is more than one view older"); + return None; + } + + debug!("VID disperse data is fresh."); + let block_commitment = disperse.data.commitment; + + // ED Is this the right leader? + let view_leader_key = self.committee_exchange.get_leader(view); + if view_leader_key != sender { + error!("VID proposal doesn't have expected leader key for view {} \n DA proposal is: [N/A for VID]", *view); + return None; + } + + if !view_leader_key.validate(&disperse.signature, block_commitment.as_ref()) { + error!("Could not verify VID proposal sig."); + return None; + } + + let vote_token = self.committee_exchange.make_vote_token(view); + match vote_token { + Err(e) => { + error!("Failed to generate vote token for {:?} {:?}", view, e); + } + Ok(None) => { + debug!("We were not chosen for VID quorum on {:?}", view); + } + Ok(Some(vote_token)) => { + // Generate and send vote + let vote = self.committee_exchange.create_vid_message( + block_commitment, + view, + vote_token, + ); + + // ED Don't think this is necessary? + // self.cur_view = view; + + debug!("Sending vote to the VID leader {:?}", vote.current_view); + self.event_stream + .publish(SequencingHotShotEvent::VidVoteSend(vote)) + .await; + let mut consensus = self.consensus.write().await; + + // Ensure this view is in the view map for garbage collection, but do not overwrite if + // there is already a view there: the replica task may have inserted a `Leaf` view which + // contains strictly more information. + consensus.state_map.entry(view).or_insert(View { + view_inner: ViewInner::DA { + block: block_commitment, + }, + }); + + // Record the block we have promised to make available. + // TODO https://github.com/EspressoSystems/HotShot/issues/1692 + // consensus.saved_blocks.insert(proposal.data.deltas); + } + } + } SequencingHotShotEvent::ViewChange(view) => { if *self.cur_view >= *view { return None; @@ -425,7 +583,6 @@ where // Inject view info into network // ED I think it is possible that you receive a quorum proposal, vote on it and update your view before the da leader has sent their proposal, and therefore you skip polling for this view? - // TODO ED Only poll if you are on the committee let is_da = self .committee_exchange .membership() @@ -451,8 +608,6 @@ where .await; } - // TODO ED Make this a new task so it doesn't block main DA task - // If we are not the next leader (DA leader for this view) immediately exit if !self.committee_exchange.is_leader(self.cur_view + 1) { // panic!("We are not the DA leader for view {}", *self.cur_view + 1); @@ -466,62 +621,19 @@ where .inject_consensus_info(ConsensusIntentEvent::PollForVotes(*self.cur_view + 1)) .await; - // ED Copy of parent_leaf() function from sequencing leader - - let consensus = self.consensus.read().await; - let parent_view_number = &consensus.high_qc.view_number; - - let Some(parent_view) = consensus.state_map.get(parent_view_number) else { - error!( - "Couldn't find high QC parent in state map. Parent view {:?}", - parent_view_number - ); - return None; - }; - let Some(leaf) = parent_view.get_leaf_commitment() else { - error!( - ?parent_view_number, - ?parent_view, - "Parent of high QC points to a view without a proposal" - ); - return None; - }; - let Some(leaf) = consensus.saved_leaves.get(&leaf) else { - error!("Failed to find high QC parent."); - return None; - }; - let parent_leaf = leaf.clone(); - - // Prepare the DA Proposal - // let Some(parent_leaf) = self.parent_leaf().await else { - // warn!("Couldn't find high QC parent in state map."); - // return None; - // }; - - drop(consensus); - - let mut block = ::StateType::next_block(None); - let txns = self.wait_for_transactions(parent_leaf).await?; - + return None; + } + SequencingHotShotEvent::BlockReady(block, view) => { self.committee_exchange .network() - .inject_consensus_info(ConsensusIntentEvent::CancelPollForTransactions( - *self.cur_view + 1, - )) + .inject_consensus_info(ConsensusIntentEvent::CancelPollForTransactions(*view)) .await; - for txn in txns { - if let Ok(new_block) = block.add_transaction_raw(&txn) { - block = new_block; - continue; - } - } - let signature = self.committee_exchange.sign_da_proposal(&block.commit()); let data: DAProposal = DAProposal { deltas: block.clone(), // Upon entering a new view we want to send a DA Proposal for the next view -> Is it always the case that this is cur_view + 1? - view_number: self.cur_view + 1, + view_number: view, }; debug!("Sending DA proposal for view {:?}", data.view_number); @@ -543,12 +655,43 @@ where // } self.event_stream .publish(SequencingHotShotEvent::DAProposalSend( - message, + message.clone(), self.committee_exchange.public_key().clone(), )) .await; - return None; + 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 (shares, common) = vid.dispersal_data(&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(), + shares, + 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) => { @@ -561,73 +704,11 @@ where SequencingHotShotEvent::Shutdown => { return Some(HotShotTaskCompleted::ShutDown); } - _ => {} - } - None - } - - /// return None if we can't get transactions - #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "DA Vote Collection Task", level = "error")] - - async fn wait_for_transactions( - &self, - parent_leaf: SequencingLeaf, - ) -> Option> { - let task_start_time = Instant::now(); - - // 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 consensus = self.consensus.read().await; - - let receiver = consensus.transactions.subscribe().await; - - loop { - let all_txns = consensus.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(); - - let time_past = task_start_time.elapsed(); - if unclaimed_txns.len() < self.api.min_transactions() - && (time_past < self.api.propose_max_round_time()) - { - let duration = self.api.propose_max_round_time() - time_past; - let result = async_timeout(duration, receiver.recv()).await; - match result { - Err(_) => { - // Fall through below to updating new block - error!( - "propose_max_round_time passed, sending transactions we have so far" - ); - } - Ok(Err(e)) => { - // Something unprecedented is wrong, and `transactions` has been dropped - error!("Channel receiver error for SubscribableRwLock {:?}", e); - return None; - } - Ok(Ok(_)) => continue, - } + _ => { + error!("unexpected event {:?}", event); } - 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(); - Some(txns) + None } /// Filter the DA event. @@ -637,8 +718,10 @@ where SequencingHotShotEvent::DAProposalRecv(_, _) | SequencingHotShotEvent::DAVoteRecv(_) | SequencingHotShotEvent::Shutdown - | SequencingHotShotEvent::TransactionsRecv(_) + | SequencingHotShotEvent::BlockReady(_, _) | SequencingHotShotEvent::Timeout(_) + | SequencingHotShotEvent::VidDisperseRecv(_, _) + | SequencingHotShotEvent::VidVoteRecv(_) | SequencingHotShotEvent::ViewChange(_) ) } diff --git a/crates/task-impls/src/events.rs b/crates/task-impls/src/events.rs index 98819108a4..de8e31fd03 100644 --- a/crates/task-impls/src/events.rs +++ b/crates/task-impls/src/events.rs @@ -1,6 +1,6 @@ use hotshot_types::{ certificate::{DACertificate, QuorumCertificate}, - data::DAProposal, + data::{DAProposal, VidDisperse}, message::Proposal, traits::node_implementation::{ NodeImplementation, NodeType, QuorumProposalType, ViewSyncProposalType, @@ -62,4 +62,32 @@ pub enum SequencingHotShotEvent> { TransactionSend(TYPES::Transaction, TYPES::SignatureKey), /// Event to send DA block data from DA leader to next quorum leader (which should always be the same node); internal event only SendDABlockData(TYPES::BlockType), + /// Event when the transactions task has a block formed + BlockReady(TYPES::BlockType, TYPES::Time), + /// Event when consensus decided on a leaf + LeafDecided(Vec), + /// Send VID shares to VID storage nodes; emitted by the DA leader + /// + /// Like [`DAProposalSend`]. + VidDisperseSend(Proposal>, TYPES::SignatureKey), + /// Vid disperse data has been received from the network; handled by the DA task + /// + /// Like [`DAProposalRecv`]. + VidDisperseRecv(Proposal>, TYPES::SignatureKey), + /// Send a VID vote to the VID leader; emitted by VID storage nodes in the DA task after seeing a valid VID dispersal + /// + /// Like [`DAVoteSend`] + VidVoteSend(DAVote), + /// A VID vote has been received by the network; handled by the DA task + /// + /// Like [`DAVoteRecv`] + VidVoteRecv(DAVote), + /// The VID leader has collected enough votes to form a VID cert; emitted by the VID leader in the DA task; sent to the entire network via the networking task + /// + /// Like [`DACSend`] + VidCertSend(DACertificate, TYPES::SignatureKey), + /// A VID cert has been recieved by the network; handled by the consensus task + /// + /// Like [`DACRecv`] + VidCertRecv(DACertificate), } diff --git a/crates/task-impls/src/lib.rs b/crates/task-impls/src/lib.rs index aca2cafdea..5e7492d84a 100644 --- a/crates/task-impls/src/lib.rs +++ b/crates/task-impls/src/lib.rs @@ -17,6 +17,9 @@ pub mod consensus; /// The task which implements the main parts of data availability. pub mod da; +/// The task which implements all transaction handling +pub mod transactions; + /// Defines the events passed between tasks pub mod events; diff --git a/crates/task-impls/src/network.rs b/crates/task-impls/src/network.rs index a4b083ba37..5fbada8539 100644 --- a/crates/task-impls/src/network.rs +++ b/crates/task-impls/src/network.rs @@ -7,7 +7,7 @@ use hotshot_task::{ GeneratedStream, Merge, }; use hotshot_types::{ - data::{ProposalType, SequencingLeaf}, + data::SequencingLeaf, message::{ CommitteeConsensusMessage, GeneralConsensusMessage, Message, MessageKind, Messages, SequencingMessage, @@ -106,6 +106,15 @@ impl< // panic!("Recevid DA C! "); SequencingHotShotEvent::DACRecv(cert) } + CommitteeConsensusMessage::VidDisperseMsg(proposal) => { + SequencingHotShotEvent::VidDisperseRecv(proposal, sender) + } + CommitteeConsensusMessage::VidVote(vote) => { + SequencingHotShotEvent::VidVoteRecv(vote.clone()) + } + CommitteeConsensusMessage::VidCertificate(cert) => { + SequencingHotShotEvent::VidCertRecv(cert) + } }, }; // TODO (Keyao benchmarking) Update these event variants (similar to the @@ -137,10 +146,8 @@ pub struct NetworkEventTaskState< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, - COMMCHANNEL: CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP>, + COMMCHANNEL: CommunicationChannel, MEMBERSHIP>, > { /// comm channel pub channel: COMMCHANNEL, @@ -149,7 +156,7 @@ pub struct NetworkEventTaskState< /// view number pub view: TYPES::Time, /// phantom data - pub phantom: PhantomData<(PROPOSAL, VOTE, MEMBERSHIP)>, + pub phantom: PhantomData, // TODO ED Need to add exchange so we can get the recipient key and our own key? } @@ -160,11 +167,9 @@ impl< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, - COMMCHANNEL: CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP>, - > TS for NetworkEventTaskState + COMMCHANNEL: CommunicationChannel, MEMBERSHIP>, + > TS for NetworkEventTaskState { } @@ -175,17 +180,16 @@ impl< Leaf = SequencingLeaf, ConsensusMessage = SequencingMessage, >, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, - COMMCHANNEL: CommunicationChannel, PROPOSAL, VOTE, MEMBERSHIP>, - > NetworkEventTaskState + COMMCHANNEL: CommunicationChannel, MEMBERSHIP>, + > NetworkEventTaskState { /// Handle the given event. /// /// Returns the completion status. /// # Panics /// Panic sif a direct message event is received with no recipient + #[allow(clippy::too_many_lines)] // TODO https://github.com/EspressoSystems/HotShot/issues/1704 pub async fn handle_event( &mut self, event: SequencingHotShotEvent, @@ -208,9 +212,16 @@ impl< GeneralConsensusMessage::Vote(vote.clone()), ))), TransmitType::Direct, - Some(membership.get_leader(vote.current_view() + 1)), + Some(membership.get_leader(vote.get_view() + 1)), + ), + SequencingHotShotEvent::VidDisperseSend(proposal, sender) => ( + sender, + MessageKind::::from_consensus_message(SequencingMessage(Right( + CommitteeConsensusMessage::VidDisperseMsg(proposal), + ))), // TODO not a CommitteeConsensusMessage https://github.com/EspressoSystems/HotShot/issues/1696 + TransmitType::Broadcast, // TODO not a broadcast https://github.com/EspressoSystems/HotShot/issues/1696 + None, ), - SequencingHotShotEvent::DAProposalSend(proposal, sender) => ( sender, MessageKind::::from_consensus_message(SequencingMessage(Right( @@ -219,6 +230,14 @@ impl< TransmitType::Broadcast, None, ), + SequencingHotShotEvent::VidVoteSend(vote) => ( + vote.signature_key(), + MessageKind::::from_consensus_message(SequencingMessage(Right( + CommitteeConsensusMessage::VidVote(vote.clone()), + ))), + TransmitType::Direct, + Some(membership.get_leader(vote.current_view)), // TODO who is VID leader? https://github.com/EspressoSystems/HotShot/issues/1699 + ), SequencingHotShotEvent::DAVoteSend(vote) => ( vote.signature_key(), MessageKind::::from_consensus_message(SequencingMessage(Right( @@ -227,6 +246,14 @@ impl< TransmitType::Direct, Some(membership.get_leader(vote.current_view)), ), + SequencingHotShotEvent::VidCertSend(certificate, sender) => ( + sender, + MessageKind::::from_consensus_message(SequencingMessage(Right( + CommitteeConsensusMessage::VidCertificate(certificate), + ))), + TransmitType::Broadcast, + None, + ), // ED NOTE: This needs to be broadcasted to all nodes, not just ones on the DA committee SequencingHotShotEvent::DACSend(certificate, sender) => ( sender, @@ -307,6 +334,7 @@ impl< | SequencingHotShotEvent::QuorumVoteSend(_) | SequencingHotShotEvent::Shutdown | SequencingHotShotEvent::DACSend(_, _) + | SequencingHotShotEvent::VidCertSend(_, _) | SequencingHotShotEvent::ViewChange(_) ) } @@ -318,6 +346,8 @@ impl< SequencingHotShotEvent::DAProposalSend(_, _) | SequencingHotShotEvent::DAVoteSend(_) | SequencingHotShotEvent::Shutdown + | SequencingHotShotEvent::VidDisperseSend(_, _) + | SequencingHotShotEvent::VidVoteSend(_) | SequencingHotShotEvent::ViewChange(_) ) } @@ -348,9 +378,9 @@ pub type NetworkMessageTaskTypes = HSTWithMessage< >; /// network event task types -pub type NetworkEventTaskTypes = HSTWithEvent< +pub type NetworkEventTaskTypes = HSTWithEvent< NetworkTaskError, SequencingHotShotEvent, ChannelStream>, - NetworkEventTaskState, + NetworkEventTaskState, >; diff --git a/crates/task-impls/src/transactions.rs b/crates/task-impls/src/transactions.rs new file mode 100644 index 0000000000..8609708653 --- /dev/null +++ b/crates/task-impls/src/transactions.rs @@ -0,0 +1,345 @@ +use crate::events::SequencingHotShotEvent; +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 hotshot_task::{ + event_stream::{ChannelStream, EventStream}, + global_registry::GlobalRegistry, + task::{HotShotTaskCompleted, TS}, + task_impls::HSTWithEvent, +}; +use hotshot_types::{ + certificate::DACertificate, + consensus::Consensus, + data::SequencingLeaf, + message::{Message, SequencingMessage}, + traits::{ + consensus_api::SequencingConsensusApi, + election::ConsensusExchange, + node_implementation::{CommitteeEx, NodeImplementation, NodeType}, + BlockPayload, State, + }, +}; +use hotshot_utils::bincode::bincode_opts; +use snafu::Snafu; +use std::{collections::HashSet, sync::Arc, time::Instant}; +use tracing::{debug, error, instrument, warn}; + +#[derive(Snafu, Debug)] +/// Error type for consensus tasks +pub struct ConsensusTaskError {} + +/// Tracks state of a Transaction task +pub struct TransactionTaskState< + TYPES: NodeType, + I: NodeImplementation< + TYPES, + Leaf = SequencingLeaf, + ConsensusMessage = SequencingMessage, + >, + A: SequencingConsensusApi, I> + 'static, +> where + CommitteeEx: ConsensusExchange< + TYPES, + Message, + Certificate = DACertificate, + Commitment = TYPES::BlockType, + >, +{ + /// The state's api + pub api: A, + /// Global registry task for the state + pub registry: GlobalRegistry, + + /// View number this view is executing in. + pub cur_view: TYPES::Time, + + /// Reference to consensus. Leader will require a read lock on this. + pub consensus: Arc>>>, + + /// the committee exchange + pub committee_exchange: Arc>, + + /// Global events stream to publish events + pub event_stream: ChannelStream>, + + /// This state's ID + pub id: u64, +} + +impl< + TYPES: NodeType, + I: NodeImplementation< + TYPES, + Leaf = SequencingLeaf, + ConsensusMessage = SequencingMessage, + >, + A: SequencingConsensusApi, I> + 'static, + > TransactionTaskState +where + CommitteeEx: ConsensusExchange< + TYPES, + Message, + Certificate = DACertificate, + Commitment = TYPES::BlockType, + >, +{ + /// main task event handler + #[instrument(skip_all, fields(id = self.id, view = *self.cur_view), name = "Transaction Handling Task", level = "error")] + + pub async fn handle_event( + &mut self, + event: SequencingHotShotEvent, + ) -> Option { + match event { + SequencingHotShotEvent::TransactionsRecv(transactions) => { + let mut consensus = self.consensus.write().await; + consensus + .get_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()) + && txns.insert(transaction.commit(), transaction).is_none() + { + consensus.metrics.outstanding_transactions.update(1); + consensus + .metrics + .outstanding_transactions_memory_size + .update(i64::try_from(size).unwrap_or_else(|e| { + warn!("Conversion failed: {e}. Using the max value."); + i64::MAX + })); + } + } + }) + .await; + + return None; + } + SequencingHotShotEvent::LeafDecided(leaf_chain) => { + let mut included_txns = HashSet::new(); + let mut included_txn_size = 0; + let mut included_txn_count = 0; + for leaf in leaf_chain { + match &leaf.deltas { + Left(block) => { + let txns = block.contained_transactions(); + for txn in txns { + included_txns.insert(txn); + } + } + Right(_) => {} + } + } + let mut consensus = self.consensus.write().await; + let txns = consensus.transactions.cloned().await; + + let _ = included_txns.iter().map(|hash| { + if !txns.contains_key(hash) { + consensus.seen_transactions.insert(*hash); + } + }); + drop(txns); + consensus + .transactions + .modify(|txns| { + *txns = txns + .drain() + .filter(|(txn_hash, txn)| { + if included_txns.contains(txn_hash) { + included_txn_count += 1; + included_txn_size += + bincode_opts().serialized_size(txn).unwrap_or_default(); + false + } else { + true + } + }) + .collect(); + }) + .await; + + consensus + .metrics + .outstanding_transactions + .update(-included_txn_count); + consensus + .metrics + .outstanding_transactions_memory_size + .update(-(i64::try_from(included_txn_size).unwrap_or(i64::MAX))); + return None; + } + SequencingHotShotEvent::ViewChange(view) => { + if *self.cur_view >= *view { + return None; + } + + if *view - *self.cur_view > 1 { + error!("View changed by more than 1 going to view {:?}", view); + } + self.cur_view = view; + + // If we are not the next leader (DA leader for this view) immediately exit + if !self.committee_exchange.is_leader(self.cur_view + 1) { + // panic!("We are not the DA leader for view {}", *self.cur_view + 1); + return None; + } + + // ED Copy of parent_leaf() function from sequencing leader + + let consensus = self.consensus.read().await; + let parent_view_number = &consensus.high_qc.view_number; + + let Some(parent_view) = consensus.state_map.get(parent_view_number) else { + error!( + "Couldn't find high QC parent in state map. Parent view {:?}", + parent_view_number + ); + return None; + }; + let Some(leaf) = parent_view.get_leaf_commitment() else { + error!( + ?parent_view_number, + ?parent_view, + "Parent of high QC points to a view without a proposal" + ); + return None; + }; + let Some(leaf) = consensus.saved_leaves.get(&leaf) else { + error!("Failed to find high QC parent."); + return None; + }; + let parent_leaf = leaf.clone(); + + drop(consensus); + + let mut block = ::StateType::next_block(None); + let txns = self.wait_for_transactions(parent_leaf).await?; + + for txn in txns { + if let Ok(new_block) = block.add_transaction_raw(&txn) { + block = new_block; + continue; + } + } + self.event_stream + .publish(SequencingHotShotEvent::BlockReady(block, view + 1)) + .await; + return None; + } + SequencingHotShotEvent::Shutdown => { + return Some(HotShotTaskCompleted::ShutDown); + } + _ => {} + } + None + } + + #[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, + ) -> Option> { + let task_start_time = Instant::now(); + + // 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 consensus = self.consensus.read().await; + + let receiver = consensus.transactions.subscribe().await; + + loop { + let all_txns = consensus.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(); + + let time_past = task_start_time.elapsed(); + if unclaimed_txns.len() < self.api.min_transactions() + && (time_past < self.api.propose_max_round_time()) + { + let duration = self.api.propose_max_round_time() - time_past; + let result = async_timeout(duration, receiver.recv()).await; + match result { + Err(_) => { + // Fall through below to updating new block + error!( + "propose_max_round_time passed, sending transactions we have so far" + ); + } + Ok(Err(e)) => { + // Something unprecedented is wrong, and `transactions` has been dropped + error!("Channel receiver error for SubscribableRwLock {:?}", e); + return None; + } + Ok(Ok(_)) => continue, + } + } + 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(); + Some(txns) + } + + /// Event filter for the transaction task + pub fn filter(event: &SequencingHotShotEvent) -> bool { + matches!( + event, + SequencingHotShotEvent::TransactionsRecv(_) + | SequencingHotShotEvent::LeafDecided(_) + | SequencingHotShotEvent::Shutdown + | SequencingHotShotEvent::ViewChange(_) + ) + } +} + +/// task state implementation for Transactions Task +impl< + TYPES: NodeType, + I: NodeImplementation< + TYPES, + Leaf = SequencingLeaf, + ConsensusMessage = SequencingMessage, + >, + A: SequencingConsensusApi, I> + 'static, + > TS for TransactionTaskState +where + CommitteeEx: ConsensusExchange< + TYPES, + Message, + Certificate = DACertificate, + Commitment = TYPES::BlockType, + >, +{ +} + +/// Type alias for DA Task Types +pub type TransactionsTaskTypes = HSTWithEvent< + ConsensusTaskError, + SequencingHotShotEvent, + ChannelStream>, + TransactionTaskState, +>; diff --git a/crates/task-impls/src/view_sync.rs b/crates/task-impls/src/view_sync.rs index 18c8ae581f..f92752f487 100644 --- a/crates/task-impls/src/view_sync.rs +++ b/crates/task-impls/src/view_sync.rs @@ -9,7 +9,13 @@ use hotshot_task::{ task::{FilterEvent, HandleEvent, HotShotTaskCompleted, HotShotTaskTypes, TS}, task_impls::{HSTWithEvent, TaskBuilder}, }; -use hotshot_types::traits::{election::Membership, network::ConsensusIntentEvent}; +use hotshot_types::{ + traits::{ + election::{Membership, SignedCertificate}, + network::ConsensusIntentEvent, + }, + vote::ViewSyncVoteAccumulator, +}; use bitvec::prelude::*; use hotshot_task::global_registry::GlobalRegistry; @@ -25,10 +31,10 @@ use hotshot_types::{ signature_key::SignatureKey, state::ConsensusTime, }, - vote::{ViewSyncData, ViewSyncVote, VoteAccumulator}, + vote::{ViewSyncData, ViewSyncVote}, }; use snafu::Snafu; -use std::{collections::HashMap, sync::Arc, time::Duration}; +use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; use tracing::{debug, error, instrument}; #[derive(PartialEq, PartialOrd, Clone, Debug, Eq, Hash)] /// Phases of view sync @@ -216,8 +222,14 @@ pub struct ViewSyncRelayTaskState< /// View sync exchange pub exchange: Arc>, /// Vote accumulator + #[allow(clippy::type_complexity)] pub accumulator: Either< - VoteAccumulator>, + as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + ViewSyncData, + >>::VoteAccumulator, ViewSyncCertificate, >, /// Our node id; for logging @@ -380,24 +392,23 @@ where return; } - let accumulator = VoteAccumulator { - total_vote_outcomes: HashMap::new(), - da_vote_outcomes: HashMap::new(), - yes_vote_outcomes: HashMap::new(), - no_vote_outcomes: HashMap::new(), - viewsync_precommit_vote_outcomes: HashMap::new(), - viewsync_commit_vote_outcomes: HashMap::new(), - viewsync_finalize_vote_outcomes: HashMap::new(), + let new_accumulator = ViewSyncVoteAccumulator { + pre_commit_vote_outcomes: HashMap::new(), + commit_vote_outcomes: HashMap::new(), + finalize_vote_outcomes: HashMap::new(), + success_threshold: self.exchange.success_threshold(), failure_threshold: self.exchange.failure_threshold(), + sig_lists: Vec::new(), signers: bitvec![0; self.exchange.total_nodes()], + phantom: PhantomData, }; let mut relay_state = ViewSyncRelayTaskState { event_stream: self.event_stream.clone(), exchange: self.exchange.clone(), - accumulator: either::Left(accumulator), + accumulator: either::Left(new_accumulator), id: self.id, }; @@ -958,7 +969,7 @@ where return (Some(HotShotTaskCompleted::ShutDown), self); } - let (vote_internal, phase) = match vote { + let (vote_internal, phase) = match vote.clone() { ViewSyncVote::PreCommit(vote_internal) => { (vote_internal, ViewSyncPhase::PreCommit) } @@ -993,15 +1004,10 @@ where *vote_internal.round, vote_internal.relay ); - let accumulator = self.exchange.accumulate_vote( - &vote_internal.signature.0, - &vote_internal.signature.1, - view_sync_data, - vote_internal.vote_data, - vote_internal.vote_token.clone(), - vote_internal.round, + let accumulator = self.exchange.accumulate_vote_2( self.accumulator.left().unwrap(), - Some(vote_internal.relay), + &vote, + &view_sync_data, ); self.accumulator = match accumulator { @@ -1013,7 +1019,6 @@ where data: certificate.clone(), signature, }; - // error!("Sending view sync cert {:?}", message.clone()); self.event_stream .publish(SequencingHotShotEvent::ViewSyncCertificateSend( message, @@ -1022,19 +1027,19 @@ where .await; // Reset accumulator for new certificate - either::Left(VoteAccumulator { - total_vote_outcomes: HashMap::new(), - da_vote_outcomes: HashMap::new(), - yes_vote_outcomes: HashMap::new(), - no_vote_outcomes: HashMap::new(), - viewsync_precommit_vote_outcomes: HashMap::new(), - viewsync_commit_vote_outcomes: HashMap::new(), - viewsync_finalize_vote_outcomes: HashMap::new(), + let new_accumulator = ViewSyncVoteAccumulator { + pre_commit_vote_outcomes: HashMap::new(), + commit_vote_outcomes: HashMap::new(), + finalize_vote_outcomes: HashMap::new(), + success_threshold: self.exchange.success_threshold(), failure_threshold: self.exchange.failure_threshold(), + sig_lists: Vec::new(), signers: bitvec![0; self.exchange.total_nodes()], - }) + phantom: PhantomData, + }; + either::Left(new_accumulator) } }; diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index e75c328a5d..b361b241d7 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -41,6 +41,7 @@ bitvec = { workspace = true } [dev-dependencies] async-lock = { workspace = true } +bincode = { workspace = true } # GG any better options for serialization? [target.'cfg(all(async_executor_impl = "tokio"))'.dependencies] tokio = { workspace = true } diff --git a/crates/testing/src/node_types.rs b/crates/testing/src/node_types.rs index 1d2985c56d..c788006984 100644 --- a/crates/testing/src/node_types.rs +++ b/crates/testing/src/node_types.rs @@ -15,14 +15,13 @@ use hotshot::{ }; use hotshot_types::{ certificate::ViewSyncCertificate, - data::{DAProposal, QuorumProposal, SequencingLeaf, ViewNumber}, + data::{QuorumProposal, SequencingLeaf, ViewNumber}, message::{Message, SequencingMessage}, traits::{ election::{CommitteeExchange, QuorumExchange, ViewSyncExchange}, network::{TestableChannelImplementation, TestableNetworkingImplementation}, node_implementation::{ChannelMaps, NodeType, SequencingExchanges, TestableExchange}, }, - vote::{DAVote, QuorumVote, ViewSyncVote}, }; use serde::{Deserialize, Serialize}; @@ -65,80 +64,33 @@ pub struct StaticFallbackImpl; pub type StaticMembership = StaticCommittee>; -pub type StaticMemoryDAComm = MemoryCommChannel< - SequencingTestTypes, - SequencingMemoryImpl, - DAProposal, - DAVote, - StaticMembership, ->; +pub type StaticMemoryDAComm = + MemoryCommChannel; -type StaticLibp2pDAComm = Libp2pCommChannel< - SequencingTestTypes, - SequencingLibp2pImpl, - DAProposal, - DAVote, - StaticMembership, ->; +type StaticLibp2pDAComm = + Libp2pCommChannel; -type StaticWebDAComm = WebCommChannel< - SequencingTestTypes, - SequencingWebImpl, - DAProposal, - DAVote, - StaticMembership, ->; +type StaticWebDAComm = WebCommChannel; type StaticFallbackComm = WebServerWithFallbackCommChannel; -pub type StaticMemoryQuorumComm = MemoryCommChannel< - SequencingTestTypes, - SequencingMemoryImpl, - QuorumProposal>, - QuorumVote>, - StaticMembership, ->; +pub type StaticMemoryQuorumComm = + MemoryCommChannel; -type StaticLibp2pQuorumComm = Libp2pCommChannel< - SequencingTestTypes, - SequencingLibp2pImpl, - QuorumProposal>, - QuorumVote>, - StaticMembership, ->; +type StaticLibp2pQuorumComm = + Libp2pCommChannel; -type StaticWebQuorumComm = WebCommChannel< - SequencingTestTypes, - SequencingWebImpl, - QuorumProposal>, - QuorumVote>, - StaticMembership, ->; +type StaticWebQuorumComm = WebCommChannel; -pub type StaticMemoryViewSyncComm = MemoryCommChannel< - SequencingTestTypes, - SequencingMemoryImpl, - ViewSyncCertificate, - ViewSyncVote, - StaticMembership, ->; +pub type StaticMemoryViewSyncComm = + MemoryCommChannel; -type StaticLibp2pViewSyncComm = Libp2pCommChannel< - SequencingTestTypes, - SequencingLibp2pImpl, - ViewSyncCertificate, - ViewSyncVote, - StaticMembership, ->; +type StaticLibp2pViewSyncComm = + Libp2pCommChannel; -type StaticWebViewSyncComm = WebCommChannel< - SequencingTestTypes, - SequencingWebImpl, - ViewSyncCertificate, - ViewSyncVote, - StaticMembership, ->; +type StaticWebViewSyncComm = + WebCommChannel; pub type SequencingLibp2pExchange = SequencingExchanges< SequencingTestTypes, @@ -231,9 +183,24 @@ impl Box::new(move |id| { let network = Arc::new(network_generator(id)); - let quorum_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network.clone()); - let committee_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network.clone()); - let view_sync_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network); + let quorum_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network.clone()); + let committee_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network.clone()); + let view_sync_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network); (quorum_chan, committee_chan, view_sync_chan) }) @@ -325,9 +292,24 @@ impl Box::new(move |id| { let network = Arc::new(network_generator(id)); let network_da = Arc::new(network_da_generator(id)); - let quorum_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network.clone()); - let committee_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network_da); - let view_sync_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network); + let quorum_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network.clone()); + let committee_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network_da); + let view_sync_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network); (quorum_chan, committee_chan, view_sync_chan) }) @@ -445,9 +427,24 @@ impl Box::new(move |id| { let network = Arc::new(network_generator(id)); let network_da = Arc::new(network_da_generator(id)); - let quorum_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network.clone()); - let committee_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network_da); - let view_sync_chan = <>>::Networking as TestableChannelImplementation<_, _, _, _, _, _>>::generate_network()(network); + let quorum_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network.clone()); + let committee_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network_da); + let view_sync_chan = + <, + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network); (quorum_chan, committee_chan, view_sync_chan) }) @@ -584,44 +581,20 @@ impl <, - >>::Networking as TestableChannelImplementation< - _, - _, - QuorumProposal< - SequencingTestTypes, - >::Leaf, - >, - QuorumVote< - SequencingTestTypes, - >::Leaf, - >, - _, - _, - >>::generate_network()(network.clone()); + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network.clone()); let committee_chan = <, - >>::Networking as TestableChannelImplementation< - _, - _, - DAProposal, - DAVote, - _, - _, - >>::generate_network()(network_da); + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network_da); let view_sync_chan = <, - >>::Networking as TestableChannelImplementation< - _, - _, - ViewSyncCertificate, - ViewSyncVote, - _, - _, - >>::generate_network()(network); + >>::Networking as TestableChannelImplementation<_, _, _, _>>::generate_network( + )(network); (quorum_chan, committee_chan, view_sync_chan) }) } diff --git a/crates/testing/src/spinning_task.rs b/crates/testing/src/spinning_task.rs index c8ef07d51c..a9d9e5d586 100644 --- a/crates/testing/src/spinning_task.rs +++ b/crates/testing/src/spinning_task.rs @@ -113,7 +113,8 @@ impl SpinningTaskDescription { state.late_start.remove(&idx.try_into().unwrap()) { tracing::error!("Spinning up node late"); - node.run_tasks().await; + let handle = node.run_tasks().await; + handle.hotshot.start_consensus().await; } } UpDown::Down => { diff --git a/crates/testing/src/task_helpers.rs b/crates/testing/src/task_helpers.rs index 13868721d4..652ee6b8d9 100644 --- a/crates/testing/src/task_helpers.rs +++ b/crates/testing/src/task_helpers.rs @@ -6,14 +6,14 @@ use commit::Committable; use either::Right; use hotshot::{ certificate::QuorumCertificate, - traits::{Block, NodeImplementation, TestableNodeImplementation}, + traits::{BlockPayload, NodeImplementation, TestableNodeImplementation}, types::{bn254::BN254Pub, SignatureKey, SystemContextHandle}, HotShotInitializer, HotShotSequencingConsensusApi, SystemContext, }; use hotshot_task::event_stream::ChannelStream; use hotshot_task_impls::events::SequencingHotShotEvent; use hotshot_types::{ - data::{QuorumProposal, SequencingLeaf, ViewNumber}, + data::{QuorumProposal, SequencingLeaf, VidScheme, ViewNumber}, message::{Message, Proposal}, traits::{ consensus_api::ConsensusSharedApi, @@ -47,7 +47,6 @@ pub async fn build_system_handle( >>::block_genesis()) .unwrap(); - let known_nodes = config.known_nodes.clone(); let known_nodes_with_stake = config.known_nodes_with_stake.clone(); let private_key = ::generated_from_seed_indexed([0u8; 32], node_id).1; let public_key = ::SignatureKey::from_private(&private_key); @@ -67,7 +66,6 @@ pub async fn build_system_handle( let exchanges = >::Exchanges::create( known_nodes_with_stake.clone(), - known_nodes.clone(), (quorum_election_config, committee_election_config), networks, public_key, @@ -163,3 +161,10 @@ pub fn key_pair_for_id(node_id: u64) -> (::PrivateKey, let public_key = ::SignatureKey::from_private(&private_key); (private_key, public_key) } + +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 f1db2a9320..326debb69b 100644 --- a/crates/testing/src/test_builder.rs +++ b/crates/testing/src/test_builder.rs @@ -219,7 +219,6 @@ impl TestMetadata { num_bootstrap: num_bootstrap_nodes, min_transactions, max_transactions: NonZeroUsize::new(99999).unwrap(), - known_nodes, known_nodes_with_stake, da_committee_size, next_view_timeout: 500, @@ -247,7 +246,7 @@ impl TestMetadata { } = timing_data; let mod_config = // TODO this should really be using the timing config struct - |a: &mut HotShotConfig::StakeTableEntry, TYPES::ElectionConfigType>| { + |a: &mut HotShotConfig<::StakeTableEntry, TYPES::ElectionConfigType>| { a.next_view_timeout = next_view_timeout; a.timeout_ratio = timeout_ratio; a.round_start_delay = round_start_delay; diff --git a/crates/testing/src/test_launcher.rs b/crates/testing/src/test_launcher.rs index 4a7e6bd9b1..4fb230b315 100644 --- a/crates/testing/src/test_launcher.rs +++ b/crates/testing/src/test_launcher.rs @@ -83,8 +83,6 @@ where QuorumCommChannel: CommunicationChannel< TYPES, Message, - as ConsensusExchange>>::Proposal, - as ConsensusExchange>>::Vote, as ConsensusExchange>>::Membership, >, { @@ -94,7 +92,6 @@ where pub storage: Generator<>::Storage>, /// configuration used to generate each hotshot node pub config: HotShotConfig< - TYPES::SignatureKey, ::StakeTableEntry, TYPES::ElectionConfigType, >, @@ -198,7 +195,6 @@ impl> TestLauncher::StakeTableEntry, TYPES::ElectionConfigType, >, diff --git a/crates/testing/src/test_runner.rs b/crates/testing/src/test_runner.rs index 57c82d24e9..056f0f8a26 100644 --- a/crates/testing/src/test_runner.rs +++ b/crates/testing/src/test_runner.rs @@ -41,8 +41,6 @@ where QuorumCommChannel: CommunicationChannel< TYPES, Message, - as ConsensusExchange>>::Proposal, - as ConsensusExchange>>::Vote, as ConsensusExchange>>::Membership, >, { @@ -59,8 +57,6 @@ where QuorumCommChannel: CommunicationChannel< TYPES, Message, - as ConsensusExchange>>::Proposal, - as ConsensusExchange>>::Vote, as ConsensusExchange>>::Membership, >, { @@ -202,8 +198,8 @@ where >, { let mut results = vec![]; - for _i in 0..total { - tracing::error!("running node{}", _i); + for i in 0..total { + tracing::debug!("launch node {}", i); let node_id = self.next_node_id; let storage = (self.launcher.resource_generator.storage)(node_id); let config = self.launcher.resource_generator.config.clone(); @@ -234,7 +230,6 @@ where storage: I::Storage, initializer: HotShotInitializer, config: HotShotConfig< - TYPES::SignatureKey, ::StakeTableEntry, TYPES::ElectionConfigType, >, @@ -250,7 +245,6 @@ where let node_id = self.next_node_id; self.next_node_id += 1; - let known_nodes = config.known_nodes.clone(); let known_nodes_with_stake = config.known_nodes_with_stake.clone(); // Generate key pair for certificate aggregation let private_key = TYPES::SignatureKey::generated_from_seed_indexed([0u8; 32], node_id).1; @@ -265,7 +259,6 @@ where let committee_election_config = I::committee_election_config_generator(); let exchanges = I::Exchanges::create( known_nodes_with_stake.clone(), - known_nodes.clone(), ( quorum_election_config, committee_election_config(config.da_committee_size as u64), diff --git a/crates/testing/tests/atomic_storage.rs b/crates/testing/tests/atomic_storage.rs index 3b97a60c9c..c8183af164 100644 --- a/crates/testing/tests/atomic_storage.rs +++ b/crates/testing/tests/atomic_storage.rs @@ -6,7 +6,7 @@ use hotshot::{ random_quorom_certificate, random_transaction, random_validating_leaf, VDemoBlock, VDemoState, }, - traits::{Block, State, Storage}, + traits::{BlockPayload, State, Storage}, }; use hotshot_types::{data::ViewNumber, traits::state::TestableState}; use rand::thread_rng; diff --git a/crates/testing/tests/basic.rs b/crates/testing/tests/basic.rs index fc4b6017f7..35e75fffa7 100644 --- a/crates/testing/tests/basic.rs +++ b/crates/testing/tests/basic.rs @@ -6,13 +6,23 @@ #[cfg_attr(async_executor_impl = "async-std", async_std::test)] async fn test_success() { use hotshot_testing::{ + completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, node_types::{SequencingMemoryImpl, SequencingTestTypes}, test_builder::TestMetadata, }; + use std::time::Duration; async_compatibility_layer::logging::setup_logging(); async_compatibility_layer::logging::setup_backtrace(); - let metadata = TestMetadata::default(); + let metadata = TestMetadata { + // allow more time to pass in CI + completion_task_description: CompletionTaskDescription::TimeBasedCompletionTaskBuilder( + TimeBasedCompletionTaskDescription { + duration: Duration::from_millis(1_200_000), + }, + ), + ..TestMetadata::default() + }; metadata .gen_launcher::() .launch() diff --git a/crates/testing/tests/catchup.rs b/crates/testing/tests/catchup.rs index 2cbc6c8a16..9c6bd0bb38 100644 --- a/crates/testing/tests/catchup.rs +++ b/crates/testing/tests/catchup.rs @@ -59,6 +59,61 @@ async fn test_catchup() { .await; } +#[cfg(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)] +async fn test_catchup_web() { + use std::time::Duration; + + use hotshot_testing::{ + completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, + node_types::{SequencingTestTypes, SequencingWebImpl}, + overall_safety_task::OverallSafetyPropertiesDescription, + spinning_task::{ChangeNode, SpinningTaskDescription, UpDown}, + test_builder::{TestMetadata, TimingData}, + }; + + async_compatibility_layer::logging::setup_logging(); + async_compatibility_layer::logging::setup_backtrace(); + let timing_data = TimingData { + next_view_timeout: 1000, + ..Default::default() + }; + let mut metadata = TestMetadata::default(); + let catchup_nodes = vec![ChangeNode { + idx: 18, + updown: UpDown::Up, + }]; + + metadata.timing_data = timing_data; + metadata.start_nodes = 19; + metadata.total_nodes = 20; + + metadata.spinning_properties = SpinningTaskDescription { + node_changes: vec![(Duration::from_millis(400), catchup_nodes)], + }; + + metadata.completion_task_description = + CompletionTaskDescription::TimeBasedCompletionTaskBuilder( + TimeBasedCompletionTaskDescription { + duration: Duration::from_millis(100000), + }, + ); + metadata.overall_safety_properties = OverallSafetyPropertiesDescription { + check_leaf: true, + ..Default::default() + }; + + metadata + .gen_launcher::() + .launch() + .run_test() + .await; +} + /// Test that one node catches up and has sucessful views after coming back #[cfg(test)] #[cfg_attr( @@ -66,6 +121,7 @@ async fn test_catchup() { tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[ignore] async fn test_catchup_one_node() { use std::time::Duration; @@ -94,13 +150,13 @@ async fn test_catchup_one_node() { metadata.total_nodes = 20; metadata.spinning_properties = SpinningTaskDescription { - node_changes: vec![(Duration::new(1, 0), catchup_nodes)], + node_changes: vec![(Duration::from_millis(400), catchup_nodes)], }; metadata.completion_task_description = CompletionTaskDescription::TimeBasedCompletionTaskBuilder( TimeBasedCompletionTaskDescription { - duration: Duration::from_millis(100000), + duration: Duration::from_millis(20000), }, ); metadata.overall_safety_properties = OverallSafetyPropertiesDescription { diff --git a/crates/testing/tests/da_task.rs b/crates/testing/tests/da_task.rs index 0322909203..e80cbff46c 100644 --- a/crates/testing/tests/da_task.rs +++ b/crates/testing/tests/da_task.rs @@ -1,9 +1,12 @@ use commit::Committable; use hotshot::HotShotSequencingConsensusApi; use hotshot_task_impls::events::SequencingHotShotEvent; -use hotshot_testing::node_types::{SequencingMemoryImpl, SequencingTestTypes}; +use hotshot_testing::{ + node_types::{SequencingMemoryImpl, SequencingTestTypes}, + task_helpers::vid_init, +}; use hotshot_types::{ - data::{DAProposal, ViewNumber}, + data::{DAProposal, VidDisperse, VidSchemeTrait, ViewNumber}, traits::{ consensus_api::ConsensusSharedApi, election::ConsensusExchange, node_implementation::ExchangesType, state::ConsensusTime, @@ -23,10 +26,7 @@ async fn test_da_task() { }; use hotshot_task_impls::harness::run_harness; use hotshot_testing::task_helpers::build_system_handle; - use hotshot_types::{ - message::{CommitteeConsensusMessage, Proposal}, - traits::election::CommitteeExchangeType, - }; + use hotshot_types::{message::Proposal, traits::election::CommitteeExchangeType}; async_compatibility_layer::logging::setup_logging(); async_compatibility_layer::logging::setup_backtrace(); @@ -53,6 +53,20 @@ async fn test_da_task() { data: proposal, signature, }; + let vid = vid_init(); + let message_bytes = bincode::serialize(&message).unwrap(); + let (shares, common) = vid.dispersal_data(&message_bytes).unwrap(); + let vid_proposal = Proposal { + data: VidDisperse { + view_number: message.data.view_number, + commitment: block_commitment, + shares, + common, + }, + signature: message.signature.clone(), + }; + // TODO for now reuse the same block commitment and signature as DA committee + // https://github.com/EspressoSystems/jellyfish/issues/369 // Every event input is seen on the event stream in the output. let mut input = Vec::new(); @@ -61,26 +75,55 @@ async fn test_da_task() { // In view 1, node 2 is the next leader. input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(1))); input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(2))); + input.push(SequencingHotShotEvent::BlockReady( + block.clone(), + ViewNumber::new(2), + )); input.push(SequencingHotShotEvent::DAProposalRecv( message.clone(), pub_key, )); + input.push(SequencingHotShotEvent::VidDisperseRecv( + vid_proposal.clone(), + pub_key, + )); input.push(SequencingHotShotEvent::Shutdown); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 1); + output.insert( + SequencingHotShotEvent::BlockReady(block.clone(), ViewNumber::new(2)), + 1, + ); output.insert(SequencingHotShotEvent::SendDABlockData(block), 1); output.insert( SequencingHotShotEvent::DAProposalSend(message.clone(), pub_key), 1, ); - if let Ok(Some(vote_token)) = committee_exchange.make_vote_token(ViewNumber::new(2)) { - let da_message = - committee_exchange.create_da_message(block_commitment, ViewNumber::new(2), vote_token); - if let CommitteeConsensusMessage::DAVote(vote) = da_message { - output.insert(SequencingHotShotEvent::DAVoteSend(vote), 1); - } - } + let vote_token = committee_exchange + .make_vote_token(ViewNumber::new(2)) + .unwrap() + .unwrap(); + let da_vote = + committee_exchange.create_da_message(block_commitment, 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); + output.insert(SequencingHotShotEvent::VidVoteSend(vid_vote), 1); + output.insert(SequencingHotShotEvent::DAProposalRecv(message, pub_key), 1); + output.insert( + SequencingHotShotEvent::VidDisperseRecv(vid_proposal, pub_key), + 1, + ); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(2)), 1); output.insert(SequencingHotShotEvent::Shutdown, 1); diff --git a/crates/testing/tests/network_task.rs b/crates/testing/tests/network_task.rs index f3d8d9f8bb..b28404f835 100644 --- a/crates/testing/tests/network_task.rs +++ b/crates/testing/tests/network_task.rs @@ -3,10 +3,10 @@ use hotshot::HotShotSequencingConsensusApi; use hotshot_task_impls::events::SequencingHotShotEvent; use hotshot_testing::{ node_types::{SequencingMemoryImpl, SequencingTestTypes}, - task_helpers::build_quorum_proposal, + task_helpers::{build_quorum_proposal, vid_init}, }; use hotshot_types::{ - data::{DAProposal, ViewNumber}, + data::{DAProposal, VidSchemeTrait, ViewNumber}, traits::{ consensus_api::ConsensusSharedApi, node_implementation::ExchangesType, state::ConsensusTime, }, @@ -19,11 +19,14 @@ use std::collections::HashMap; tokio::test(flavor = "multi_thread", worker_threads = 2) )] #[cfg_attr(async_executor_impl = "async-std", async_std::test)] +#[ignore] async fn test_network_task() { use hotshot::demos::sdemo::{SDemoBlock, SDemoNormalBlock}; 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::{ + data::VidDisperse, message::Proposal, traits::election::CommitteeExchangeType, + }; async_compatibility_layer::logging::setup_logging(); async_compatibility_layer::logging::setup_backtrace(); @@ -51,16 +54,38 @@ 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 (shares, common) = vid.dispersal_data(&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, + shares, + common, + }, + signature: da_proposal.signature.clone(), + }; // Every event input is seen on the event stream in the output. let mut input = Vec::new(); let mut output = HashMap::new(); input.push(SequencingHotShotEvent::ViewChange(ViewNumber::new(1))); + input.push(SequencingHotShotEvent::BlockReady( + block.clone(), + ViewNumber::new(2), + )); input.push(SequencingHotShotEvent::DAProposalSend( da_proposal.clone(), pub_key, )); + input.push(SequencingHotShotEvent::VidDisperseSend( + da_vid_disperse.clone(), + pub_key, + )); input.push(SequencingHotShotEvent::QuorumProposalSend( quorum_proposal.clone(), pub_key, @@ -69,11 +94,25 @@ async fn test_network_task() { input.push(SequencingHotShotEvent::Shutdown); output.insert(SequencingHotShotEvent::ViewChange(ViewNumber::new(1)), 2); - // One output from the input, the other from the DA task. output.insert( SequencingHotShotEvent::DAProposalSend(da_proposal.clone(), pub_key), + 2, // 2 occurrences: 1 from `input`, 1 from the DA task + ); + output.insert( + SequencingHotShotEvent::BlockReady(block.clone(), ViewNumber::new(2)), 2, ); + output.insert( + SequencingHotShotEvent::VidDisperseRecv(da_vid_disperse.clone(), pub_key), + 1, + ); + output.insert( + SequencingHotShotEvent::VidDisperseSend(da_vid_disperse, pub_key), + 2, // 2 occurrences: 1 from `input`, 1 from the DA task + ); + output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(1)), 1); + output.insert(SequencingHotShotEvent::Timeout(ViewNumber::new(2)), 1); + // Only one output from the input. // The consensus task will fail to send a second proposal, like the DA task does, due to the // view number check in `publish_proposal_if_able` in consensus.rs, and we will see an error in diff --git a/crates/testing/tests/timeout.rs b/crates/testing/tests/timeout.rs index 7ebeddd577..f8963c9d52 100644 --- a/crates/testing/tests/timeout.rs +++ b/crates/testing/tests/timeout.rs @@ -30,7 +30,7 @@ async fn test_timeout() { metadata.timing_data = timing_data; metadata.spinning_properties = SpinningTaskDescription { - node_changes: vec![(Duration::new(1, 0), dead_nodes)], + node_changes: vec![(Duration::new(0, 5000), dead_nodes)], }; // TODO ED Add safety task, etc to confirm TCs are being formed diff --git a/crates/testing/tests/web_server.rs b/crates/testing/tests/web_server.rs index 75d0e4f89c..6c95318c7b 100644 --- a/crates/testing/tests/web_server.rs +++ b/crates/testing/tests/web_server.rs @@ -1,5 +1,8 @@ +use std::time::Duration; + use async_compatibility_layer::logging::shutdown_logging; use hotshot_testing::{ + completion_task::{CompletionTaskDescription, TimeBasedCompletionTaskDescription}, node_types::{SequencingTestTypes, SequencingWebImpl}, overall_safety_task::OverallSafetyPropertiesDescription, test_builder::{TestMetadata, TimingData}, @@ -28,6 +31,12 @@ async fn web_server_network() { num_successful_views: 35, ..Default::default() }, + // allow more time to pass in CI + completion_task_description: CompletionTaskDescription::TimeBasedCompletionTaskBuilder( + TimeBasedCompletionTaskDescription { + duration: Duration::from_millis(1_200_000), + }, + ), ..TestMetadata::default() }; metadata diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 5ea10f246c..74ace15850 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -12,12 +12,13 @@ demo = ["ed25519-compact"] [dependencies] arbitrary = { version = "1.3", features = ["derive"] } +ark-bls12-381 = { workspace = true } async-compatibility-layer = { workspace = true } async-lock = { workspace = true } async-trait = { workspace = true } ark-serialize = { version = "0.3", features = [ "derive", -] } # TODO GG upgrade to 0.4 and inherit this dep from workspace +] } # TODO upgrade to 0.4 and inherit this dep from workspace https://github.com/EspressoSystems/HotShot/issues/1700 ark-std = { workspace = true } bincode = { workspace = true } bitvec = { workspace = true } @@ -36,12 +37,14 @@ hex_fmt = "0.3.0" hotshot-constants = { path = "../constants" } hotshot-utils = { path = "../utils" } hotshot-task = { path = "../task", default-features = false } -jf-primitives = { workspace = true } +jf-primitives = { workspace = true, features = ["test-srs"] } +jf-utils = { workspace = true } nll = { workspace = true } libp2p-networking = { workspace = true } rand = { workspace = true } rand_chacha = { workspace = true } serde = { workspace = true } +sha2 = { workspace = true } snafu = { workspace = true } tagged-base64 = { git = "https://github.com/EspressoSystems/tagged-base64", tag = "0.2.4" } time = { workspace = true } @@ -51,7 +54,7 @@ bit-vec = "0.6.3" typenum = { workspace = true } [dev-dependencies] -serde_json = "1.0.106" +serde_json = "1.0.107" [target.'cfg(all(async_executor_impl = "async-std"))'.dependencies] async-std = { workspace = true } diff --git a/crates/types/src/certificate.rs b/crates/types/src/certificate.rs index 47720eba75..9792026dcf 100644 --- a/crates/types/src/certificate.rs +++ b/crates/types/src/certificate.rs @@ -1,5 +1,10 @@ //! Provides two types of cerrtificates and their accumulators. +use crate::vote::DAVoteAccumulator; +use crate::vote::QuorumVote; +use crate::vote::QuorumVoteAccumulator; +use crate::vote::ViewSyncVoteAccumulator; +use crate::vote::VoteType; use crate::{ data::{fake_commitment, serialize_signature, LeafType}, traits::{ @@ -8,10 +13,11 @@ use crate::{ signature_key::{EncodedPublicKey, EncodedSignature, SignatureKey}, state::ConsensusTime, }, - vote::ViewSyncData, + vote::{DAVote, ViewSyncData, ViewSyncVote}, }; use bincode::Options; use commit::{Commitment, Committable}; + use espresso_systems_common::hotshot::tag; use hotshot_utils::bincode::bincode_opts; use serde::{Deserialize, Serialize}; @@ -154,15 +160,22 @@ impl> SignedCertificate for QuorumCertificate { + type Vote = QuorumVote; + type VoteAccumulator = QuorumVoteAccumulator; + fn from_signatures_and_commitment( - view_number: TYPES::Time, signatures: AssembledSignature, - commit: Commitment, - _relay: Option, + vote: Self::Vote, ) -> Self { + let leaf_commitment = match vote.clone() { + QuorumVote::Yes(vote_internal) | QuorumVote::No(vote_internal) => { + vote_internal.leaf_commitment + } + QuorumVote::Timeout(_) => unimplemented!(), + }; let qc = QuorumCertificate { - leaf_commitment: commit, - view_number, + leaf_commitment, + view_number: vote.get_view(), signatures, is_genesis: false, }; @@ -224,16 +237,17 @@ impl> Committable impl SignedCertificate for DACertificate { + type Vote = DAVote; + type VoteAccumulator = DAVoteAccumulator; + fn from_signatures_and_commitment( - view_number: TYPES::Time, signatures: AssembledSignature, - commit: Commitment, - _relay: Option, + vote: Self::Vote, ) -> Self { DACertificate { - view_number, + view_number: vote.get_view(), signatures, - block_commitment: commit, + block_commitment: vote.block_commitment, } } @@ -312,16 +326,16 @@ impl SignedCertificate> for ViewSyncCertificate { + type Vote = ViewSyncVote; + type VoteAccumulator = ViewSyncVoteAccumulator, Self::Vote>; /// Build a QC from the threshold signature and commitment fn from_signatures_and_commitment( - view_number: TYPES::Time, signatures: AssembledSignature, - _commit: Commitment>, - relay: Option, + vote: Self::Vote, ) -> Self { let certificate_internal = ViewSyncCertificateInternal { - round: view_number, - relay: relay.unwrap(), + round: vote.get_view(), + relay: vote.relay(), signatures: signatures.clone(), }; match signatures { diff --git a/crates/types/src/data.rs b/crates/types/src/data.rs index 3f12fb7c82..8275c8d9b8 100644 --- a/crates/types/src/data.rs +++ b/crates/types/src/data.rs @@ -14,9 +14,10 @@ use crate::{ signature_key::{EncodedPublicKey, SignatureKey}, state::{ConsensusTime, TestableBlock, TestableState}, storage::StoredView, - Block, State, + BlockPayload, State, }, }; +use ark_bls12_381::Bls12_381; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use bincode::Options; use commit::{Commitment, Committable}; @@ -25,6 +26,7 @@ use either::Either; use espresso_systems_common::hotshot::tag; use hotshot_constants::GENESIS_PROPOSER_ID; use hotshot_utils::bincode::bincode_opts; +use jf_primitives::pcs::{checked_fft_size, prelude::UnivariateKzgPCS, PolynomialCommitmentScheme}; use rand::Rng; use serde::{Deserialize, Serialize}; use snafu::{ensure, Snafu}; @@ -107,7 +109,7 @@ pub fn genesis_proposer_id() -> EncodedPublicKey { } /// The `Transaction` type associated with a `State`, as a syntactic shortcut -pub type Transaction = <::BlockType as Block>::Transaction; +pub type Transaction = <::BlockType as BlockPayload>::Transaction; /// `Commitment` to the `Transaction` type associated with a `State`, as a syntactic shortcut pub type TxnCommitment = Commitment>; @@ -137,14 +139,14 @@ where #[debug(skip)] pub parent_commitment: Commitment, - /// Block leaf wants to apply + /// BlockPayload leaf wants to apply pub deltas: TYPES::BlockType, /// What the state should be after applying `self.deltas` pub state_commitment: Commitment, /// Transactions that were marked for rejection while collecting deltas - pub rejected: Vec<::Transaction>, + pub rejected: Vec<::Transaction>, /// the propser id pub proposer_id: EncodedPublicKey, @@ -153,12 +155,50 @@ where /// A proposal to start providing data availability for a block. #[derive(custom_debug::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] pub struct DAProposal { - /// Block leaf wants to apply + /// BlockPayload leaf wants to apply pub deltas: TYPES::BlockType, /// View this proposal applies to pub view_number: TYPES::Time, } +/// The VID scheme type used in `HotShot`. +pub type VidScheme = jf_primitives::vid::advz::Advz; +pub use jf_primitives::vid::VidScheme as VidSchemeTrait; + +/// VID dispersal data +/// +/// Like [`DAProposal`]. +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub struct VidDisperse { + /// The view number for which this VID data is intended + pub view_number: TYPES::Time, + /// Block commitment + pub commitment: Commitment, + /// VID shares dispersed among storage nodes + pub shares: Vec<::StorageShare>, + /// VID common data sent to all storage nodes + pub common: ::StorageCommon, +} + +/// Trusted KZG setup for VID. +/// +/// TESTING ONLY: don't use this in production +/// TODO +/// +/// # Panics +/// ...because this is only for tests. This comment exists to pacify clippy. +#[must_use] +pub fn test_srs( + num_storage_nodes: usize, +) -> as PolynomialCommitmentScheme>::SRS { + let mut rng = jf_utils::test_rng(); + UnivariateKzgPCS::::gen_srs_for_testing( + &mut rng, + checked_fft_size(num_storage_nodes).unwrap(), + ) + .unwrap() +} + /// Proposal to append a block. #[derive(custom_debug::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] #[serde(bound(deserialize = ""))] @@ -202,6 +242,13 @@ impl ProposalType for DAProposal { } } +impl ProposalType for VidDisperse { + type NodeType = TYPES; + fn get_view_number(&self) -> ::Time { + self.view_number + } +} + impl> ProposalType for QuorumProposal { @@ -239,14 +286,14 @@ pub trait ProposalType: /// full. It is guaranteed to contain, at least, a cryptographic commitment to the block, and it /// provides an interface for resolving the commitment to a full block if the full block is /// available. -pub trait DeltasType: +pub trait DeltasType: Clone + Debug + for<'a> Deserialize<'a> + PartialEq + Eq + std::hash::Hash + Send + Serialize + Sync { /// Errors reported by this type. type Error: std::error::Error; /// Get a cryptographic commitment to the block represented by this delta. - fn block_commitment(&self) -> Commitment; + fn block_commitment(&self) -> Commitment; /// Get the full block if it is available, otherwise return this object unchanged. /// @@ -254,7 +301,7 @@ pub trait DeltasType: /// /// Returns the original [`DeltasType`], unchanged, in an [`Err`] variant in the case where the /// full block is not currently available. - fn try_resolve(self) -> Result; + fn try_resolve(self) -> Result; /// Fill this [`DeltasType`] by providing a complete block. /// @@ -265,7 +312,7 @@ pub trait DeltasType: /// /// Fails if `block` does not match `self.block_commitment()`, or if the block is not able to be /// stored for some implementation-defined reason. - fn fill(&mut self, block: Block) -> Result<(), Self::Error>; + fn fill(&mut self, block: BlockPayload) -> Result<(), Self::Error>; } /// Error which occurs when [`DeltasType::fill`] is called with a block that does not match the @@ -448,10 +495,10 @@ pub type LeafDeltasError = as DeltasType pub type LeafNode = ::NodeType; /// The [`StateType`] in a [`LeafType`]. pub type LeafState = as NodeType>::StateType; -/// The [`Block`] in a [`LeafType`]. +/// The [`BlockPayload`] in a [`LeafType`]. pub type LeafBlock = as NodeType>::BlockType; /// The [`Transaction`] in a [`LeafType`]. -pub type LeafTransaction = as Block>::Transaction; +pub type LeafTransaction = as BlockPayload>::Transaction; /// The [`ConsensusTime`] used by a [`LeafType`]. pub type LeafTime = as NodeType>::Time; @@ -465,12 +512,12 @@ pub trait TestableLeaf { &self, rng: &mut dyn rand::RngCore, padding: u64, - ) -> <::BlockType as Block>::Transaction; + ) -> <::BlockType as BlockPayload>::Transaction; } /// This is the consensus-internal analogous concept to a block, and it contains the block proper, /// as well as the hash of its parent `Leaf`. -/// NOTE: `State` is constrained to implementing `BlockContents`, is `TypeMap::Block` +/// NOTE: `State` is constrained to implementing `BlockContents`, is `TypeMap::BlockPayload` #[derive(Serialize, Deserialize, Clone, Debug, Derivative)] #[serde(bound(deserialize = ""))] #[derivative(Hash, PartialEq, Eq)] @@ -488,14 +535,14 @@ pub struct ValidatingLeaf { /// So we can ask if it extends pub parent_commitment: Commitment>, - /// Block leaf wants to apply + /// BlockPayload leaf wants to apply pub deltas: TYPES::BlockType, /// What the state should be AFTER applying `self.deltas` pub state: TYPES::StateType, /// Transactions that were marked for rejection while collecting deltas - pub rejected: Vec<::Transaction>, + pub rejected: Vec<::Transaction>, /// the timestamp the leaf was constructed at, in nanoseconds. Only exposed for dashboard stats #[derivative(PartialEq = "ignore")] @@ -510,7 +557,7 @@ pub struct ValidatingLeaf { /// This is the consensus-internal analogous concept to a block, and it contains the block proper, /// as well as the hash of its parent `Leaf`. -/// NOTE: `State` is constrained to implementing `BlockContents`, is `TypeMap::Block` +/// NOTE: `State` is constrained to implementing `BlockContents`, is `TypeMap::BlockPayload` #[derive(Serialize, Deserialize, Clone, Debug, Derivative, Eq)] #[serde(bound(deserialize = ""))] pub struct SequencingLeaf { @@ -531,7 +578,7 @@ pub struct SequencingLeaf { pub deltas: Either>, /// Transactions that were marked for rejection while collecting deltas - pub rejected: Vec<::Transaction>, + pub rejected: Vec<::Transaction>, /// the timestamp the leaf was constructed at, in nanoseconds. Only exposed for dashboard stats pub timestamp: i128, @@ -648,7 +695,7 @@ impl LeafType for ValidatingLeaf { self.state.clone() } - fn get_rejected(&self) -> Vec<::Transaction> { + fn get_rejected(&self) -> Vec<::Transaction> { self.rejected.clone() } @@ -686,7 +733,7 @@ where &self, rng: &mut dyn rand::RngCore, padding: u64, - ) -> <::BlockType as Block>::Transaction { + ) -> <::BlockType as BlockPayload>::Transaction { ::create_random_transaction( Some(&self.state), rng, @@ -763,7 +810,7 @@ impl LeafType for SequencingLeaf { // The Sequencing Leaf doesn't have a state. fn get_state(&self) -> Self::MaybeState {} - fn get_rejected(&self) -> Vec<::Transaction> { + fn get_rejected(&self) -> Vec<::Transaction> { self.rejected.clone() } @@ -800,7 +847,7 @@ where &self, rng: &mut dyn rand::RngCore, padding: u64, - ) -> <::BlockType as Block>::Transaction { + ) -> <::BlockType as BlockPayload>::Transaction { TYPES::StateType::create_random_transaction(None, rng, padding) } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 72ad6ce093..6152689acf 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -35,7 +35,7 @@ pub enum ExecutionType { /// Holds configuration for a `HotShot` #[derive(Clone, custom_debug::Debug, serde::Serialize, serde::Deserialize)] -pub struct HotShotConfig { +pub struct HotShotConfig { /// Whether to run one view or continuous views pub execution_type: ExecutionType, /// Total number of nodes in the network @@ -44,8 +44,6 @@ pub struct HotShotConfig { pub min_transactions: usize, /// Maximum transactions per block pub max_transactions: NonZeroUsize, - /// List of known node's public keys, including own, sorted by nonce () - pub known_nodes: Vec, /// List of known node's public keys and stake value for certificate aggregation, serving as public parameter pub known_nodes_with_stake: Vec, /// List of DA committee nodes for static DA committe diff --git a/crates/types/src/message.rs b/crates/types/src/message.rs index 89d2864aa9..d5897c0ef8 100644 --- a/crates/types/src/message.rs +++ b/crates/types/src/message.rs @@ -5,7 +5,7 @@ use crate::{ certificate::DACertificate, - data::{DAProposal, ProposalType}, + data::{DAProposal, ProposalType, VidDisperse}, traits::{ network::{NetworkMsg, ViewMessage}, node_implementation::{ @@ -52,11 +52,13 @@ impl> ViewMessage for Messa #[derive(Clone, Debug)] pub struct Messages>(pub Vec>); -/// A message type agnostic description of a messages purpose +/// A message type agnostic description of a message's purpose #[derive(PartialEq, Copy, Clone)] pub enum MessagePurpose { /// Message with a quorum proposal. Proposal, + /// Message with most recent proposal the server has + CurrentProposal, /// Message with a quorum vote. Vote, /// Message with a view sync vote. @@ -69,6 +71,12 @@ pub enum MessagePurpose { Internal, /// Data message Data, + /// VID disperse, like [`Proposal`]. + VidDisperse, + /// VID vote, like [`Vote`]. + VidVote, + /// VID certificate, like [`DAC`]. + VidCert, } // TODO (da) make it more customized to the consensus layer, maybe separating the specific message @@ -207,6 +215,12 @@ pub enum ProcessedCommitteeConsensusMessage { DAVote(DAVote, TYPES::SignatureKey), /// Certificate for the DA. DACertificate(DACertificate, TYPES::SignatureKey), + /// VID dispersal data. Like [`DAProposal`] + VidDisperseMsg(Proposal>, TYPES::SignatureKey), + /// Vote from VID storage node. Like [`DAVote`] + VidVote(DAVote, TYPES::SignatureKey), + /// Certificate for VID. Like [`DACertificate`] + VidCertificate(DACertificate, TYPES::SignatureKey), } impl From> @@ -223,6 +237,15 @@ impl From> ProcessedCommitteeConsensusMessage::DACertificate(cert, _) => { CommitteeConsensusMessage::DACertificate(cert) } + ProcessedCommitteeConsensusMessage::VidDisperseMsg(disperse, _) => { + CommitteeConsensusMessage::VidDisperseMsg(disperse) + } + ProcessedCommitteeConsensusMessage::VidVote(v, _) => { + CommitteeConsensusMessage::VidVote(v) + } + ProcessedCommitteeConsensusMessage::VidCertificate(cert, _) => { + CommitteeConsensusMessage::VidCertificate(cert) + } } } } @@ -240,6 +263,15 @@ impl ProcessedCommitteeConsensusMessage { CommitteeConsensusMessage::DACertificate(cert) => { ProcessedCommitteeConsensusMessage::DACertificate(cert, sender) } + CommitteeConsensusMessage::VidDisperseMsg(disperse) => { + ProcessedCommitteeConsensusMessage::VidDisperseMsg(disperse, sender) + } + CommitteeConsensusMessage::VidVote(v) => { + ProcessedCommitteeConsensusMessage::VidVote(v, sender) + } + CommitteeConsensusMessage::VidCertificate(cert) => { + ProcessedCommitteeConsensusMessage::VidCertificate(cert, sender) + } } } } @@ -307,6 +339,23 @@ pub enum CommitteeConsensusMessage { /// Certificate data is available DACertificate(DACertificate), + + /// Initiate VID dispersal. + /// + /// Like [`DAProposal`]. Use `Msg` suffix to distinguish from [`VidDisperse`]. + /// TODO this variant should not be a [`CommitteeConsensusMessage`] because + VidDisperseMsg(Proposal>), + + /// Vote for VID disperse data + /// + /// Like [`DAVote`]. + /// TODO currently re-using [`DAVote`]; do we need a separate VID vote? + VidVote(DAVote), + /// VID certificate data is available + /// + /// Like [`DACertificate`] + /// TODO currently re-using [`DACertificate`]; do we need a separate VID cert? + VidCertificate(DACertificate), } /// Messages related to the consensus protocol. @@ -359,7 +408,7 @@ impl< // this should match replica upon receipt p.data.get_view_number() } - GeneralConsensusMessage::Vote(vote_message) => vote_message.current_view(), + GeneralConsensusMessage::Vote(vote_message) => vote_message.get_view(), GeneralConsensusMessage::InternalTrigger(trigger) => match trigger { InternalTrigger::Timeout(time) => *time, }, @@ -376,8 +425,13 @@ impl< // this should match replica upon receipt p.data.get_view_number() } - CommitteeConsensusMessage::DAVote(vote_message) => vote_message.current_view(), - CommitteeConsensusMessage::DACertificate(cert) => cert.view_number, + CommitteeConsensusMessage::DAVote(vote_message) => vote_message.get_view(), + CommitteeConsensusMessage::DACertificate(cert) + | CommitteeConsensusMessage::VidCertificate(cert) => cert.view_number, + CommitteeConsensusMessage::VidDisperseMsg(disperse) => { + disperse.data.get_view_number() + } + CommitteeConsensusMessage::VidVote(vote) => vote.get_view(), } } } @@ -397,7 +451,10 @@ impl< Right(committee_message) => match committee_message { CommitteeConsensusMessage::DAProposal(_) => MessagePurpose::Proposal, CommitteeConsensusMessage::DAVote(_) => MessagePurpose::Vote, + CommitteeConsensusMessage::VidVote(_) => MessagePurpose::VidVote, CommitteeConsensusMessage::DACertificate(_) => MessagePurpose::DAC, + CommitteeConsensusMessage::VidDisperseMsg(_) => MessagePurpose::VidDisperse, + CommitteeConsensusMessage::VidCertificate(_) => todo!(), }, } } diff --git a/crates/types/src/traits.rs b/crates/types/src/traits.rs index c6a76acea3..9c25e5fcb8 100644 --- a/crates/types/src/traits.rs +++ b/crates/types/src/traits.rs @@ -11,5 +11,5 @@ pub mod stake_table; pub mod state; pub mod storage; -pub use block_contents::Block; +pub use block_contents::BlockPayload; pub use state::State; diff --git a/crates/types/src/traits/block_contents.rs b/crates/types/src/traits/block_contents.rs index b7d9b4da44..eb4dce2753 100644 --- a/crates/types/src/traits/block_contents.rs +++ b/crates/types/src/traits/block_contents.rs @@ -1,10 +1,10 @@ //! Abstraction over the contents of a block //! -//! This module provides the [`Block`] trait, which describes the behaviors that a block is -//! expected to have. +//! This module provides the [`BlockPayload`] and [`BlockHeader`] traits, which describe the +//! behaviors that a block is expected to have. use commit::{Commitment, Committable}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; use std::{ collections::HashSet, @@ -15,14 +15,15 @@ use std::{ /// Abstraction over the full contents of a block /// -/// This trait encapsulates the behaviors that a block must have in order to be used by consensus: -/// * Must have a predefined error type ([`Block::Error`]) +/// This trait encapsulates the behaviors that the transactions of a block must have in order to be +/// used by consensus +/// * 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`](Block::add_transaction_raw)) +/// ([`add_transaction_raw`](BlockPayload::add_transaction_raw)) /// * Must be hashable -pub trait Block: +pub trait BlockPayload: Serialize + Clone + Debug @@ -58,22 +59,17 @@ pub trait Block: fn contained_transactions(&self) -> HashSet>; } -/// Commitment to a block, used by data availibity -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] -#[serde(bound(deserialize = ""), transparent)] -pub struct BlockCommitment(pub Commitment); - -/// Abstraction over any type of transaction. Used by [`Block`]. +/// Abstraction over any type of transaction. Used by [`BlockPayload`]. pub trait Transaction: Clone + Serialize + DeserializeOwned + Debug + PartialEq + Eq + Sync + Send + Committable + Hash { } -/// Dummy implementation of `BlockContents` for unit tests +/// Dummy implementation of `BlockPayload` for unit tests pub mod dummy { use std::fmt::Display; - use super::{Block, Commitment, Committable, Debug, Hash, HashSet, Serialize}; + use super::{BlockPayload, Commitment, Committable, Debug, Hash, HashSet, Serialize}; use rand::Rng; use serde::Deserialize; @@ -107,7 +103,7 @@ pub mod dummy { impl Committable for DummyTransaction { fn commit(&self) -> commit::Commitment { - commit::RawCommitmentBuilder::new("Dummy Block Comm") + commit::RawCommitmentBuilder::new("Dummy BlockPayload Comm") .u64_field("Dummy Field", 0) .finalize() } @@ -132,7 +128,7 @@ pub mod dummy { } } - impl Block for DummyBlock { + impl BlockPayload for DummyBlock { type Error = DummyError; type Transaction = DummyTransaction; @@ -167,7 +163,7 @@ pub mod dummy { impl Committable for DummyBlock { fn commit(&self) -> commit::Commitment { - commit::RawCommitmentBuilder::new("Dummy Block Comm") + commit::RawCommitmentBuilder::new("Dummy BlockPayload Comm") .u64_field("Nonce", self.nonce) .finalize() } diff --git a/crates/types/src/traits/consensus_api.rs b/crates/types/src/traits/consensus_api.rs index 2687107886..ab3f899944 100644 --- a/crates/types/src/traits/consensus_api.rs +++ b/crates/types/src/traits/consensus_api.rs @@ -2,7 +2,7 @@ use crate::{ certificate::QuorumCertificate, - data::{LeafType, ProposalType}, + data::LeafType, error::HotShotError, event::{Event, EventType}, message::{DataMessage, SequencingMessage}, @@ -12,7 +12,6 @@ use crate::{ signature_key::SignatureKey, storage::StorageError, }, - vote::VoteType, }; use async_trait::async_trait; @@ -130,27 +129,21 @@ pub trait SequencingConsensusApi< >: ConsensusSharedApi { /// Send a direct message to the given recipient - async fn send_direct_message, VOTE: VoteType>( + async fn send_direct_message( &self, recipient: TYPES::SignatureKey, message: SequencingMessage, ) -> std::result::Result<(), NetworkError>; /// send a direct message using the DA communication channel - async fn send_direct_da_message< - PROPOSAL: ProposalType, - VOTE: VoteType, - >( + async fn send_direct_da_message( &self, recipient: TYPES::SignatureKey, message: SequencingMessage, ) -> std::result::Result<(), NetworkError>; /// Send a broadcast message to the entire network. - async fn send_broadcast_message< - PROPOSAL: ProposalType, - VOTE: VoteType, - >( + async fn send_broadcast_message( &self, message: SequencingMessage, ) -> std::result::Result<(), NetworkError>; diff --git a/crates/types/src/traits/election.rs b/crates/types/src/traits/election.rs index 81a173e954..37f0be4753 100644 --- a/crates/types/src/traits/election.rs +++ b/crates/types/src/traits/election.rs @@ -15,10 +15,11 @@ use crate::{ }; use crate::{ - message::{CommitteeConsensusMessage, GeneralConsensusMessage, Message}, + message::{GeneralConsensusMessage, Message}, vote::ViewSyncVoteInternal, }; +use crate::vote::Accumulator2; use crate::{ data::LeafType, traits::{ @@ -28,8 +29,8 @@ use crate::{ state::ConsensusTime, }, vote::{ - Accumulator, DAVote, QuorumVote, TimeoutVote, ViewSyncData, ViewSyncVote, VoteAccumulator, - VoteType, YesOrNoVote, + DAVote, QuorumVote, TimeoutVote, ViewSyncData, ViewSyncVote, VoteAccumulator, VoteType, + YesOrNoVote, }, }; use bincode::Options; @@ -60,6 +61,7 @@ pub enum ElectionError { /// the outcome is already knowable. /// /// This would be a useful general utility. +#[derive(Clone)] pub enum Checked { /// This item has been checked, and is valid Valid(T), @@ -93,9 +95,11 @@ pub enum VoteData { impl Committable for VoteData { fn commit(&self) -> Commitment { match self { - VoteData::DA(block_commitment) => commit::RawCommitmentBuilder::new("DA Block Commit") - .field("block_commitment", *block_commitment) - .finalize(), + VoteData::DA(block_commitment) => { + commit::RawCommitmentBuilder::new("DA BlockPayload Commit") + .field("block_commitment", *block_commitment) + .finalize() + } VoteData::Yes(leaf_commitment) => commit::RawCommitmentBuilder::new("Yes Vote Commit") .field("leaf_commitment", *leaf_commitment) .finalize(), @@ -181,12 +185,19 @@ where COMMITTABLE: Committable + Serialize + Clone, TOKEN: VoteToken, { + /// `VoteType` that is used in this certificate + type Vote: VoteType; + + /// `Accumulator` type to accumulate votes. + type VoteAccumulator: Accumulator2; + /// Build a QC from the threshold signature and commitment + // TODO ED Rename this function and rework this function parameters + // Assumes last vote was valid since it caused a QC to form. + // Removes need for relay on other cert specific fields fn from_signatures_and_commitment( - view_number: TIME, signatures: AssembledSignature, - commit: Commitment, - relay: Option, + vote: Self::Vote, ) -> Self; /// Get the view number. @@ -196,6 +207,7 @@ where fn signatures(&self) -> AssembledSignature; // TODO (da) the following functions should be refactored into a QC-specific trait. + // TODO ED Make an issue for this /// Get the leaf commitment. fn leaf_commitment(&self) -> Commitment; @@ -221,7 +233,6 @@ pub trait Membership: /// TODO may want to move this to a testableelection trait fn create_election( entries: Vec<::StakeTableEntry>, - keys: Vec, config: TYPES::ElectionConfigType, ) -> Self; @@ -276,7 +287,8 @@ pub trait ConsensusExchange: Send + Sync { /// A proposal for participants to vote on. type Proposal: ProposalType; /// A vote on a [`Proposal`](Self::Proposal). - type Vote: VoteType; + // TODO ED Make this equal Certificate vote (if possible?) + type Vote: VoteType; /// A [`SignedCertificate`] attesting to a decision taken by the committee. type Certificate: SignedCertificate + Hash @@ -284,14 +296,13 @@ pub trait ConsensusExchange: Send + Sync { /// The committee eligible to make decisions. type Membership: Membership; /// Network used by [`Membership`](Self::Membership) to communicate. - type Networking: CommunicationChannel; + type Networking: CommunicationChannel; /// Commitments to items which are the subject of proposals and decisions. type Commitment: Committable + Serialize + Clone; /// Join a [`ConsensusExchange`] with the given identity (`pk` and `sk`). fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, config: TYPES::ElectionConfigType, network: Self::Networking, pk: TYPES::SignatureKey, @@ -414,59 +425,38 @@ pub trait ConsensusExchange: Send + Sync { is_valid_signature && is_valid_vote_token } + /// Validate a vote by checking its signature and token. + fn is_valid_vote_2( + &self, + key: &TYPES::SignatureKey, + encoded_signature: &EncodedSignature, + data: &VoteData, + vote_token: &Checked, + ) -> bool { + let is_valid_signature = key.validate(encoded_signature, data.commit().as_ref()); + let valid_vote_token = self + .membership() + .validate_vote_token(key.clone(), vote_token.clone()); + let is_valid_vote_token = match valid_vote_token { + Err(_) => { + error!("Vote token was invalid"); + false + } + Ok(Checked::Valid(_)) => true, + Ok(Checked::Inval(_) | Checked::Unchecked(_)) => false, + }; + + is_valid_signature && is_valid_vote_token + } + #[doc(hidden)] + fn accumulate_internal( &self, - vota_meta: VoteMetaData, - accumulator: VoteAccumulator, + _vota_meta: VoteMetaData, + _accumulator: VoteAccumulator, ) -> Either, Self::Certificate> { - if !self.is_valid_vote( - &vota_meta.encoded_key, - &vota_meta.encoded_signature, - vota_meta.data.clone(), - // Ignoring deserialization errors below since we are getting rid of it soon - Checked::Unchecked(vota_meta.vote_token.clone()), - ) { - error!("Invalid vote!"); - return Either::Left(accumulator); - } - - if let Some(key) = ::from_bytes(&vota_meta.encoded_key) - { - let stake_table_entry = key.get_stake_table_entry(1u64); - let append_node_id = self - .membership() - .get_committee_qc_stake_table() - .iter() - .position(|x| *x == stake_table_entry.clone()) - .unwrap(); - - match accumulator.append(( - vota_meta.commitment, - ( - vota_meta.encoded_key.clone(), - ( - vota_meta.encoded_signature.clone(), - self.membership().get_committee_qc_stake_table(), - append_node_id, - vota_meta.data, - vota_meta.vote_token, - ), - ), - )) { - Either::Left(accumulator) => Either::Left(accumulator), - Either::Right(signatures) => { - Either::Right(Self::Certificate::from_signatures_and_commitment( - vota_meta.view_number, - signatures, - vota_meta.commitment, - vota_meta.relay, - )) - } - } - } else { - Either::Left(accumulator) - } + todo!() // TODO ED Remove this function } /// Add a vote to the accumulating signature. Return The certificate if the vote @@ -484,6 +474,72 @@ pub trait ConsensusExchange: Send + Sync { relay: Option, ) -> Either, Self::Certificate>; + // TODO ED Depending on what we do in the future with the exchanges trait, we can move the accumulator out of the `SignedCertificate` + // trait. Logically, I feel it makes sense to accumulate on the certificate rather than the exchange, however. + /// Accumulate vote + /// Returns either the accumulate if no threshold was reached, or a `SignedCertificate` if the threshold was reached + #[allow(clippy::type_complexity)] + fn accumulate_vote_2( + &self, + accumulator: <>::Certificate as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + Self::Commitment, + >>::VoteAccumulator, + vote: &<>::Certificate as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + Self::Commitment, + >>::Vote, + _commit: &Commitment, + ) -> Either< + <>::Certificate as SignedCertificate< + TYPES, + TYPES::Time, + TYPES::VoteTokenType, + Self::Commitment, + >>::VoteAccumulator, + Self::Certificate, + > { + if !self.is_valid_vote_2( + &vote.get_key(), + &vote.get_signature(), + &vote.get_data(), + // TODO ED We've had this comment for a while: Ignoring deserialization errors below since we are getting rid of it soon + &Checked::Unchecked(vote.get_vote_token()), + ) { + error!("Invalid vote!"); + return Either::Left(accumulator); + } + + let stake_table_entry = vote.get_key().get_stake_table_entry(1u64); + // TODO ED Could we make this part of the vote in the future? It's only a usize. + let append_node_id = self + .membership() + .get_committee_qc_stake_table() + .iter() + .position(|x| *x == stake_table_entry.clone()) + .unwrap(); + + // TODO ED Should make append function take a reference to vote + match accumulator.append( + vote.clone(), + append_node_id, + self.membership().get_committee_qc_stake_table(), + ) { + Either::Left(accumulator) => Either::Left(accumulator), + Either::Right(signatures) => { + // TODO ED Update this function to just take in the signatures and most recent vote + Either::Right(Self::Certificate::from_signatures_and_commitment( + signatures, + vote.clone(), + )) + } + } + } + /// The committee which votes on proposals. fn membership(&self) -> &Self::Membership; @@ -517,7 +573,23 @@ pub trait CommitteeExchangeType: block_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, - ) -> CommitteeConsensusMessage; + ) -> DAVote; + + // TODO temporary vid methods, move to quorum https://github.com/EspressoSystems/HotShot/issues/1696 + + /// Create a message with a vote on VID disperse data. + fn create_vid_message( + &self, + block_commitment: Commitment, + current_view: TYPES::Time, + vote_token: TYPES::VoteTokenType, + ) -> DAVote; + + /// Sign a vote on VID proposal. + fn sign_vid_vote( + &self, + block_commitment: Commitment, + ) -> (EncodedPublicKey, EncodedSignature); } /// Standard implementation of [`CommitteeExchangeType`] utilizing a DA committee. @@ -526,7 +598,7 @@ pub trait CommitteeExchangeType: pub struct CommitteeExchange< TYPES: NodeType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, DAVote, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > { /// The network being used by this exchange. @@ -547,7 +619,7 @@ pub struct CommitteeExchange< impl< TYPES: NodeType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, DAVote, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > CommitteeExchangeType for CommitteeExchange { @@ -581,22 +653,51 @@ impl< block_commitment: Commitment, current_view: TYPES::Time, vote_token: TYPES::VoteTokenType, - ) -> CommitteeConsensusMessage { + ) -> DAVote { let signature = self.sign_da_vote(block_commitment); - CommitteeConsensusMessage::::DAVote(DAVote { + DAVote { + signature, + block_commitment, + current_view, + vote_token, + vote_data: VoteData::DA(block_commitment), + } + } + + fn create_vid_message( + &self, + block_commitment: Commitment, + current_view: ::Time, + vote_token: ::VoteTokenType, + ) -> DAVote { + let signature = self.sign_vid_vote(block_commitment); + DAVote { signature, block_commitment, current_view, vote_token, vote_data: VoteData::DA(block_commitment), - }) + } + } + + fn sign_vid_vote( + &self, + block_commitment: Commitment<::BlockType>, + ) -> (EncodedPublicKey, EncodedSignature) { + let signature = TYPES::SignatureKey::sign( + &self.private_key, + VoteData::::DA(block_commitment) + .commit() + .as_ref(), + ); + (self.public_key.to_bytes(), signature) } } impl< TYPES: NodeType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, DAVote, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > ConsensusExchange for CommitteeExchange { @@ -609,16 +710,14 @@ impl< fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, config: TYPES::ElectionConfigType, network: Self::Networking, pk: TYPES::SignatureKey, entry: ::StakeTableEntry, sk: ::PrivateKey, ) -> Self { - let membership = >::Membership::create_election( - entries, keys, config, - ); + let membership = + >::Membership::create_election(entries, config); Self { network, membership, @@ -760,7 +859,7 @@ pub struct QuorumExchange< LEAF: LeafType, PROPOSAL: ProposalType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > { /// The network being used by this exchange. @@ -783,7 +882,7 @@ impl< LEAF: LeafType, MEMBERSHIP: Membership, PROPOSAL: ProposalType, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > QuorumExchangeType for QuorumExchange @@ -915,7 +1014,7 @@ impl< LEAF: LeafType, PROPOSAL: ProposalType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > ConsensusExchange for QuorumExchange @@ -929,16 +1028,14 @@ impl< fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, config: TYPES::ElectionConfigType, network: Self::Networking, pk: TYPES::SignatureKey, entry: ::StakeTableEntry, sk: ::PrivateKey, ) -> Self { - let membership = >::Membership::create_election( - entries, keys, config, - ); + let membership = + >::Membership::create_election(entries, config); Self { network, membership, @@ -1052,7 +1149,7 @@ pub struct ViewSyncExchange< TYPES: NodeType, PROPOSAL: ProposalType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > { /// The network being used by this exchange. @@ -1074,7 +1171,7 @@ impl< TYPES: NodeType, MEMBERSHIP: Membership, PROPOSAL: ProposalType, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > ViewSyncExchangeType for ViewSyncExchange { @@ -1275,7 +1372,7 @@ impl< TYPES: NodeType, PROPOSAL: ProposalType, MEMBERSHIP: Membership, - NETWORK: CommunicationChannel, MEMBERSHIP>, + NETWORK: CommunicationChannel, M: NetworkMsg, > ConsensusExchange for ViewSyncExchange { @@ -1288,16 +1385,14 @@ impl< fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, config: TYPES::ElectionConfigType, network: Self::Networking, pk: TYPES::SignatureKey, entry: ::StakeTableEntry, sk: ::PrivateKey, ) -> Self { - let membership = >::Membership::create_election( - entries, keys, config, - ); + let membership = + >::Membership::create_election(entries, config); Self { network, membership, diff --git a/crates/types/src/traits/network.rs b/crates/types/src/traits/network.rs index 2f7ff00f1b..ef02b05245 100644 --- a/crates/types/src/traits/network.rs +++ b/crates/types/src/traits/network.rs @@ -11,11 +11,7 @@ use tokio::time::error::Elapsed as TimeoutError; #[cfg(not(any(async_executor_impl = "async-std", async_executor_impl = "tokio")))] compile_error! {"Either config option \"async-std\" or \"tokio\" must be enabled for this crate."} use super::{election::Membership, node_implementation::NodeType, signature_key::SignatureKey}; -use crate::{ - data::{ProposalType, ViewNumber}, - message::MessagePurpose, - vote::VoteType, -}; +use crate::{data::ViewNumber, message::MessagePurpose}; use async_compatibility_layer::channel::UnboundedSendError; use async_trait::async_trait; use serde::{Deserialize, Serialize}; @@ -140,6 +136,8 @@ pub enum ConsensusIntentEvent { PollForVotes(u64), /// Poll for a proposal for a particular view PollForProposal(u64), + /// Poll for the most recent proposal the webserver has + PollForCurrentProposal, /// Poll for a DAC for a particular view PollForDAC(u64), /// Poll for view sync votes starting at a particular view @@ -182,6 +180,7 @@ impl ConsensusIntentEvent { | ConsensusIntentEvent::PollForTransactions(view_number) | ConsensusIntentEvent::CancelPollForTransactions(view_number) | ConsensusIntentEvent::PollFutureLeader(view_number, _) => *view_number, + ConsensusIntentEvent::PollForCurrentProposal => 1, } } } @@ -204,13 +203,8 @@ pub trait ViewMessage { /// API for interacting directly with a consensus committee /// intended to be implemented for both DA and for validating consensus committees #[async_trait] -pub trait CommunicationChannel< - TYPES: NodeType, - M: NetworkMsg, - PROPOSAL: ProposalType, - VOTE: VoteType, - MEMBERSHIP: Membership, ->: Clone + Debug + Send + Sync + 'static +pub trait CommunicationChannel>: + Clone + Debug + Send + Sync + 'static { /// Underlying Network implementation's type type NETWORK; @@ -353,11 +347,9 @@ pub trait TestableNetworkingImplementation { pub trait TestableChannelImplementation< TYPES: NodeType, M: NetworkMsg, - PROPOSAL: ProposalType, - VOTE: VoteType, MEMBERSHIP: Membership, NETWORK, ->: CommunicationChannel +>: CommunicationChannel { /// generates the `CommunicationChannel` given it's associated network type fn generate_network() -> Box) -> Self + 'static>; diff --git a/crates/types/src/traits/node_implementation.rs b/crates/types/src/traits/node_implementation.rs index 8ba5abdebb..49d9184bc4 100644 --- a/crates/types/src/traits/node_implementation.rs +++ b/crates/types/src/traits/node_implementation.rs @@ -19,7 +19,7 @@ use crate::{ message::{ConsensusMessageType, Message, SequencingMessage}, traits::{ election::Membership, network::TestableChannelImplementation, signature_key::SignatureKey, - storage::Storage, Block, + storage::Storage, BlockPayload, }, }; use async_compatibility_layer::channel::{unbounded, UnboundedReceiver, UnboundedSender}; @@ -166,7 +166,6 @@ pub trait ExchangesType, MESSA /// Create all exchanges. fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, configs: Self::ElectionConfigs, networks: ( >::Networking, @@ -184,7 +183,7 @@ pub trait ExchangesType, MESSA /// Get the view sync exchange. fn view_sync_exchange(&self) -> &Self::ViewSyncExchange; - /// Block the underlying networking interfaces until node is successfully initialized into the + /// BlockPayload the underlying networking interfaces until node is successfully initialized into the /// networks. async fn wait_for_networks_ready(&self); @@ -257,7 +256,6 @@ where fn create( entries: Vec<::StakeTableEntry>, - keys: Vec, configs: Self::ElectionConfigs, networks: ( >::Networking, @@ -270,7 +268,6 @@ where ) -> Self { let quorum_exchange = QUORUMEXCHANGE::create( entries.clone(), - keys.clone(), configs.0.clone(), networks.0, pk.clone(), @@ -279,7 +276,6 @@ where ); let view_sync_exchange = VIEWSYNCEXCHANGE::create( entries.clone(), - keys.clone(), configs.0, networks.2, pk.clone(), @@ -287,7 +283,7 @@ where sk.clone(), ); let committee_exchange = - COMMITTEEEXCHANGE::create(entries, keys, configs.1, networks.1, pk, entry, sk); + COMMITTEEEXCHANGE::create(entries, configs.1, networks.1, pk, entry, sk); Self { quorum_exchange, @@ -363,7 +359,7 @@ pub trait TestableNodeImplementation: NodeImplementation state: Option<&TYPES::StateType>, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction; + ) -> ::Transaction; /// Creates random transaction if possible /// otherwise panics @@ -372,7 +368,7 @@ pub trait TestableNodeImplementation: NodeImplementation leaf: &Self::Leaf, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction; + ) -> ::Transaction; /// generate a genesis block fn block_genesis() -> TYPES::BlockType; @@ -401,24 +397,18 @@ where QuorumCommChannel: TestableChannelImplementation< TYPES, Message, - QuorumProposalType, - QuorumVoteType, QuorumMembership, QuorumNetwork, >, CommitteeCommChannel: TestableChannelImplementation< TYPES, Message, - CommitteeProposalType, - CommitteeVote, CommitteeMembership, QuorumNetwork, >, ViewSyncCommChannel: TestableChannelImplementation< TYPES, Message, - ViewSyncProposalType, - ViewSyncVoteType, ViewSyncMembership, QuorumNetwork, >, @@ -438,7 +428,7 @@ where state: Option<&TYPES::StateType>, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction { + ) -> ::Transaction { ::create_random_transaction(state, rng, padding) } @@ -446,7 +436,7 @@ where leaf: &Self::Leaf, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction { + ) -> ::Transaction { ::create_random_transaction(leaf, rng, padding) } @@ -518,8 +508,6 @@ pub type ViewSyncMembership = QuorumMembership; pub type QuorumNetwork = as CommunicationChannel< TYPES, Message, - QuorumProposalType, - QuorumVoteType, QuorumMembership, >>::NETWORK; @@ -527,8 +515,6 @@ pub type QuorumNetwork = as Communication pub type CommitteeNetwork = as CommunicationChannel< TYPES, Message, - CommitteeProposalType, - CommitteeVote, CommitteeMembership, >>::NETWORK; @@ -536,8 +522,6 @@ pub type CommitteeNetwork = as Communi pub type ViewSyncNetwork = as CommunicationChannel< TYPES, Message, - ViewSyncProposalType, - ViewSyncVoteType, ViewSyncMembership, >>::NETWORK; @@ -565,14 +549,14 @@ pub trait NodeType: /// The block type that this hotshot setup is using. /// /// This should be the same block that `StateType::BlockType` is using. - type BlockType: Block; + type BlockType: BlockPayload; /// The signature key that this hotshot setup is using. type SignatureKey: SignatureKey; /// The vote token that this hotshot setup is using. type VoteTokenType: VoteToken; /// The transaction type that this hotshot setup is using. /// - /// This should be equal to `Block::Transaction` + /// This should be equal to `BlockPayload::Transaction` type Transaction: Transaction; /// The election config type that this hotshot setup is using. type ElectionConfigType: ElectionConfig; diff --git a/crates/types/src/traits/signature_key.rs b/crates/types/src/traits/signature_key.rs index 5daed175bd..d8f79a080e 100644 --- a/crates/types/src/traits/signature_key.rs +++ b/crates/types/src/traits/signature_key.rs @@ -101,6 +101,9 @@ pub trait SignatureKey: /// get the stake table entry from the public key and stake value fn get_stake_table_entry(&self, stake: u64) -> Self::StakeTableEntry; + /// only get the public key from the stake table entry + fn get_public_key(entry: &Self::StakeTableEntry) -> Self; + /// get the public parameter for the assembled signature checking fn get_public_parameter( stake_entries: Vec, diff --git a/crates/types/src/traits/state.rs b/crates/types/src/traits/state.rs index 83fb250442..93ff3e6603 100644 --- a/crates/types/src/traits/state.rs +++ b/crates/types/src/traits/state.rs @@ -3,7 +3,7 @@ //! This module provides the [`State`] trait, which serves as an compatibility over the current //! network state, which is modified by the transactions contained within blocks. -use crate::traits::Block; +use crate::traits::BlockPayload; use commit::Committable; use espresso_systems_common::hotshot::tag; use serde::{de::DeserializeOwned, Serialize}; @@ -19,7 +19,7 @@ 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 ([`Block`](State::BlockType)) +/// * 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 @@ -42,7 +42,7 @@ pub trait State: /// The error type for this particular type of ledger state type Error: Error + Debug + Send + Sync; /// The type of block this state is associated with - type BlockType: Block; + type BlockType: BlockPayload; /// Time compatibility needed for reward collection type Time: ConsensusTime; @@ -109,11 +109,11 @@ where state: Option<&Self>, rng: &mut dyn rand::RngCore, padding: u64, - ) -> ::Transaction; + ) -> ::Transaction; } /// extra functions required on block to be usable by hotshot-testing -pub trait TestableBlock: Block + Debug { +pub trait TestableBlock: BlockPayload + Debug { /// generate a genesis block fn genesis() -> Self; diff --git a/crates/types/src/traits/storage.rs b/crates/types/src/traits/storage.rs index 41b7cc225a..20c8f87f9a 100644 --- a/crates/types/src/traits/storage.rs +++ b/crates/types/src/traits/storage.rs @@ -4,7 +4,7 @@ use super::{node_implementation::NodeType, signature_key::EncodedPublicKey}; use crate::{ certificate::QuorumCertificate, data::LeafType, - traits::{election::SignedCertificate, Block}, + traits::{election::SignedCertificate, BlockPayload}, }; use async_trait::async_trait; use commit::Commitment; @@ -152,7 +152,7 @@ where TYPES: NodeType, LEAF: LeafType, { - /// Create a new `StoredView` from the given QC, Block and State. + /// Create a new `StoredView` from the given QC, `BlockPayload` and State. /// /// Note that this will set the `parent` to `LeafHash::default()`, so this will not have a parent. pub fn from_qc_block_and_state( @@ -161,7 +161,7 @@ where state: LEAF::MaybeState, height: u64, parent_commitment: Commitment, - rejected: Vec<::Transaction>, + rejected: Vec<::Transaction>, proposer_id: EncodedPublicKey, ) -> Self { Self { diff --git a/crates/types/src/vote.rs b/crates/types/src/vote.rs index cdbb8cb03f..a0ae42c926 100644 --- a/crates/types/src/vote.rs +++ b/crates/types/src/vote.rs @@ -25,16 +25,25 @@ use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, HashMap}, fmt::Debug, + 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 { - /// The view this vote was cast for. - fn current_view(&self) -> TYPES::Time; + /// Get the view this vote was cast for + fn get_view(&self) -> TYPES::Time; + /// Get the signature key associated with this vote + fn get_key(&self) -> TYPES::SignatureKey; + /// Get the signature associated with this vote + fn get_signature(&self) -> EncodedSignature; + /// Get the data this vote was signed over + fn get_data(&self) -> VoteData; + /// Get the vote token of this vote + fn get_vote_token(&self) -> TYPES::VoteTokenType; } /// A vote on DA proposal. @@ -190,10 +199,22 @@ pub enum QuorumVote> { Timeout(TimeoutVote), } -impl VoteType for DAVote { - fn current_view(&self) -> TYPES::Time { +impl VoteType for DAVote { + fn get_view(&self) -> TYPES::Time { self.current_view } + fn get_key(&self) -> ::SignatureKey { + self.signature_key() + } + fn get_signature(&self) -> EncodedSignature { + self.signature.1.clone() + } + fn get_data(&self) -> VoteData { + self.vote_data.clone() + } + fn get_vote_token(&self) -> ::VoteTokenType { + self.vote_token.clone() + } } impl DAVote { @@ -205,19 +226,39 @@ impl DAVote { } } -impl> VoteType +impl> VoteType for QuorumVote { - fn current_view(&self) -> TYPES::Time { + fn get_view(&self) -> TYPES::Time { match self { QuorumVote::Yes(v) | QuorumVote::No(v) => v.current_view, QuorumVote::Timeout(v) => v.current_view, } } + + fn get_key(&self) -> ::SignatureKey { + self.signature_key() + } + fn get_signature(&self) -> EncodedSignature { + self.signature() + } + fn get_data(&self) -> VoteData { + match self { + QuorumVote::Yes(v) | QuorumVote::No(v) => v.vote_data.clone(), + QuorumVote::Timeout(_) => unimplemented!(), + } + } + fn get_vote_token(&self) -> ::VoteTokenType { + match self { + QuorumVote::Yes(v) | QuorumVote::No(v) => v.vote_token.clone(), + QuorumVote::Timeout(_) => unimplemented!(), + } + } } impl> QuorumVote { /// Get the encoded signature. + pub fn signature(&self) -> EncodedSignature { match &self { Self::Yes(vote) | Self::No(vote) => vote.signature.1.clone(), @@ -227,6 +268,7 @@ impl> QuorumVote /// Get the signature key. /// # Panics /// If the deserialization fails. + pub fn signature_key(&self) -> TYPES::SignatureKey { let encoded = match &self { Self::Yes(vote) | Self::No(vote) => vote.signature.0.clone(), @@ -236,14 +278,36 @@ impl> QuorumVote } } -impl VoteType for ViewSyncVote { - fn current_view(&self) -> TYPES::Time { +impl VoteType> for ViewSyncVote { + fn get_view(&self) -> TYPES::Time { match self { ViewSyncVote::PreCommit(v) | ViewSyncVote::Commit(v) | ViewSyncVote::Finalize(v) => { v.round } } } + fn get_key(&self) -> ::SignatureKey { + self.signature_key() + } + + fn get_signature(&self) -> EncodedSignature { + self.signature() + } + fn get_data(&self) -> VoteData> { + match self { + ViewSyncVote::PreCommit(vote_internal) + | ViewSyncVote::Commit(vote_internal) + | ViewSyncVote::Finalize(vote_internal) => vote_internal.vote_data.clone(), + } + } + + fn get_vote_token(&self) -> ::VoteTokenType { + match self { + ViewSyncVote::PreCommit(vote_internal) + | ViewSyncVote::Commit(vote_internal) + | ViewSyncVote::Finalize(vote_internal) => vote_internal.vote_token.clone(), + } + } } /// The aggreation of votes, implemented by `VoteAccumulator`. @@ -255,7 +319,448 @@ pub trait Accumulator: Sized { fn append(self, val: T) -> Either; } +/// Accumulator trait used to accumulate votes into an `AssembledSignature` +pub trait Accumulator2< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +>: Sized +{ + /// Append 1 vote to the accumulator. If the threshold is not reached, return + /// the accumulator, else return the `AssembledSignature` + /// Only called from inside `accumulate_internal` + fn append( + self, + vote: VOTE, + vote_node_id: usize, + stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either>; +} + +/// Accumulates DA votes +pub struct DAVoteAccumulator< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +> { + /// Map of all da signatures accumlated so far + 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 + pub sig_lists: Vec<::Signature>, + /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check + pub signers: BitVec, + /// Phantom data to specify the vote this accumulator is for + pub phantom: PhantomData, +} + +impl< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, + > Accumulator2 for DAVoteAccumulator +{ + fn append( + mut self, + vote: VOTE, + vote_node_id: usize, + stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either> { + let VoteData::DA(vote_commitment) = vote.get_data() else { + return Either::Left(self); + }; + + let encoded_key = vote.get_key().to_bytes(); + + // Deserialize the signature so that it can be assembeld into a QC + // TODO ED Update this once we've gotten rid of EncodedSignature + let original_signature: ::Signature = + bincode_opts() + .deserialize(&vote.get_signature().0) + .expect("Deserialization on the signature shouldn't be able to fail."); + + let (da_stake_casted, da_vote_map) = self + .da_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + // Check for duplicate vote + // TODO ED Re-encoding signature key to bytes until we get rid of EncodedKey + // Have to do this because SignatureKey is not hashable + if da_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + if self.signers.get(vote_node_id).as_deref() == Some(&true) { + error!("Node id is already in signers list"); + return Either::Left(self); + } + self.signers.set(vote_node_id, true); + self.sig_lists.push(original_signature); + + // Already checked that vote data was for a DA vote above + *da_stake_casted += u64::from(vote.get_vote_token().vote_count()); + da_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + + if *da_stake_casted >= u64::from(self.success_threshold) { + // Assemble QC + let real_qc_pp = ::get_public_parameter( + // TODO ED Something about stake table entries. Might be easier to just pass in membership? + stake_table_entries.clone(), + U256::from(self.success_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + + self.da_vote_outcomes.remove(&vote_commitment); + + return Either::Right(AssembledSignature::DA(real_qc_sig)); + } + Either::Left(self) + } +} + +/// Accumulate quorum votes +pub struct QuorumVoteAccumulator< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +> { + /// Map of all signatures accumlated so far + pub total_vote_outcomes: VoteMap, + /// Map of all yes signatures accumlated so far + pub yes_vote_outcomes: VoteMap, + /// Map of all no signatures accumlated so far + pub no_vote_outcomes: VoteMap, + + /// A quorum's worth of stake, generally 2f + 1 + pub success_threshold: NonZeroU64, + /// A failure threshold, generally f + 1 + pub failure_threshold: NonZeroU64, + /// A list of valid signatures for certificate aggregation + pub sig_lists: Vec<::Signature>, + /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check + pub signers: BitVec, + /// Phantom data to ensure this struct is over a specific `VoteType` implementation + pub phantom: PhantomData, +} + +impl< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, + > Accumulator2 for QuorumVoteAccumulator +{ + fn append( + mut self, + vote: VOTE, + vote_node_id: usize, + stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either> { + let (VoteData::Yes(vote_commitment) | VoteData::No(vote_commitment)) = vote.get_data() + else { + return Either::Left(self); + }; + + let encoded_key = vote.get_key().to_bytes(); + + // Deserialize the signature so that it can be assembeld into a QC + // TODO ED Update this once we've gotten rid of EncodedSignature + let original_signature: ::Signature = + bincode_opts() + .deserialize(&vote.get_signature().0) + .expect("Deserialization on the signature shouldn't be able to fail."); + + let (total_stake_casted, total_vote_map) = self + .total_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + let (yes_stake_casted, yes_vote_map) = self + .yes_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + let (no_stake_casted, no_vote_map) = self + .no_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + // Check for duplicate vote + // TODO ED Re-encoding signature key to bytes until we get rid of EncodedKey + // Have to do this because SignatureKey is not hashable + if total_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + if self.signers.get(vote_node_id).as_deref() == Some(&true) { + error!("Node id is already in signers list"); + return Either::Left(self); + } + self.signers.set(vote_node_id, true); + self.sig_lists.push(original_signature); + + // TODO ED Make all these get calls as local variables to avoid constantly calling them + *total_stake_casted += u64::from(vote.get_vote_token().vote_count()); + total_vote_map.insert( + encoded_key.clone(), + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + + match vote.get_data() { + VoteData::Yes(_) => { + *yes_stake_casted += u64::from(vote.get_vote_token().vote_count()); + yes_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + } + VoteData::No(_) => { + *no_stake_casted += u64::from(vote.get_vote_token().vote_count()); + no_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + } + _ => return Either::Left(self), + } + + if *total_stake_casted >= u64::from(self.success_threshold) { + // Assemble QC + let real_qc_pp = ::get_public_parameter( + // TODO ED Something about stake table entries. Might be easier to just pass in membership? + stake_table_entries.clone(), + U256::from(self.success_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + + if *yes_stake_casted >= u64::from(self.success_threshold) { + self.yes_vote_outcomes.remove(&vote_commitment); + return Either::Right(AssembledSignature::Yes(real_qc_sig)); + } else if *no_stake_casted >= u64::from(self.failure_threshold) { + self.total_vote_outcomes.remove(&vote_commitment); + return Either::Right(AssembledSignature::No(real_qc_sig)); + } + } + Either::Left(self) + } +} + +/// Accumulates view sync votes +pub struct ViewSyncVoteAccumulator< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +> { + /// Map of all pre_commit signatures accumlated so far + pub pre_commit_vote_outcomes: VoteMap, + /// Map of all ommit signatures accumlated so far + pub commit_vote_outcomes: VoteMap, + /// Map of all finalize signatures accumlated so far + pub finalize_vote_outcomes: VoteMap, + + /// A quorum's worth of stake, generally 2f + 1 + pub success_threshold: NonZeroU64, + /// A quorum's failure threshold, generally f + 1 + pub failure_threshold: NonZeroU64, + /// A list of valid signatures for certificate aggregation + pub sig_lists: Vec<::Signature>, + /// A bitvec to indicate which node is active and send out a valid signature for certificate aggregation, this automatically do uniqueness check + pub signers: BitVec, + /// Phantom data since we want the accumulator to be attached to a single `VoteType` + pub phantom: PhantomData, +} + +impl< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, + > Accumulator2 for ViewSyncVoteAccumulator +{ + #[allow(clippy::too_many_lines)] + fn append( + mut self, + vote: VOTE, + vote_node_id: usize, + stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either> { + let (VoteData::ViewSyncPreCommit(vote_commitment) + | VoteData::ViewSyncCommit(vote_commitment) + | VoteData::ViewSyncFinalize(vote_commitment)) = vote.get_data() + else { + return Either::Left(self); + }; + + // error!("Vote is {:?}", vote.clone()); + + let encoded_key = vote.get_key().to_bytes(); + + // Deserialize the signature so that it can be assembeld into a QC + // TODO ED Update this once we've gotten rid of EncodedSignature + let original_signature: ::Signature = + bincode_opts() + .deserialize(&vote.get_signature().0) + .expect("Deserialization on the signature shouldn't be able to fail."); + + let (pre_commit_stake_casted, pre_commit_vote_map) = self + .pre_commit_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + // Check for duplicate vote + if pre_commit_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + let (commit_stake_casted, commit_vote_map) = self + .commit_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + if commit_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + let (finalize_stake_casted, finalize_vote_map) = self + .finalize_vote_outcomes + .entry(vote_commitment) + .or_insert_with(|| (0, BTreeMap::new())); + + if finalize_vote_map.contains_key(&encoded_key) { + return Either::Left(self); + } + + // update the active_keys and sig_lists + // TODO ED Possible bug where a node sends precommit vote and then commit vote after + // precommit cert is formed, their commit vote won't be counted because of this check + // Probably need separate signers vecs. + if self.signers.get(vote_node_id).as_deref() == Some(&true) { + error!("node id already in signers"); + return Either::Left(self); + } + self.signers.set(vote_node_id, true); + self.sig_lists.push(original_signature); + + match vote.get_data() { + VoteData::ViewSyncPreCommit(_) => { + *pre_commit_stake_casted += u64::from(vote.get_vote_token().vote_count()); + pre_commit_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + } + VoteData::ViewSyncCommit(_) => { + *commit_stake_casted += u64::from(vote.get_vote_token().vote_count()); + commit_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + } + VoteData::ViewSyncFinalize(_) => { + *finalize_stake_casted += u64::from(vote.get_vote_token().vote_count()); + finalize_vote_map.insert( + encoded_key, + (vote.get_signature(), vote.get_data(), vote.get_vote_token()), + ); + } + _ => unimplemented!(), + } + + if *pre_commit_stake_casted >= u64::from(self.failure_threshold) { + let real_qc_pp = ::get_public_parameter( + stake_table_entries, + U256::from(self.failure_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + + self.pre_commit_vote_outcomes + .remove(&vote_commitment) + .unwrap(); + return Either::Right(AssembledSignature::ViewSyncPreCommit(real_qc_sig)); + } + + if *commit_stake_casted >= u64::from(self.success_threshold) { + let real_qc_pp = ::get_public_parameter( + stake_table_entries.clone(), + U256::from(self.success_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + self.commit_vote_outcomes.remove(&vote_commitment).unwrap(); + return Either::Right(AssembledSignature::ViewSyncCommit(real_qc_sig)); + } + + if *finalize_stake_casted >= u64::from(self.success_threshold) { + let real_qc_pp = ::get_public_parameter( + stake_table_entries.clone(), + U256::from(self.success_threshold.get()), + ); + + let real_qc_sig = ::assemble( + &real_qc_pp, + self.signers.as_bitslice(), + &self.sig_lists[..], + ); + self.finalize_vote_outcomes + .remove(&vote_commitment) + .unwrap(); + return Either::Right(AssembledSignature::ViewSyncFinalize(real_qc_sig)); + } + + Either::Left(self) + } +} + +/// Placeholder accumulator; will be replaced by accumulator for each certificate type +pub struct AccumulatorPlaceholder< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, +> { + /// Phantom data to make compiler happy + pub phantom: PhantomData<(TYPES, VOTE, COMMITTABLE)>, +} + +impl< + TYPES: NodeType, + COMMITTABLE: Committable + Serialize + Clone, + VOTE: VoteType, + > Accumulator2 for AccumulatorPlaceholder +{ + fn append( + self, + _vote: VOTE, + _vote_node_id: usize, + _stake_table_entries: Vec<::StakeTableEntry>, + ) -> Either> { + either::Left(self) + } +} + /// Mapping of commitments to vote tokens by key. +// TODO ED Remove this whole token generic type VoteMap = HashMap< Commitment, ( diff --git a/crates/web_server/api.toml b/crates/web_server/api.toml index 5f749bcdb0..cc610fc9c9 100644 --- a/crates/web_server/api.toml +++ b/crates/web_server/api.toml @@ -11,6 +11,13 @@ DOC = """ Return the proposal for a given view number """ +# GET the proposal for a view, where the view is passed as an argument +[route.getrecentproposal] +PATH = ["proposal/"] +DOC = """ +Return the proposal for the most recent view the server has +""" + # POST a proposal, where the view is passed as an argument [route.postproposal] PATH = ["proposal/:view_number"] diff --git a/crates/web_server/src/config.rs b/crates/web_server/src/config.rs index 1da2781c55..f9d0e7c0c7 100644 --- a/crates/web_server/src/config.rs +++ b/crates/web_server/src/config.rs @@ -17,6 +17,10 @@ pub fn post_proposal_route(view_number: u64) -> String { format!("api/proposal/{view_number}") } +pub fn get_recent_proposal_route() -> String { + "api/proposal".to_string() +} + pub fn get_da_certificate_route(view_number: u64) -> String { format!("api/certificate/{view_number}") } @@ -33,6 +37,30 @@ pub fn post_vote_route(view_number: u64) -> String { format!("api/votes/{view_number}") } +pub fn get_vid_disperse_route(view_number: u64) -> String { + format!("api/vid/disperse/{view_number}") +} + +pub fn post_vid_disperse_route(view_number: u64) -> String { + format!("api/vid/disperse/{view_number}") +} + +pub fn get_vid_vote_route(view_number: u64, index: u64) -> String { + format!("api/vid/votes/{view_number}/{index}") +} + +pub fn post_vid_vote_route(view_number: u64) -> String { + format!("api/vid/votes/{view_number}") +} + +pub fn get_vid_cert_route(view_number: u64) -> String { + format!("api/vid/cert/{view_number}") +} + +pub fn post_vid_cert_route(view_number: u64) -> String { + format!("api/vid/cert/{view_number}") +} + pub fn get_transactions_route(index: u64) -> String { format!("api/transactions/{index}") } diff --git a/crates/web_server/src/lib.rs b/crates/web_server/src/lib.rs index ef7850cbb0..2bd746e1cd 100644 --- a/crates/web_server/src/lib.rs +++ b/crates/web_server/src/lib.rs @@ -33,6 +33,8 @@ struct WebServerState { da_certificates: HashMap)>, /// view for oldest proposals in memory oldest_proposal: u64, + /// view for the most recent proposal to help nodes catchup + recent_proposal: u64, /// view for teh oldest DA certificate oldest_certificate: u64, @@ -74,6 +76,7 @@ impl WebServerState { num_txns: 0, oldest_vote: 0, oldest_proposal: 0, + recent_proposal: 0, oldest_certificate: 0, shutdown: None, stake_table: Vec::new(), @@ -101,6 +104,7 @@ impl WebServerState { /// Trait defining methods needed for the `WebServerState` pub trait WebServerDataSource { fn get_proposal(&self, view_number: u64) -> Result>>, Error>; + fn get_recent_proposal(&self) -> Result>>, Error>; fn get_view_sync_proposal( &self, view_number: u64, @@ -156,6 +160,10 @@ impl WebServerDataSource for WebServerState { } } + fn get_recent_proposal(&self) -> Result>>, Error> { + self.get_proposal(self.recent_proposal) + } + fn get_view_sync_proposal( &self, view_number: u64, @@ -316,6 +324,10 @@ impl WebServerDataSource for WebServerState { fn post_proposal(&mut self, view_number: u64, mut proposal: Vec) -> Result<(), Error> { debug!("Received proposal for view {}", view_number); + if view_number > self.recent_proposal { + self.recent_proposal = view_number; + } + // Only keep proposal history for MAX_VIEWS number of view if self.proposals.len() >= MAX_VIEWS { self.proposals.remove(&self.oldest_proposal); @@ -494,6 +506,9 @@ where } .boxed() })? + .get("getrecentproposal", |_req, state| { + async move { state.get_recent_proposal() }.boxed() + })? .get("getviewsyncproposal", |req, state| { async move { let view_number: u64 = req.integer_param("view_number")?; diff --git a/docs/espresso-sequencer-paper.pdf b/docs/espresso-sequencer-paper.pdf index add7843500..6f0976f7c9 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 7b811ec412..ef4fb0daac 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1694240353, - "narHash": "sha256-UUtR7ff9iZMWhZHDoXSQGWjdmCGPMusTYw/94pARFT8=", + "lastModified": 1694845188, + "narHash": "sha256-M3Q1PDYcTOcqPRKkvRW+0sOm7dFGW95zK+wFmJYyw2M=", "owner": "nix-community", "repo": "fenix", - "rev": "94a5b9a4f8df7b2fa328044b8908b892a2733f60", + "rev": "95d6c30ecac01307772ec4273c649dab40211a9e", "type": "github" }, "original": { @@ -72,11 +72,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1694062546, - "narHash": "sha256-PiGI4f2BGnZcedP6slLjCLGLRLXPa9+ogGGgVPfGxys=", + "lastModified": 1694760568, + "narHash": "sha256-3G07BiXrp2YQKxdcdms22MUx6spc6A++MSePtatCYuI=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b200e0df08f80c32974a6108ce431d8a8a5e6547", + "rev": "46688f8eb5cd6f1298d873d4d2b9cf245e09e88e", "type": "github" }, "original": { @@ -99,11 +99,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1694196865, - "narHash": "sha256-OmL94alcXqzYlJuqWRlhpV0lvkO6HziuyPbtmM5C0ps=", + "lastModified": 1694682672, + "narHash": "sha256-AReT7Eis+iRs7ve6L1CeW/obR/QHi2xZpf7m8NX00hU=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "c405509f2e61cadaa8b18f340582e5c362356f2d", + "rev": "12e28c35758051dd6bc9cdf419a50dff80fab64d", "type": "github" }, "original": { @@ -133,11 +133,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1692799911, - "narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { diff --git a/justfile b/justfile index 9992d9057a..d0c4b9a5b2 100644 --- a/justfile +++ b/justfile @@ -16,14 +16,14 @@ run_ci: lint build test export RUSTDOCFLAGS='--cfg async_executor_impl="async-std" --cfg async_channel_impl="async-std" {{original_rustdocflags}}' RUSTFLAGS='--cfg async_executor_impl="async-std" --cfg async_channel_impl="async-std" {{original_rustflags}}' && just {{target}} {{ARGS}} build: - cargo build --verbose --profile=release-lto --workspace --examples --bins --tests --lib --benches + cargo build --verbose --workspace --examples --bins --tests --lib --benches example *ARGS: cargo run --profile=release-lto --example {{ARGS}} test: echo Testing - cargo test --verbose --profile=release-lto --lib --bins --tests --benches --workspace --no-fail-fast -- --test-threads=1 --nocapture + cargo test --verbose --lib --bins --tests --benches --workspace --no-fail-fast -- --test-threads=1 --nocapture test_basic: test_success test_with_failures test_network_task test_consensus_task test_da_task test_view_sync_task @@ -33,11 +33,15 @@ test_catchup: test_success: echo Testing success test - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_success -- --test-threads=1 --nocapture + ASYNC_STD_THREAD_COUNT=1 cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_success -- --test-threads=1 --nocapture + +test_timeout: + echo Testing timeout test + cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_timeout -- --test-threads=1 --nocapture --ignored test_web_server: echo Testing web server - cargo test --lib --bins --tests --benches --workspace --no-fail-fast web_server_network -- --test-threads=1 --nocapture + ASYNC_STD_THREAD_COUNT=1 cargo test --lib --bins --tests --benches --workspace --no-fail-fast web_server_network -- --test-threads=1 --nocapture test_with_failures: echo Testing nodes leaving the network with async std executor @@ -49,7 +53,7 @@ test_network_task: test_consensus_task: echo Testing with async std executor - cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_consensus -- --test-threads=1 --nocapture + ASYNC_STD_THREAD_COUNT=1 cargo test --lib --bins --tests --benches --workspace --no-fail-fast test_consensus -- --test-threads=1 --nocapture test_da_task: echo Testing the DA task with async std executor @@ -72,7 +76,7 @@ test_pkg_all pkg=test_pkg: cargo test --verbose --release --lib --bins --tests --benches --package={{pkg}} --no-fail-fast -- --test-threads=1 --nocapture list_tests_json package=test_pkg: - RUST_LOG=none cargo test --verbose --profile=release-lto --lib --bins --tests --benches --package={{package}} --no-fail-fast -- --test-threads=1 -Zunstable-options --format json + RUST_LOG=none cargo test --verbose --lib --bins --tests --benches --package={{package}} --no-fail-fast -- --test-threads=1 -Zunstable-options --format json list_examples package=test_pkg: cargo metadata | jq '.packages[] | select(.name == "{{package}}") | .targets[] | select(.kind == ["example"] ) | .name' @@ -98,7 +102,7 @@ fix: doc: echo Generating docs {{env_var('RUSTFLAGS')}} - cargo doc --no-deps --workspace --profile=release-lto --document-private-items --bins --examples --lib + cargo doc --no-deps --workspace --document-private-items --bins --examples --lib doc_test: echo Test docs diff --git a/testing-macros/src/lib.rs b/testing-macros/src/lib.rs index db68144a72..8057569da7 100644 --- a/testing-macros/src/lib.rs +++ b/testing-macros/src/lib.rs @@ -310,7 +310,7 @@ impl TestData { type Time = #time_type; type BlockType = <#demo_state as hotshot_types::traits::State>::BlockType; type SignatureKey = #signature_key_type; - type Transaction = <<#demo_state as hotshot_types::traits::State>::BlockType as hotshot_types::traits::Block>::Transaction; + type Transaction = <<#demo_state as hotshot_types::traits::State>::BlockType as hotshot_types::traits::BlockPayload>::Transaction; type StateType = #demo_state; type VoteTokenType = hotshot::traits::election::static_committee::StaticVoteToken; type ElectionConfigType = hotshot::traits::election::static_committee::StaticElectionConfig;