diff --git a/Cargo.lock b/Cargo.lock index cf421fe7e2..3819b7e396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -535,30 +535,14 @@ dependencies = [ "term", ] -[[package]] -name = "asn1-rs" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" -dependencies = [ - "asn1-rs-derive 0.4.0", - "asn1-rs-impl 0.1.0", - "displaydoc", - "nom", - "num-traits", - "rusticata-macros", - "thiserror", - "time 0.3.36", -] - [[package]] name = "asn1-rs" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" dependencies = [ - "asn1-rs-derive 0.5.0", - "asn1-rs-impl 0.2.0", + "asn1-rs-derive", + "asn1-rs-impl", "displaydoc", "nom", "num-traits", @@ -567,18 +551,6 @@ dependencies = [ "time 0.3.36", ] -[[package]] -name = "asn1-rs-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure 0.12.6", -] - [[package]] name = "asn1-rs-derive" version = "0.5.0" @@ -591,17 +563,6 @@ dependencies = [ "synstructure 0.13.1", ] -[[package]] -name = "asn1-rs-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "asn1-rs-impl" version = "0.2.0" @@ -674,7 +635,7 @@ dependencies = [ "async-std", "async-trait", "color-eyre", - "console-subscriber", + "console-subscriber 0.2.0", "flume 0.11.0", "futures", "tokio", @@ -859,9 +820,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329972aa325176e89114919f2a80fdae4f4c040f66a370b1a1159c6c0f94e7aa" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ "async-io 2.3.3", "async-lock 3.4.0", @@ -1392,6 +1353,7 @@ dependencies = [ "tracing", "url", "vbs", + "vec1", ] [[package]] @@ -1537,9 +1499,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -1549,12 +1511,12 @@ dependencies = [ [[package]] name = "cdn-broker" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.6#569d6a6ec3ccbbd41c041a4c80bd260bc5c928ff" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", "cdn-proto", "clap", - "console-subscriber", + "console-subscriber 0.3.0", "dashmap", "derivative", "jf-signature", @@ -1573,7 +1535,7 @@ dependencies = [ [[package]] name = "cdn-client" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.6#569d6a6ec3ccbbd41c041a4c80bd260bc5c928ff" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", "cdn-proto", @@ -1588,7 +1550,7 @@ dependencies = [ [[package]] name = "cdn-marshal" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.6#569d6a6ec3ccbbd41c041a4c80bd260bc5c928ff" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "async-std", "cdn-proto", @@ -1602,7 +1564,7 @@ dependencies = [ [[package]] name = "cdn-proto" version = "0.1.0" -source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.6#569d6a6ec3ccbbd41c041a4c80bd260bc5c928ff" +source = "git+https://github.com/EspressoSystems/Push-CDN?tag=0.3.12#7bf490b34fb5b996d7e98d3d4be6d88028a076ac" dependencies = [ "anyhow", "ark-serialize", @@ -1617,15 +1579,17 @@ dependencies = [ "num_enum", "pem 3.0.4", "prometheus", - "quinn 0.10.2", + "quinn", "rand 0.8.5", - "rcgen 0.12.1", + "rcgen 0.13.1", "redis", "rkyv", - "rustls 0.21.12", + "rustls 0.23.10", + "rustls-pki-types", "sqlx", "thiserror", "tokio", + "tokio-rustls 0.26.0", "tracing", "url", "warp", @@ -1698,9 +1662,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -1708,9 +1672,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -1720,9 +1684,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1732,9 +1696,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "cld" @@ -1897,7 +1861,20 @@ dependencies = [ "futures-core", "prost", "prost-types", - "tonic", + "tonic 0.10.2", + "tracing-core", +] + +[[package]] +name = "console-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257c22cd7e487dd4a13d413beabc512c5052f0bc048db0da6a84c3d8a6142fd" +dependencies = [ + "futures-core", + "prost", + "prost-types", + "tonic 0.11.0", "tracing-core", ] @@ -1907,19 +1884,44 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" dependencies = [ - "console-api", + "console-api 0.6.0", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost-types", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic 0.10.2", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.18", +] + +[[package]] +name = "console-subscriber" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c4cc54bae66f7d9188996404abdf7fdfa23034ef8e43478c8810828abad758" +dependencies = [ + "console-api 0.7.0", "crossbeam-channel", "crossbeam-utils", "futures-task", "hdrhistogram", "humantime", + "prost", "prost-types", "serde", "serde_json", "thread_local", "tokio", "tokio-stream", - "tonic", + "tonic 0.11.0", "tracing", "tracing-core", "tracing-subscriber 0.3.18", @@ -2421,27 +2423,13 @@ dependencies = [ "zeroize", ] -[[package]] -name = "der-parser" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" -dependencies = [ - "asn1-rs 0.5.2", - "displaydoc", - "nom", - "num-bigint", - "num-traits", - "rusticata-macros", -] - [[package]] name = "der-parser" version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" dependencies = [ - "asn1-rs 0.6.1", + "asn1-rs", "displaydoc", "nom", "num-bigint", @@ -3563,7 +3551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", ] @@ -4016,8 +4004,8 @@ dependencies = [ [[package]] name = "hotshot" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-broadcast", @@ -4063,7 +4051,7 @@ dependencies = [ [[package]] name = "hotshot-builder-api" version = "0.1.7" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "async-trait", "clap", @@ -4082,7 +4070,7 @@ dependencies = [ [[package]] name = "hotshot-builder-core" version = "0.1.26" -source = "git+https://github.com/EspressoSystems/hotshot-builder-core?tag=0.1.27#592ddf5f807a01835d40b3f1ea1f8484906ba7d1" +source = "git+https://github.com/EspressoSystems/hotshot-builder-core?tag=rc-0.1.28#ba3b264d1cfe534f11742038578520dac44d0f6d" dependencies = [ "anyhow", "async-broadcast", @@ -4134,8 +4122,8 @@ dependencies = [ [[package]] name = "hotshot-events-service" -version = "0.1.26" -source = "git+https://github.com/EspressoSystems/hotshot-events-service.git?tag=0.1.27#e4703477c687be50dea4faa4235aafa67a068cd4" +version = "0.1.27" +source = "git+https://github.com/EspressoSystems/hotshot-events-service.git?tag=rc-0.1.28#7bec1bd3740418914f6c07f237b4b22adab3fe0e" dependencies = [ "async-broadcast", "async-compatibility-layer", @@ -4159,8 +4147,8 @@ dependencies = [ [[package]] name = "hotshot-example-types" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-broadcast", @@ -4190,8 +4178,8 @@ dependencies = [ [[package]] name = "hotshot-macros" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "derive_builder", "proc-macro2", @@ -4201,8 +4189,8 @@ dependencies = [ [[package]] name = "hotshot-orchestrator" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-compatibility-layer", @@ -4226,12 +4214,13 @@ dependencies = [ "toml", "tracing", "vbs", + "vec1", ] [[package]] name = "hotshot-query-service" version = "0.1.39" -source = "git+https://github.com/EspressoSystems/hotshot-query-service?tag=0.1.39#b4fe8bfa9e2e3fd9cd98c5f91c614211bf60c530" +source = "git+https://github.com/EspressoSystems/hotshot-query-service?tag=rc-0.1.40#38b7c5f8e27d76768024990ca9109988468699e2" dependencies = [ "anyhow", "ark-serialize", @@ -4280,12 +4269,13 @@ dependencies = [ "trait-variant", "typenum", "vbs", + "vec1", ] [[package]] name = "hotshot-stake-table" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "ark-bn254", "ark-ed-on-bn254", @@ -4356,8 +4346,8 @@ dependencies = [ [[package]] name = "hotshot-task" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-broadcast", @@ -4371,8 +4361,8 @@ dependencies = [ [[package]] name = "hotshot-task-impls" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-broadcast", @@ -4406,8 +4396,8 @@ dependencies = [ [[package]] name = "hotshot-testing" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-broadcast", @@ -4443,12 +4433,13 @@ dependencies = [ "tokio", "tracing", "vbs", + "vec1", ] [[package]] name = "hotshot-types" version = "0.1.11" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "ark-bls12-381", @@ -4496,6 +4487,7 @@ dependencies = [ "typenum", "url", "vbs", + "vec1", ] [[package]] @@ -4543,12 +4535,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes 1.6.0", - "futures-core", + "futures-util", "http 1.1.0", "http-body 1.0.0", "pin-project-lite 0.2.14", @@ -4592,9 +4584,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -4663,7 +4655,7 @@ dependencies = [ "hyper 0.14.29", "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -4737,6 +4729,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f8ac670d7422d7f76b32e17a5db556510825b29ec9154f235977c9caba61036" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -4755,12 +4865,14 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "4716a3a0933a1d01c2f72450e89596eb51dd34ef3c211ccd875acdf1f8fe47ed" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", + "smallvec", + "utf8_iter", ] [[package]] @@ -5354,7 +5466,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "string_cache", "term", "tiny-keccak", @@ -5368,7 +5480,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.6", + "regex-automata 0.4.7", ] [[package]] @@ -5616,9 +5728,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" dependencies = [ "asn1_der", "bs58", @@ -5710,14 +5822,15 @@ dependencies = [ [[package]] name = "libp2p-networking" -version = "0.5.56" -source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.57#828f71864bec2600a2740e24c86468c5a485585b" +version = "0.5.57" +source = "git+https://github.com/EspressoSystems/hotshot?tag=rc-0.5.58#9b642d35e88db3083dbbe1ab6e2aa8bd32ac68ea" dependencies = [ "anyhow", "async-compatibility-layer", "async-lock 2.8.0", "async-std", "async-trait", + "bincode", "blake3", "custom_debug 0.5.1", "derive_builder", @@ -5736,7 +5849,6 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "vbs", "void", ] @@ -5829,10 +5941,10 @@ dependencies = [ "libp2p-identity", "libp2p-tls", "parking_lot", - "quinn 0.11.1", + "quinn", "rand 0.8.5", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.10", "socket2 0.5.7", "thiserror", "tokio", @@ -5977,10 +6089,10 @@ dependencies = [ "libp2p-identity", "rcgen 0.11.3", "ring 0.17.8", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-webpki 0.101.7", "thiserror", - "x509-parser 0.16.0", + "x509-parser", "yasna", ] @@ -6043,7 +6155,7 @@ dependencies = [ "thiserror", "tracing", "yamux 0.12.1", - "yamux 0.13.2", + "yamux 0.13.3", ] [[package]] @@ -6145,6 +6257,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + [[package]] name = "local-ip-address" version = "0.6.1" @@ -6270,9 +6388,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoize" @@ -6575,9 +6693,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -6616,9 +6734,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -6651,11 +6769,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -6720,22 +6837,13 @@ dependencies = [ "memchr", ] -[[package]] -name = "oid-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" -dependencies = [ - "asn1-rs 0.5.2", -] - [[package]] name = "oid-registry" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" dependencies = [ - "asn1-rs 0.6.1", + "asn1-rs", ] [[package]] @@ -6897,7 +7005,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] @@ -7460,7 +7568,7 @@ dependencies = [ "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "unarray", ] @@ -7481,7 +7589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.66", @@ -7565,36 +7673,19 @@ dependencies = [ [[package]] name = "quinn" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" -dependencies = [ - "bytes 1.6.0", - "pin-project-lite 0.2.14", - "quinn-proto 0.10.6", - "quinn-udp 0.4.1", - "rustc-hash", - "rustls 0.21.12", - "thiserror", - "tokio", - "tracing", -] - -[[package]] -name = "quinn" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904e3d3ba178131798c6d9375db2b13b34337d489b089fc5ba0825a2ff1bee73" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ "async-io 2.3.3", "async-std", "bytes 1.6.0", "futures-io", "pin-project-lite 0.2.14", - "quinn-proto 0.11.2", - "quinn-udp 0.5.1", + "quinn-proto", + "quinn-udp", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.10", "thiserror", "tokio", "tracing", @@ -7602,32 +7693,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" -dependencies = [ - "bytes 1.6.0", - "rand 0.8.5", - "ring 0.16.20", - "rustc-hash", - "rustls 0.21.12", - "slab", - "thiserror", - "tinyvec", - "tracing", -] - -[[package]] -name = "quinn-proto" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e974563a4b1c2206bbc61191ca4da9c22e4308b4c455e8906751cc7828393f08" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ "bytes 1.6.0", "rand 0.8.5", "ring 0.17.8", "rustc-hash", - "rustls 0.23.9", + "rustls 0.23.10", "slab", "thiserror", "tinyvec", @@ -7636,22 +7710,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" -dependencies = [ - "bytes 1.6.0", - "libc", - "socket2 0.5.7", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "quinn-udp" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4f0def2590301f4f667db5a77f9694fb004f82796dc1a8b1508fafa3d0e8b72" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" dependencies = [ "libc", "once_cell", @@ -7799,22 +7860,23 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" +checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" dependencies = [ "pem 3.0.4", "ring 0.17.8", + "rustls-pki-types", "time 0.3.36", - "x509-parser 0.15.1", + "x509-parser", "yasna", ] [[package]] name = "redis" -version = "0.24.0" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c580d9cbbe1d1b479e8d67cf9daf6a62c957e6846048408b80b43ac3f6af84cd" +checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" dependencies = [ "arc-swap", "async-trait", @@ -7843,9 +7905,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -7908,14 +7970,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -7929,13 +7991,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -7946,9 +8008,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rend" @@ -7990,7 +8052,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -8339,9 +8401,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "once_cell", "ring 0.17.8", @@ -8650,6 +8712,7 @@ dependencies = [ "jf-vid", "libp2p", "num-traits", + "num_enum", "paste", "portpicker", "pretty_assertions", @@ -8662,6 +8725,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "snafu 0.8.3", + "static_assertions", "strum", "surf-disco", "tagged-base64", @@ -8677,6 +8741,7 @@ dependencies = [ "typenum", "url", "vbs", + "vec1", "vergen", "zeroize", ] @@ -9206,11 +9271,10 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools 0.12.1", "nom", "unicode_categories", ] @@ -9413,6 +9477,12 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "standback" version = "0.2.17" @@ -10013,6 +10083,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -10126,6 +10206,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.10", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -10147,7 +10238,7 @@ dependencies = [ "log", "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tungstenite 0.20.1", "webpki-roots 0.25.4", ] @@ -10207,7 +10298,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.12", + "winnow 0.6.13", ] [[package]] @@ -10237,6 +10328,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes 1.6.0", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.29", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -10611,12 +10729,12 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "f7c25da092f0a868cdf09e8674cd3b7ef3a7d92a24253e663a2fb85e2496de56" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.0", "percent-encoding", "serde", ] @@ -10633,11 +10751,23 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -10716,6 +10846,15 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vec1" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322" +dependencies = [ + "serde", +] + [[package]] name = "vergen" version = "8.3.1" @@ -11158,9 +11297,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ff33f391015ecab21cd092389215eb265ef9496a9a07b6bee7d3529831deda" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -11185,6 +11324,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -11225,36 +11376,19 @@ dependencies = [ "zeroize", ] -[[package]] -name = "x509-parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" -dependencies = [ - "asn1-rs 0.5.2", - "data-encoding", - "der-parser 8.2.0", - "lazy_static", - "nom", - "oid-registry 0.6.1", - "ring 0.16.20", - "rusticata-macros", - "thiserror", - "time 0.3.36", -] - [[package]] name = "x509-parser" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" dependencies = [ - "asn1-rs 0.6.1", + "asn1-rs", "data-encoding", - "der-parser 9.0.0", + "der-parser", "lazy_static", "nom", - "oid-registry 0.7.0", + "oid-registry", + "ring 0.17.8", "rusticata-macros", "thiserror", "time 0.3.36", @@ -11301,18 +11435,18 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f97202f6b125031b95d83e01dc57292b529384f80bfae4677e4bbc10178cf72" +checksum = "a31b5e376a8b012bee9c423acdbb835fc34d45001cfa3106236a624e4b738028" dependencies = [ "futures", - "instant", "log", "nohash-hasher", "parking_lot", "pin-project", "rand 0.8.5", "static_assertions", + "web-time", ] [[package]] @@ -11330,6 +11464,30 @@ dependencies = [ "time 0.3.36", ] +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure 0.13.1", +] + [[package]] name = "zerocopy" version = "0.7.34" @@ -11350,6 +11508,27 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", + "synstructure 0.13.1", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -11370,6 +11549,28 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "zerovec" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2cc8827d6c0994478a15c53f374f46fbd41bea663d809b14744bc42e6b109c" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97cf56601ee5052b4417d90c8755c6683473c926039908196cf35d99f893ebe7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index ffc603c2c3..ff58fdbc5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,29 +47,29 @@ dotenvy = "0.15" ethers = { version = "2.0", features = ["solc"] } futures = "0.3" -hotshot = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } +hotshot = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } # Hotshot imports -hotshot-builder-api = { git = "https://github.com/EspressoSystems/HotShot.git", tag = "rc-0.5.57" } -hotshot-builder-core = { git = "https://github.com/EspressoSystems/hotshot-builder-core", tag = "0.1.27" } -hotshot-events-service = { git = "https://github.com/EspressoSystems/hotshot-events-service.git", tag = "0.1.27" } -hotshot-orchestrator = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } -hotshot-query-service = { git = "https://github.com/EspressoSystems/hotshot-query-service", tag = "0.1.39" } -hotshot-stake-table = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } +hotshot-builder-api = { git = "https://github.com/EspressoSystems/HotShot.git", tag = "rc-0.5.58" } +hotshot-builder-core = { git = "https://github.com/EspressoSystems/hotshot-builder-core", tag = "rc-0.1.28" } +hotshot-events-service = { git = "https://github.com/EspressoSystems/hotshot-events-service.git", tag = "rc-0.1.28" } +hotshot-orchestrator = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } +hotshot-query-service = { git = "https://github.com/EspressoSystems/hotshot-query-service", tag = "rc-0.1.40" } +hotshot-stake-table = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } hotshot-state-prover = { version = "0.1.0", path = "hotshot-state-prover" } -hotshot-task = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } -hotshot-testing = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } -hotshot-types = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.57" } +hotshot-task = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } +hotshot-testing = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } +hotshot-types = { git = "https://github.com/EspressoSystems/hotshot", tag = "rc-0.5.58" } hotshot-contract-adapter = { version = "0.1.0", path = "contracts/rust/adapter" } # Push CDN imports cdn-broker = { git = "https://github.com/EspressoSystems/Push-CDN", features = [ "runtime-async-std", "global-permits", -], tag = "0.3.6", package = "cdn-broker" } +], tag = "0.3.12", package = "cdn-broker" } cdn-marshal = { git = "https://github.com/EspressoSystems/Push-CDN", features = [ "runtime-async-std", "global-permits", -], tag = "0.3.6", package = "cdn-marshal" } +], tag = "0.3.12", package = "cdn-marshal" } jf-plonk = { git = "https://github.com/EspressoSystems/jellyfish", tag = "0.4.5", features = [ "test-apis", @@ -115,6 +115,7 @@ serde = { version = "1.0.195", features = ["derive"] } toml = "0.8" url = "2.3" vbs = "0.1" +vec1 = "1.12" vergen = { version = "8.3", features = ["git", "gitcl"] } zeroize = "1.7" committable = "0.2" diff --git a/builder/Cargo.toml b/builder/Cargo.toml index f9a9f3f89d..d3036a0468 100644 --- a/builder/Cargo.toml +++ b/builder/Cargo.toml @@ -45,6 +45,7 @@ tide-disco = { workspace = true } tracing = { workspace = true } url = { workspace = true } vbs = { workspace = true } +vec1 = { workspace = true } [dev-dependencies] sequencer = { path = "../sequencer", features = ["testing"] } diff --git a/builder/src/bin/permissionless-builder.rs b/builder/src/bin/permissionless-builder.rs index a506b8c480..886a2108b1 100644 --- a/builder/src/bin/permissionless-builder.rs +++ b/builder/src/bin/permissionless-builder.rs @@ -122,13 +122,14 @@ async fn main() -> anyhow::Result<()> { ) .unwrap(); + let validated_state = ValidatedState::genesis(&instance_state).0; + let api_response_timeout_duration = opt.max_api_timeout_duration; // make the txn timeout as 1/4 of the api_response_timeout_duration let txn_timeout_duration = api_response_timeout_duration / 4; let buffer_view_num_count = opt.buffer_view_num_count; - let validated_state = ValidatedState::genesis(&instance_state).0; let _builder_config = BuilderConfig::init( builder_key_pair, @@ -136,12 +137,12 @@ async fn main() -> anyhow::Result<()> { opt.channel_capacity, opt.node_count, instance_state, + validated_state, opt.hotshot_event_streaming_url, builder_server_url, api_response_timeout_duration, buffer_view_num_count, txn_timeout_duration, - validated_state, ) .await; diff --git a/builder/src/lib.rs b/builder/src/lib.rs index 5e846587fa..883a66e74b 100644 --- a/builder/src/lib.rs +++ b/builder/src/lib.rs @@ -9,9 +9,7 @@ use futures::{ stream::{Stream, StreamExt}, }; use hotshot::{ - traits::{ - election::static_committee::GeneralStaticCommittee, implementations::NetworkingMetricsValue, - }, + traits::election::static_committee::GeneralStaticCommittee, types::{SignatureKey, SystemContextHandle}, HotShotInitializer, Memberships, Networks, SystemContext, }; @@ -21,12 +19,11 @@ use hotshot_orchestrator::{ }; use hotshot_types::{ consensus::ConsensusMetricsValue, - constants::{Version01, STATIC_VER_0_1}, + constants::Base, event::Event, light_client::StateKeyPair, signature_key::{BLSPrivKey, BLSPubKey}, - traits::election::Membership, - traits::metrics::Metrics, + traits::{election::Membership, metrics::Metrics}, HotShotConfig, PeerConfig, ValidatorConfig, }; use std::fmt::Display; @@ -68,20 +65,18 @@ pub mod permissioned; // It runs the api service for the builder pub fn run_builder_api_service(url: Url, source: ProxyGlobalState) { // it is to serve hotshot - let builder_api = hotshot_builder_api::builder::define_api::< - ProxyGlobalState, - SeqTypes, - Version01, - >(&HotshotBuilderApiOptions::default()) - .expect("Failed to construct the builder APIs"); + let builder_api = + hotshot_builder_api::builder::define_api::, SeqTypes, Base>( + &HotshotBuilderApiOptions::default(), + ) + .expect("Failed to construct the builder APIs"); // it enables external clients to submit txn to the builder's private mempool - let private_mempool_api = hotshot_builder_api::builder::submit_api::< - ProxyGlobalState, - SeqTypes, - Version01, - >(&HotshotBuilderApiOptions::default()) - .expect("Failed to construct the builder API for private mempool txns"); + let private_mempool_api = + hotshot_builder_api::builder::submit_api::, SeqTypes, Base>( + &HotshotBuilderApiOptions::default(), + ) + .expect("Failed to construct the builder API for private mempool txns"); let mut app: App, BuilderApiError> = App::with_state(source); @@ -91,7 +86,7 @@ pub fn run_builder_api_service(url: Url, source: ProxyGlobalState) { app.register_module("txn_submit", private_mempool_api) .expect("Failed to register the private mempool API"); - async_spawn(app.serve(url, STATIC_VER_0_1)); + async_spawn(app.serve(url, Base::instance())); } #[cfg(test)] @@ -121,6 +116,7 @@ pub mod testing { ExecutionType, HotShotConfig, PeerConfig, ValidatorConfig, }; use portpicker::pick_unused_port; + use vbs::version::StaticVersion; //use sequencer::persistence::NoStorage; use async_broadcast::{ broadcast, Receiver as BroadcastReceiver, RecvError, Sender as BroadcastSender, @@ -160,7 +156,6 @@ pub mod testing { events::{Error as EventStreamApiError, Options as EventStreamingApiOptions}, events_source::{EventConsumer, EventsStreamer}, }; - use hotshot_types::constants::{Version01, STATIC_VER_0_1}; use serde::{Deserialize, Serialize}; use snafu::{guide::feature_flags, *}; @@ -172,7 +167,7 @@ pub mod testing { staking_nodes_state_key_pairs: Vec, non_staking_nodes_state_key_pairs: Vec, non_staking_nodes_stake_entries: Vec>, - master_map: Arc, PubKey>>, + master_map: Arc>, anvil: Arc, } @@ -220,12 +215,16 @@ pub mod testing { data_request_delay: Duration::from_millis(200), view_sync_timeout: Duration::from_secs(5), fixed_leader_for_gpuvid: 0, - builder_url, + builder_urls: vec1::vec1![builder_url], builder_timeout: Duration::from_secs(1), start_threshold: ( known_nodes_with_stake.clone().len() as u64, known_nodes_with_stake.clone().len() as u64, ), + start_proposing_view: 0, + stop_proposing_view: 0, + start_voting_view: 0, + stop_voting_view: 0, }; Self { @@ -378,8 +377,7 @@ pub mod testing { let network = Arc::new(MemoryNetwork::new( config.my_own_validator_config.public_key, - NetworkingMetricsValue::new(metrics), - self.master_map.clone(), + &self.master_map, None, )); let networks = Networks { @@ -436,7 +434,7 @@ pub mod testing { let hotshot_events_api = hotshot_events_service::events::define_api::< Arc>>, SeqTypes, - Version01, + Base, >(&EventStreamingApiOptions::default()) .expect("Failed to define hotshot eventsAPI"); @@ -445,7 +443,7 @@ pub mod testing { app.register_module("hotshot-events", hotshot_events_api) .expect("Failed to register hotshot events API"); - async_spawn(app.serve(url, STATIC_VER_0_1)); + async_spawn(app.serve(url, Base::instance())); } // enable hotshot event streaming pub fn enable_hotshot_node_event_streaming( @@ -555,12 +553,12 @@ pub mod testing { channel_capacity, node_count, node_state, + ValidatedState::default(), hotshot_events_streaming_api_url, hotshot_builder_api_url, Duration::from_millis(2000), 15, Duration::from_millis(500), - ValidatedState::default(), ) .await .unwrap(); @@ -620,11 +618,11 @@ pub mod testing { bootstrapped_view, channel_capacity, node_state, + ValidatedState::default(), hotshot_builder_api_url, Duration::from_millis(2000), 15, Duration::from_millis(500), - ValidatedState::default(), ) .await .unwrap(); @@ -703,10 +701,11 @@ mod test { } let genesis_state = NodeState::mock(); + let validated_state = ValidatedState::default(); let mut parent = { // TODO refactor repeated code from other tests let (genesis_payload, genesis_ns_table) = - Payload::from_transactions([], &ValidatedState::default(), &genesis_state) + Payload::from_transactions([], &validated_state, &genesis_state) .await .expect("unable to create genesis payload"); let builder_commitment = genesis_payload.builder_commitment(&genesis_ns_table); diff --git a/builder/src/non_permissioned.rs b/builder/src/non_permissioned.rs index 911f6a69c5..da28c3548a 100644 --- a/builder/src/non_permissioned.rs +++ b/builder/src/non_permissioned.rs @@ -25,7 +25,6 @@ use hotshot_builder_core::{ }; use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, data::{fake_commitment, Leaf, ViewNumber}, traits::{ block_contents::{vid_commitment, GENESIS_VID_NUM_STORAGE_NODES}, @@ -82,12 +81,12 @@ impl BuilderConfig { channel_capacity: NonZeroUsize, node_count: NonZeroUsize, instance_state: NodeState, + validated_state: ValidatedState, hotshot_events_api_url: Url, hotshot_builder_apis_url: Url, max_api_timeout_duration: Duration, buffered_view_num_count: usize, maximize_txns_count_timeout_duration: Duration, - validated_state: ValidatedState, ) -> anyhow::Result { tracing::info!( address = %builder_key_pair.fee_account(), @@ -161,8 +160,7 @@ impl BuilderConfig { .base_fee .as_u64() .context("the base fee exceeds the maximum amount that a builder can pay (defined by u64::MAX)")?, - Arc::new(instance_state), - Duration::from_secs(60), + Arc::new(instance_state), Duration::from_secs(60), Arc::new(validated_state), ); @@ -232,12 +230,10 @@ mod test { events::{Error as EventStreamApiError, Options as EventStreamingApiOptions}, events_source::{BuilderEvent, EventConsumer, EventsStreamer}, }; - use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, - traits::{ - block_contents::{BlockPayload, GENESIS_VID_NUM_STORAGE_NODES}, - node_implementation::NodeType, - }, + use hotshot_types::constants::Base; + use hotshot_types::traits::{ + block_contents::{BlockPayload, GENESIS_VID_NUM_STORAGE_NODES}, + node_implementation::NodeType, }; use hotshot_types::{signature_key::BLSPubKey, traits::signature_key::SignatureKey}; use sequencer::{ @@ -294,7 +290,7 @@ mod test { ); // builder api url - let hotshot_builder_api_url = hotshot_config.config.builder_url.clone(); + let hotshot_builder_api_url = hotshot_config.config.builder_urls[0].clone(); let builder_config = NonPermissionedBuilderTestConfig::init_non_permissioned_builder( &hotshot_config, @@ -306,7 +302,7 @@ mod test { let builder_pub_key = builder_config.fee_account; // Start a builder api client - let builder_client = Client::::new( + let builder_client = Client::::new( hotshot_builder_api_url.clone(), ); assert!(builder_client.connect(Some(Duration::from_secs(60))).await); diff --git a/builder/src/permissioned.rs b/builder/src/permissioned.rs index d62049e431..93a5d60731 100644 --- a/builder/src/permissioned.rs +++ b/builder/src/permissioned.rs @@ -12,8 +12,8 @@ use hotshot::{ traits::{ election::static_committee::GeneralStaticCommittee, implementations::{ - derive_libp2p_peer_id, CombinedNetworks, KeyPair, Libp2pNetwork, - NetworkingMetricsValue, PushCdnNetwork, Topic, WrappedSignatureKey, + derive_libp2p_peer_id, CdnMetricsValue, CombinedNetworks, KeyPair, Libp2pNetwork, + PushCdnNetwork, Topic, WrappedSignatureKey, }, }, types::{SignatureKey, SystemContextHandle}, @@ -86,7 +86,6 @@ use tide_disco::{app, method::ReadState, App, Url}; use vbs::version::StaticVersionType; use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, data::{fake_commitment, Leaf, ViewNumber}, traits::{ block_contents::{vid_commitment, GENESIS_VID_NUM_STORAGE_NODES}, @@ -210,6 +209,7 @@ pub async fn init_node::from_urls(network_params.state_peers)), node_id: node_index, + upgrades: Default::default(), + current_version: Ver::VERSION, }; let stake_table_commit = @@ -309,11 +309,11 @@ pub async fn init_node anyhow::Result { // tx channel let (tx_sender, tx_receiver) = broadcast::>(channel_capacity.get()); @@ -557,8 +557,8 @@ mod test { events::{Error as EventStreamApiError, Options as EventStreamingApiOptions}, events_source::{BuilderEvent, EventConsumer, EventsStreamer}, }; + use hotshot_types::constants::Base; use hotshot_types::{ - constants::{Version01, STATIC_VER_0_1}, signature_key::BLSPubKey, traits::{ block_contents::{BlockPayload, GENESIS_VID_NUM_STORAGE_NODES}, @@ -600,7 +600,7 @@ mod test { let state_signer = handles[node_id].1.take().unwrap(); // builder api url - let hotshot_builder_api_url = hotshot_config.config.builder_url.clone(); + let hotshot_builder_api_url = hotshot_config.config.builder_urls[0].clone(); let builder_config = PermissionedBuilderTestConfig::init_permissioned_builder( hotshot_config, hotshot_context_handle, @@ -613,7 +613,7 @@ mod test { let builder_pub_key = builder_config.fee_account; // Start a builder api client - let builder_client = Client::::new( + let builder_client = Client::::new( hotshot_builder_api_url.clone(), ); assert!(builder_client.connect(Some(Duration::from_secs(60))).await); diff --git a/data/genesis/demo.toml b/data/genesis/demo.toml index 1f70a67860..5c52dc3861 100644 --- a/data/genesis/demo.toml +++ b/data/genesis/demo.toml @@ -10,3 +10,16 @@ fee_contract = '0xa15bb66138824a1c7167f5e85b957d04dd34e468' [header] timestamp = "1970-01-01T00:00:00Z" + +[[upgrade]] +version = "0.2" +view = 5 +propose_window = 10 + + +[upgrade.chain_config] +chain_id = 999999999 +base_fee = '2 wei' +max_block_size = '1mb' +fee_recipient = '0x0000000000000000000000000000000000000000' +fee_contract = '0xa15bb66138824a1c7167f5e85b957d04dd34e468' diff --git a/docker-compose.yaml b/docker-compose.yaml index a0abc0bc93..7d1c4fcdc8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -71,7 +71,7 @@ services: ports: - "$ESPRESSO_ORCHESTRATOR_PORT:$ESPRESSO_ORCHESTRATOR_PORT" environment: - - ESPRESSO_ORCHESTRATOR_BUILDER_URL=http://permissionless-builder:$ESPRESSO_BUILDER_SERVER_PORT + - ESPRESSO_ORCHESTRATOR_BUILDER_URLS=http://permissionless-builder:$ESPRESSO_BUILDER_SERVER_PORT - ESPRESSO_ORCHESTRATOR_PORT - ESPRESSO_ORCHESTRATOR_NUM_NODES - ESPRESSO_ORCHESTRATOR_START_DELAY diff --git a/hotshot-state-prover/src/mock_ledger.rs b/hotshot-state-prover/src/mock_ledger.rs index 0e9ec11904..0b7b061f76 100644 --- a/hotshot-state-prover/src/mock_ledger.rs +++ b/hotshot-state-prover/src/mock_ledger.rs @@ -477,23 +477,19 @@ pub fn gen_plonk_proof_for_test( let mut proofs = vec![]; let mut extra_msgs = vec![]; - circuits - .iter() - .zip(prove_keys.iter()) - .enumerate() - .for_each(|(_, (cs, pk))| { - let extra_msg = Some(vec![]); // We set extra_msg="" for the contract tests to pass - proofs.push( - PlonkKzgSnark::::prove::<_, _, SolidityTranscript>( - rng, - cs, - pk, - extra_msg.clone(), - ) - .unwrap(), - ); - extra_msgs.push(extra_msg); - }); + circuits.iter().zip(prove_keys.iter()).for_each(|(cs, pk)| { + let extra_msg = Some(vec![]); // We set extra_msg="" for the contract tests to pass + proofs.push( + PlonkKzgSnark::::prove::<_, _, SolidityTranscript>( + rng, + cs, + pk, + extra_msg.clone(), + ) + .unwrap(), + ); + extra_msgs.push(extra_msg); + }); let public_inputs: Vec> = circuits .iter() diff --git a/process-compose.yaml b/process-compose.yaml index cdb0e9a80f..67796668b2 100644 --- a/process-compose.yaml +++ b/process-compose.yaml @@ -64,7 +64,7 @@ processes: orchestrator: command: orchestrator environment: - - ESPRESSO_ORCHESTRATOR_BUILDER_URL=http://localhost:$ESPRESSO_BUILDER_SERVER_PORT + - ESPRESSO_ORCHESTRATOR_BUILDER_URLS=http://localhost:$ESPRESSO_BUILDER_SERVER_PORT readiness_probe: http_get: scheme: http diff --git a/sequencer/Cargo.toml b/sequencer/Cargo.toml index 40a7a2e693..77a03fe9d6 100644 --- a/sequencer/Cargo.toml +++ b/sequencer/Cargo.toml @@ -86,6 +86,7 @@ jf-utils = { workspace = true } # TODO temporary: used only for test_rng() jf-vid = { workspace = true } libp2p = { workspace = true } num-traits = "0.2.18" +num_enum = "0.7" portpicker = { workspace = true } rand = "0.8.5" rand_chacha = { workspace = true } @@ -95,6 +96,7 @@ serde = { workspace = true } serde_json = "^1.0.113" sha2 = "0.10" # TODO temporary, used only for VID, should be set in hotshot snafu = { workspace = true } +static_assertions = "1" strum = { workspace = true } surf-disco = { workspace = true } tagged-base64 = { workspace = true } @@ -113,6 +115,7 @@ typenum = { version = "1.15.0", default-features = false, features = [ ] } url = { workspace = true } vbs = { workspace = true } +vec1 = { workspace = true } zeroize = { workspace = true } [package.metadata.cargo-udeps.ignore] diff --git a/sequencer/api/catchup.toml b/sequencer/api/catchup.toml index 416d034dd5..af7d942bae 100644 --- a/sequencer/api/catchup.toml +++ b/sequencer/api/catchup.toml @@ -44,3 +44,15 @@ decided view. Returns the blocks Merkle tree frontier -- the path to the most recently appended leaf, relative to root node at the requested block height and view. """ + +[route.chainconfig] +PATH = ["/chain-config/:commitment"] +":commitment" = "TaggedBase64" +DOC = """ + +This endpoint retrieves the chain config from a peer that matches the specified `:commitment`. +This is only called when the state does not have full chain config which is different from the genesis one. +This can happen if the node missed a protocol upgrade. + +Returns the chain config -- this includes parameters such as `max_block_size`, `chain_id`, `base_fee`, and `fee_recipient`. +""" \ No newline at end of file diff --git a/sequencer/api/migrations/V33__chain_config_table.sql b/sequencer/api/migrations/V33__chain_config_table.sql new file mode 100644 index 0000000000..f41825ac0a --- /dev/null +++ b/sequencer/api/migrations/V33__chain_config_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE chain_config ( + commitment VARCHAR PRIMARY KEY, + data BYTEA NOT NULL +); diff --git a/sequencer/api/public-env-vars.toml b/sequencer/api/public-env-vars.toml index 34be7819b4..88f37c8b06 100644 --- a/sequencer/api/public-env-vars.toml +++ b/sequencer/api/public-env-vars.toml @@ -42,7 +42,7 @@ variables = [ "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_NAMESPACE", "ESPRESSO_NASTY_CLIENT_WEIGHT_QUERY_WINDOW", "ESPRESSO_ORCHESTRATOR_BUILDER_TIMEOUT", - "ESPRESSO_ORCHESTRATOR_BUILDER_URL", + "ESPRESSO_ORCHESTRATOR_BUILDER_URLS", "ESPRESSO_ORCHESTRATOR_LIBP2P_MESH_N", "ESPRESSO_ORCHESTRATOR_NEXT_VIEW_TIMEOUT", "ESPRESSO_ORCHESTRATOR_NUM_NODES", diff --git a/sequencer/src/api.rs b/sequencer/src/api.rs index 5ac1925787..abba9131fb 100644 --- a/sequencer/src/api.rs +++ b/sequencer/src/api.rs @@ -1,15 +1,16 @@ use self::data_source::{HotShotConfigDataSource, PublicHotShotConfig, StateSignatureDataSource}; use crate::{ network, - persistence::SequencerPersistence, + persistence::{ChainConfigPersistence, SequencerPersistence}, state::{BlockMerkleTree, FeeAccountProof}, state_signature::StateSigner, - Node, NodeState, PubKey, SeqTypes, SequencerContext, Transaction, + ChainConfig, Node, NodeState, PubKey, SeqTypes, SequencerContext, Transaction, }; -use anyhow::Context; +use anyhow::{bail, Context}; use async_once_cell::Lazy; use async_std::sync::{Arc, RwLock}; use async_trait::async_trait; +use committable::Commitment; use data_source::{CatchupDataSource, SubmitDataSource}; use derivative::Derivative; use ethers::prelude::{Address, U256}; @@ -217,6 +218,41 @@ impl< // Try storage. self.inner().get_frontier(height, view).await } + + async fn get_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + // Check if we have the desired state in memory. + match self.as_ref().get_chain_config(commitment).await { + Ok(cf) => return Ok(cf), + Err(err) => { + tracing::info!("chain config is not in memory, trying storage: {err:#}"); + } + } + + // Try storage. + self.inner().get_chain_config(commitment).await + } +} + +#[async_trait] +impl< + N: network::Type, + Ver: StaticVersionType + 'static, + P: SequencerPersistence, + D: ChainConfigPersistence + Send + Sync, + > ChainConfigPersistence for StorageState +{ + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + self.inner_mut().insert_chain_config(chain_config).await + } + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.inner().load_chain_config(commitment).await + } } impl CatchupDataSource @@ -261,6 +297,20 @@ impl, + ) -> anyhow::Result { + let state = self.consensus().await.read().await.decided_state().await; + let chain_config = state.chain_config; + + if chain_config.commit() == commitment { + chain_config.resolve().context("chain config found") + } else { + bail!("chain config not found") + } + } } impl @@ -302,6 +352,7 @@ pub mod test_helpers { use super::*; use crate::{ catchup::{mock::MockStateCatchup, StateCatchup}, + genesis::Upgrade, persistence::{no_storage, PersistenceOptions, SequencerPersistence}, state::{BlockMerkleTree, ValidatedState}, testing::{run_test_builder, wait_for_decide_on_handle, TestConfig}, @@ -325,10 +376,11 @@ pub mod test_helpers { use itertools::izip; use jf_merkle_tree::{MerkleCommitment, MerkleTreeScheme}; use portpicker::pick_unused_port; - use std::time::Duration; + use std::{collections::BTreeMap, time::Duration}; use surf_disco::Client; use tide_disco::error::ServerError; use url::Url; + use vbs::version::Version; pub const STAKE_TABLE_CAPACITY_FOR_TEST: u64 = 10; @@ -338,6 +390,15 @@ pub mod test_helpers { pub cfg: TestConfig, } + #[derive(Clone, Debug)] + pub struct TestNetworkUpgrades { + pub upgrades: BTreeMap, + pub start_proposing_view: u64, + pub stop_proposing_view: u64, + pub start_voting_view: u64, + pub stop_voting_view: u64, + } + impl TestNetwork

{ pub async fn with_state( opt: Options, @@ -345,17 +406,31 @@ pub mod test_helpers { persistence: [impl PersistenceOptions; TestConfig::NUM_NODES], catchup: [impl StateCatchup + 'static; TestConfig::NUM_NODES], l1: Url, + upgrades: Option, builder_port: Option, ) -> Self { let mut cfg = TestConfig::default_with_l1(l1); let (builder_task, builder_url) = run_test_builder(builder_port).await; - cfg.set_builder_url(builder_url); + cfg.set_builder_urls(vec1::vec1![builder_url]); + + let mut upgrades_map = BTreeMap::default(); + if let Some(upgrades) = upgrades { + cfg.set_upgrade_parameters( + upgrades.start_proposing_view, + upgrades.stop_proposing_view, + upgrades.start_voting_view, + upgrades.stop_voting_view, + ); + + upgrades_map = upgrades.upgrades; + } let mut nodes = join_all(izip!(state, persistence, catchup).enumerate().map( |(i, (state, persistence, catchup))| { let opt = opt.clone(); + let upgrades_map = upgrades_map.clone(); let cfg = &cfg; async move { if i == 0 { @@ -371,6 +446,7 @@ pub mod test_helpers { &*metrics, STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, + upgrades_map, ) .await } @@ -389,6 +465,7 @@ pub mod test_helpers { &NoMetrics, STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, + upgrades_map, ) .await } @@ -400,9 +477,7 @@ pub mod test_helpers { let handle_0 = &nodes[0]; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.event_stream().await)); - } + builder_task.start(Box::new(handle_0.event_stream().await)); for ctx in &nodes { ctx.start_consensus().await; @@ -426,6 +501,7 @@ pub mod test_helpers { persistence, std::array::from_fn(|_| MockStateCatchup::default()), l1, + Default::default(), builder_port, ) .await @@ -897,6 +973,7 @@ mod test { use super::*; use crate::{ catchup::{mock::MockStateCatchup, StatePeers}, + genesis::{Upgrade, UpgradeType}, persistence::no_storage, state::{FeeAccount, FeeAmount, ValidatedState}, testing::TestConfig, @@ -904,7 +981,7 @@ mod test { }; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use async_std::task::sleep; - use committable::Commitment; + use committable::{Commitment, Committable}; use es_version::{SequencerVersion, SEQUENCER_VERSION}; use ethers::utils::Anvil; use futures::future::{self, join_all}; @@ -924,9 +1001,10 @@ mod test { use surf_disco::Client; use test_helpers::{ catchup_test_helper, state_signature_test_helper, status_test_helper, submit_test_helper, - TestNetwork, + TestNetwork, TestNetworkUpgrades, }; use tide_disco::{app::AppHealth, error::ServerError, healthcheck::HealthStatus}; + use vbs::version::Version; #[async_std::test] async fn test_healthcheck() { @@ -1067,6 +1145,7 @@ mod test { }), l1, None, + None, ) .await; @@ -1112,6 +1191,7 @@ mod test { &NoMetrics, test_helpers::STAKE_TABLE_CAPACITY_FOR_TEST, SEQUENCER_VERSION, + Default::default(), ) .await; let mut events = node.event_stream().await; @@ -1143,6 +1223,242 @@ mod test { } } + #[async_std::test] + async fn test_chain_config_from_instance() { + // This test uses a ValidatedState which only has the default chain config commitment. + // The NodeState has the full chain config. + // Both chain config commitments will match, so the ValidatedState should have the full chain config after a non-genesis block is decided. + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let chain_config: ChainConfig = ChainConfig::default(); + + let state = ValidatedState { + chain_config: chain_config.commit().into(), + ..Default::default() + }; + + let states = std::array::from_fn(|_| state.clone()); + + let mut network = TestNetwork::with_state( + Options::with_port(port).catchup(Default::default()), + states, + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls(vec![format!("http://localhost:{port}") + .parse() + .unwrap()]) + }), + l1, + None, + None, + ) + .await; + + // Wait for few blocks to be decided. + network + .server + .event_stream() + .await + .filter(|event| future::ready(matches!(event.event, EventType::Decide { .. }))) + .take(3) + .collect::>() + .await; + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + assert_eq!(state.chain_config.resolve().unwrap(), chain_config) + } + + network.server.shut_down().await; + drop(network); + } + + #[async_std::test] + async fn test_chain_config_catchup() { + // This test uses a ValidatedState with a non-default chain config + // so it will be different from the NodeState chain config used by the TestNetwork. + // However, for this test to work, at least one node should have a full chain config + // to allow other nodes to catch up. + + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let cf = ChainConfig { + max_block_size: 300.into(), + base_fee: 1.into(), + ..Default::default() + }; + + // State1 contains only the chain config commitment + let state1 = ValidatedState { + chain_config: cf.commit().into(), + ..Default::default() + }; + + //state 2 contains the full chain config + let state2 = ValidatedState { + chain_config: cf.into(), + ..Default::default() + }; + + let mut states = std::array::from_fn(|_| state1.clone()); + // only one node has the full chain config + // all the other nodes should do a catchup to get the full chain config from peer 0 + states[0] = state2; + + let mut network = TestNetwork::with_state( + Options::from(options::Http { + port, + max_connections: None, + }) + .catchup(Default::default()), + states, + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls(vec![format!("http://localhost:{port}") + .parse() + .unwrap()]) + }), + l1, + None, + None, + ) + .await; + + // Wait for a few blocks to be decided. + network + .server + .event_stream() + .await + .filter(|event| future::ready(matches!(event.event, EventType::Decide { .. }))) + .take(3) + .collect::>() + .await; + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + assert_eq!(state.chain_config.resolve().unwrap(), cf) + } + + network.server.shut_down().await; + drop(network); + } + + #[async_std::test] + async fn test_chain_config_upgrade() { + setup_logging(); + setup_backtrace(); + + let port = pick_unused_port().expect("No ports free"); + let anvil = Anvil::new().spawn(); + let l1 = anvil.endpoint().parse().unwrap(); + + let chain_config_upgrade = ChainConfig { + max_block_size: 300.into(), + base_fee: 1.into(), + ..Default::default() + }; + let mut map = std::collections::BTreeMap::new(); + let view = 5; + let propose_window = 10; + map.insert( + Version { major: 0, minor: 2 }, + Upgrade { + view, + propose_window, + upgrade_type: UpgradeType::ChainConfig { + chain_config: chain_config_upgrade, + }, + }, + ); + + let stop_voting_view = 100; + let upgrades = TestNetworkUpgrades { + upgrades: map, + start_proposing_view: view, + stop_proposing_view: view + propose_window, + start_voting_view: 1, + stop_voting_view, + }; + + let mut network = TestNetwork::with_state( + Options::from(options::Http { + port, + max_connections: None, + }) + .catchup(Default::default()) + .status(Default::default()), + Default::default(), + [no_storage::Options; TestConfig::NUM_NODES], + std::array::from_fn(|_| { + StatePeers::::from_urls(vec![format!("http://localhost:{port}") + .parse() + .unwrap()]) + }), + l1, + Some(upgrades), + None, + ) + .await; + + let mut events = network.server.event_stream().await; + loop { + let event = events.next().await.unwrap(); + + match event.event { + EventType::UpgradeProposal { proposal, .. } => { + let upgrade = proposal.data.upgrade_proposal; + let new_version = upgrade.new_version; + assert_eq!(new_version, Version { major: 0, minor: 2 }); + break; + } + _ => continue, + } + } + + let client: Client = + Client::new(format!("http://localhost:{port}").parse().unwrap()); + client.connect(None).await; + tracing::info!(port, "server running"); + + 'outer: loop { + let height = client + .get::("status/block-height") + .send() + .await + .unwrap(); + + for peer in &network.peers { + let state = peer.consensus().read().await.decided_state().await; + + match state.chain_config.resolve() { + Some(cf) => { + if cf != chain_config_upgrade && height as u64 > stop_voting_view { + panic!("failed to upgrade chain config"); + } + } + None => continue 'outer, + } + } + + break; + } + + network.server.shut_down().await; + drop(network); + } + #[async_std::test] pub(crate) async fn test_restart() { setup_logging(); @@ -1169,6 +1485,7 @@ mod test { std::array::from_fn(|_| MockStateCatchup::default()), l1, None, + None, ) .await; @@ -1245,6 +1562,7 @@ mod test { }), l1, None, + None, ) .await; let client: Client = diff --git a/sequencer/src/api/data_source.rs b/sequencer/src/api/data_source.rs index 82e1dbc06f..392f554171 100644 --- a/sequencer/src/api/data_source.rs +++ b/sequencer/src/api/data_source.rs @@ -8,10 +8,11 @@ use super::{ use crate::{ network, persistence::{self, SequencerPersistence}, - PubKey, SeqTypes, Transaction, + ChainConfig, PubKey, SeqTypes, Transaction, }; use anyhow::bail; use async_trait::async_trait; +use committable::Commitment; use ethers::prelude::Address; use futures::future::Future; use hotshot_query_service::{ @@ -29,6 +30,7 @@ use hotshot_types::{ use serde::Serialize; use tide_disco::Url; use vbs::version::StaticVersionType; +use vec1::Vec1; pub trait DataSourceOptions: persistence::PersistenceOptions { type DataSource: SequencerDataSource; @@ -141,6 +143,15 @@ pub(crate) trait CatchupDataSource { bail!("merklized state catchup is not supported for this data source"); } } + + fn get_chain_config( + &self, + _commitment: Commitment, + ) -> impl Send + Future> { + async { + bail!("chain config catchup is not supported for this data source"); + } + } } impl CatchupDataSource for MetricsDataSource {} @@ -205,7 +216,11 @@ pub struct PublicHotShotConfig { pub num_bootstrap: usize, pub builder_timeout: Duration, pub data_request_delay: Duration, - pub builder_url: Url, + pub builder_urls: Vec1, + pub start_proposing_view: u64, + pub stop_proposing_view: u64, + pub start_voting_view: u64, + pub stop_voting_view: u64, } impl From> for PublicHotShotConfig { @@ -233,7 +248,11 @@ impl From> for PublicHotShotConfig { num_bootstrap, builder_timeout, data_request_delay, - builder_url, + builder_urls, + start_proposing_view, + stop_proposing_view, + start_voting_view, + stop_voting_view, } = v; Self { @@ -256,7 +275,11 @@ impl From> for PublicHotShotConfig { num_bootstrap, builder_timeout, data_request_delay, - builder_url, + builder_urls, + start_proposing_view, + stop_proposing_view, + start_voting_view, + stop_voting_view, } } } diff --git a/sequencer/src/api/endpoints.rs b/sequencer/src/api/endpoints.rs index f91e5153c0..685e4ba61e 100644 --- a/sequencer/src/api/endpoints.rs +++ b/sequencer/src/api/endpoints.rs @@ -258,6 +258,19 @@ where .map_err(|err| Error::catch_all(StatusCode::NOT_FOUND, format!("{err:#}"))) } .boxed() + })? + .get("chainconfig", |req, state| { + async move { + let commitment = req + .blob_param("commitment") + .map_err(Error::from_request_error)?; + + state + .get_chain_config(commitment) + .await + .map_err(|err| Error::catch_all(StatusCode::NOT_FOUND, format!("{err:#}"))) + } + .boxed() })?; Ok(api) diff --git a/sequencer/src/api/options.rs b/sequencer/src/api/options.rs index d7ce499ce3..3a99b5c865 100644 --- a/sequencer/src/api/options.rs +++ b/sequencer/src/api/options.rs @@ -371,7 +371,7 @@ impl Options { let get_node_state = async move { state.node_state().await.clone() }; tasks.spawn( "merklized state storage update loop", - update_state_storage_loop(ds, get_node_state), + update_state_storage_loop(ds, get_node_state, Ver::version()), ); } diff --git a/sequencer/src/api/sql.rs b/sequencer/src/api/sql.rs index 4ea3dc5cb0..6b9afac5f7 100644 --- a/sequencer/src/api/sql.rs +++ b/sequencer/src/api/sql.rs @@ -3,19 +3,25 @@ use super::{ AccountQueryData, BlocksFrontier, }; use crate::{ - persistence::sql::Options, + persistence::{ + sql::{sql_param, transaction, Options}, + ChainConfigPersistence, + }, state::{BlockMerkleTree, FeeAccountProof, FeeMerkleTree}, - SeqTypes, + ChainConfig, SeqTypes, }; use anyhow::{bail, Context}; use async_trait::async_trait; +use committable::Commitment; use ethers::prelude::Address; +use futures::FutureExt; use hotshot_query_service::{ data_source::{ - sql::{Config, SqlDataSource}, + sql::{Config, Query, SqlDataSource}, storage::SqlStorage, }, merklized_state::{MerklizedStateDataSource, Snapshot}, + Resolvable, }; use hotshot_types::data::ViewNumber; use jf_merkle_tree::{prelude::MerkleNode, MerkleTreeScheme}; @@ -93,6 +99,13 @@ impl CatchupDataSource for SqlStorage { .await .context(format!("fetching frontier at height {height}")) } + + async fn get_chain_config( + &self, + commitment: committable::Commitment, + ) -> anyhow::Result { + self.load_chain_config(commitment).await + } } impl CatchupDataSource for DataSource { @@ -112,10 +125,67 @@ impl CatchupDataSource for DataSource { } } +#[async_trait] +impl ChainConfigPersistence for SqlStorage { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + let commitment = chain_config.commitment(); + let data = bincode::serialize(&chain_config)?; + + transaction(self, |mut tx| { + async move { + tx.upsert( + "chain_config", + ["commitment", "data"], + ["commitment"], + [[sql_param(&(commitment.to_string())), sql_param(&data)]], + ) + .await + .map_err(Into::into) + } + .boxed() + }) + .await + } + + async fn load_chain_config( + &self, + commitment: committable::Commitment, + ) -> anyhow::Result { + let query = self + .query_one( + "SELECT * from chain_config where commitment = $1", + [&commitment.to_string()], + ) + .await?; + + let data: Vec = query.try_get("data")?; + + bincode::deserialize(&data[..]).context("failed to deserialize") + } +} + +#[async_trait] +impl ChainConfigPersistence for DataSource { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()> { + (*self.storage_mut().await) + .insert_chain_config(chain_config) + .await + } + + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.storage().await.load_chain_config(commitment).await + } +} + #[cfg(test)] mod impl_testable_data_source { + use super::*; use crate::api::{self, data_source::testing::TestableSequencerDataSource}; + use hotshot_query_service::data_source::storage::sql::testing::TmpDb; fn tmp_options(db: &TmpDb) -> Options { diff --git a/sequencer/src/bin/cdn-broker.rs b/sequencer/src/bin/cdn-broker.rs index 530b33da5c..0e6266261d 100644 --- a/sequencer/src/bin/cdn-broker.rs +++ b/sequencer/src/bin/cdn-broker.rs @@ -1,12 +1,13 @@ //! The following is the main `Broker` binary, which just instantiates and runs //! a `Broker` object. -use anyhow::Result; +use anyhow::{Context, Result}; use cdn_broker::reexports::crypto::signature::KeyPair; use cdn_broker::{Broker, Config}; use clap::Parser; use hotshot_types::traits::node_implementation::NodeType; use hotshot_types::traits::signature_key::SignatureKey; use sequencer::network::cdn::{ProductionDef, WrappedSignatureKey}; +use sequencer::options::parse_size; use sequencer::SeqTypes; use sha2::Digest; use tracing_subscriber::EnvFilter; @@ -72,6 +73,17 @@ struct Args { /// The seed for broker key generation #[arg(short, long, default_value_t = 0, env = "ESPRESSO_CDN_BROKER_KEY_SEED")] key_seed: u64, + + /// The size of the global memory pool. This is the maximum number of bytes that + /// can be allocated at once for all connections. A connection will block if it + /// tries to allocate more than this amount until some memory is freed. + #[arg( + long, + default_value = "1GB", + value_parser = parse_size, + env = "ESPRESSO_CDN_BROKER_GLOBAL_MEMORY_POOL_SIZE" + )] + global_memory_pool_size: u64, } #[async_std::main] async fn main() -> Result<()> { @@ -95,6 +107,15 @@ async fn main() -> Result<()> { let (public_key, private_key) = ::SignatureKey::generated_from_seed_indexed(key_hash.into(), 1337); + // Cast the memory pool size to a `usize` + let global_memory_pool_size = + usize::try_from(args.global_memory_pool_size).with_context(|| { + format!( + "Failed to convert global memory pool size to usize: {}", + args.global_memory_pool_size + ) + })?; + // Create config let broker_config: Config> = Config { ca_cert_path: args.ca_cert_path, @@ -111,6 +132,7 @@ async fn main() -> Result<()> { public_advertise_endpoint: args.public_advertise_endpoint, private_bind_endpoint: args.private_bind_endpoint, private_advertise_endpoint: args.private_advertise_endpoint, + global_memory_pool_size: Some(global_memory_pool_size), }; // Create new `Broker` diff --git a/sequencer/src/bin/cdn-marshal.rs b/sequencer/src/bin/cdn-marshal.rs index a9646d8b7d..7b15f23789 100644 --- a/sequencer/src/bin/cdn-marshal.rs +++ b/sequencer/src/bin/cdn-marshal.rs @@ -1,10 +1,10 @@ //! The following is the main `Marshal` binary, which just instantiates and runs //! a `Marshal` object. -use anyhow::Result; +use anyhow::{Context, Result}; use cdn_marshal::{Config, Marshal}; use clap::Parser; -use sequencer::{network::cdn::ProductionDef, SeqTypes}; +use sequencer::{network::cdn::ProductionDef, options::parse_size, SeqTypes}; use tracing_subscriber::EnvFilter; #[derive(Parser, Debug)] @@ -38,6 +38,17 @@ struct Args { /// If not provided, a local, pinned CA is used #[arg(long, env = "ESPRESSO_CDN_MARSHAL_CA_KEY_PATH")] ca_key_path: Option, + + /// The size of the global memory pool. This is the maximum number of bytes that + /// can be allocated at once for all connections. A connection will block if it + /// tries to allocate more than this amount until some memory is freed. + #[arg( + long, + default_value = "1GB", + value_parser = parse_size, + env = "ESPRESSO_CDN_MARSHAL_GLOBAL_MEMORY_POOL_SIZE" + )] + global_memory_pool_size: u64, } #[async_std::main] @@ -57,6 +68,15 @@ async fn main() -> Result<()> { .init(); } + // Cast the memory pool size to a `usize` + let global_memory_pool_size = + usize::try_from(args.global_memory_pool_size).with_context(|| { + format!( + "Failed to convert global memory pool size to usize: {}", + args.global_memory_pool_size + ) + })?; + // Create a new `Config` let config = Config { discovery_endpoint: args.discovery_endpoint, @@ -64,6 +84,7 @@ async fn main() -> Result<()> { metrics_bind_endpoint: args.metrics_bind_endpoint, ca_cert_path: args.ca_cert_path, ca_key_path: args.ca_key_path, + global_memory_pool_size: Some(global_memory_pool_size), }; // Create new `Marshal` from the config diff --git a/sequencer/src/bin/dev-cdn.rs b/sequencer/src/bin/dev-cdn.rs index 73def9e41c..064142d713 100644 --- a/sequencer/src/bin/dev-cdn.rs +++ b/sequencer/src/bin/dev-cdn.rs @@ -75,6 +75,7 @@ async fn main() -> Result<()> { ca_cert_path: None, ca_key_path: None, + global_memory_pool_size: Some(1024 * 1024 * 1024), }; // Configure the marshal @@ -84,6 +85,7 @@ async fn main() -> Result<()> { discovery_endpoint: discovery_endpoint.clone(), ca_cert_path: None, ca_key_path: None, + global_memory_pool_size: Some(1024 * 1024 * 1024), }; // Create a new `Broker` diff --git a/sequencer/src/bin/orchestrator.rs b/sequencer/src/bin/orchestrator.rs index 9319a015b5..f51b79c263 100644 --- a/sequencer/src/bin/orchestrator.rs +++ b/sequencer/src/bin/orchestrator.rs @@ -11,6 +11,7 @@ use std::num::{NonZeroUsize, ParseIntError}; use std::str::FromStr; use std::time::Duration; use url::Url; +use vec1::Vec1; #[derive(Parser)] struct Args { @@ -76,8 +77,8 @@ struct Args { keygen_seed: [u8; 32], /// HotShot builder URL - #[arg(long, env = "ESPRESSO_ORCHESTRATOR_BUILDER_URL")] - builder_url: Url, + #[arg(long, env = "ESPRESSO_ORCHESTRATOR_BUILDER_URLS", num_args = 1.., value_delimiter = ',')] + builder_urls: Vec, /// The maximum amount of time a leader can wait to get a block from a builder. /// @@ -196,7 +197,7 @@ async fn main() { config.config.start_delay = args.start_delay.as_millis() as u64; config.config.da_staked_committee_size = args.num_nodes.get(); config.config.da_non_staked_committee_size = 0; - config.config.builder_url = args.builder_url; + config.config.builder_urls = Vec1::try_from_vec(args.builder_urls).unwrap(); config.config.builder_timeout = args.builder_timeout; run_orchestrator( config, diff --git a/sequencer/src/block/full_payload/payload.rs b/sequencer/src/block/full_payload/payload.rs index 1d199cac0d..208742fe72 100644 --- a/sequencer/src/block/full_payload/payload.rs +++ b/sequencer/src/block/full_payload/payload.rs @@ -3,9 +3,11 @@ use crate::{ full_payload::ns_table::{NsIndex, NsTable, NsTableBuilder}, namespace_payload::{Index, Iter, NsPayload, NsPayloadBuilder, NsPayloadRange, TxProof}, }, - NamespaceId, NodeState, SeqTypes, Transaction, ValidatedState, + ChainConfig, NamespaceId, NodeState, SeqTypes, Transaction, ValidatedState, }; + use async_trait::async_trait; +use committable::Committable; use hotshot_query_service::availability::QueryablePayload; use hotshot_types::{ traits::{BlockPayload, EncodeBytes}, @@ -84,14 +86,14 @@ impl Payload { /// Need a sync version of [`BlockPayload::from_transactions`] in order to impl [`BlockPayload::empty`]. fn from_transactions_sync( transactions: impl IntoIterator>::Transaction> + Send, - _validated_state: &>::ValidatedState, - instance_state: &>::Instance, + chain_config: ChainConfig, + _instance_state: &>::Instance, ) -> Result< (Self, >::Metadata), >::Error, > { // accounting for block byte length limit - let max_block_byte_len: usize = u64::from(instance_state.chain_config.max_block_size) + let max_block_byte_len: usize = u64::from(chain_config.max_block_size) .try_into() .map_err(|_| >::Error::BlockBuilding)?; let mut block_byte_len = NsTableBuilder::fixed_overhead_byte_len(); @@ -149,7 +151,25 @@ impl BlockPayload for Payload { validated_state: &Self::ValidatedState, instance_state: &Self::Instance, ) -> Result<(Self, Self::Metadata), Self::Error> { - Self::from_transactions_sync(transactions, validated_state, instance_state) + let validated_state_cf = validated_state.chain_config; + let instance_state_cf = instance_state.chain_config; + + let chain_config = if validated_state_cf.commit() == instance_state_cf.commit() { + instance_state_cf + } else { + match validated_state_cf.resolve() { + Some(cf) => cf, + None => { + instance_state + .peers + .as_ref() + .fetch_chain_config(validated_state_cf.commit()) + .await + } + } + }; + + Self::from_transactions_sync(transactions, chain_config, instance_state) } // TODO avoid cloning the entire payload here? @@ -161,10 +181,9 @@ impl BlockPayload for Payload { } fn empty() -> (Self, Self::Metadata) { - let payload = - Self::from_transactions_sync(vec![], &Default::default(), &Default::default()) - .unwrap() - .0; + let payload = Self::from_transactions_sync(vec![], Default::default(), &Default::default()) + .unwrap() + .0; let ns_table = payload.ns_table().clone(); (payload, ns_table) } diff --git a/sequencer/src/block/test.rs b/sequencer/src/block/test.rs index 02adf867d8..993d016847 100644 --- a/sequencer/src/block/test.rs +++ b/sequencer/src/block/test.rs @@ -4,7 +4,7 @@ use crate::{ namespace_payload::TxProof, }, chain_config::BlockSize, - ChainConfig, NamespaceId, NodeState, Transaction, + ChainConfig, NamespaceId, NodeState, Transaction, ValidatedState, }; use async_compatibility_layer::logging::{setup_backtrace, setup_logging}; use hotshot::traits::BlockPayload; @@ -117,15 +117,21 @@ async fn enforce_max_block_size() { let test = ValidTest::from_tx_lengths(test_case, &mut rng); let tx_count_expected = test.all_txs().len(); - // test: actual block size equals max block size - let instance_state = NodeState::default().with_chain_config(ChainConfig { + let chain_config = ChainConfig { max_block_size: BlockSize::from( (payload_byte_len_expected + ns_table_byte_len_expected) as u64, ), ..Default::default() - }); + }; + + // test: actual block size equals max block size + let instance_state = NodeState::default().with_chain_config(chain_config); - let block = Payload::from_transactions(test.all_txs(), &Default::default(), &instance_state) + let validated_state = ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }; + let block = Payload::from_transactions(test.all_txs(), &validated_state, &instance_state) .await .unwrap() .0; @@ -135,13 +141,21 @@ async fn enforce_max_block_size() { // test: actual block size exceeds max block size, so 1 tx is dropped // WARN log should be emitted - let instance_state = NodeState::default().with_chain_config(ChainConfig { + + let chain_config = ChainConfig { max_block_size: BlockSize::from( (payload_byte_len_expected + ns_table_byte_len_expected - 1) as u64, ), ..Default::default() - }); - let block = Payload::from_transactions(test.all_txs(), &Default::default(), &instance_state) + }; + let instance_state = NodeState::default().with_chain_config(chain_config); + + let validated_state = ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }; + + let block = Payload::from_transactions(test.all_txs(), &validated_state, &instance_state) .await .unwrap() .0; diff --git a/sequencer/src/catchup.rs b/sequencer/src/catchup.rs index b37e60398f..f5b4ba1e6f 100644 --- a/sequencer/src/catchup.rs +++ b/sequencer/src/catchup.rs @@ -2,10 +2,12 @@ use crate::{ api::{data_source::CatchupDataSource, AccountQueryData, BlocksFrontier}, persistence::PersistenceOptions, state::{BlockMerkleTree, FeeAccount, FeeMerkleCommitment}, + ChainConfig, }; use anyhow::{bail, Context}; use async_std::{sync::RwLock, task::sleep}; use async_trait::async_trait; +use committable::Commitment; use derive_more::From; use hotshot_types::{data::ViewNumber, traits::node_implementation::ConsensusTime as _}; use jf_merkle_tree::{prelude::MerkleNode, ForgetableMerkleTreeScheme, MerkleTreeScheme}; @@ -141,6 +143,30 @@ pub trait StateCatchup: Send + Sync + std::fmt::Debug { Ok(()) } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result; + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + // Retry until we succeed. + let mut delay = MIN_RETRY_DELAY; + + loop { + match self.try_fetch_chain_config(commitment).await { + Ok(cf) => return cf, + Err(err) => { + tracing::warn!( + ?delay, + "Could not fetch chain config from any peer, retrying: {err:#}" + ); + sleep(delay).await; + delay = backoff(delay); + } + } + } + } } /// A catchup implementation that falls back to a remote provider, but prefers a local provider when @@ -241,6 +267,28 @@ impl StateCatchup for StatePeers { } bail!("Could not fetch frontier from any peer"); } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + for client in self.clients.iter() { + tracing::info!("Fetching chain config from {}", client.url); + match client + .get::(&format!("catchup/chain-config/{}", commitment)) + .send() + .await + { + Ok(cf) => { + return Ok(cf); + } + Err(err) => { + tracing::warn!("Error fetching chain config from peer: {}", err); + } + } + } + bail!("Could not fetch chain config from any peer"); + } } #[derive(Debug, From)] @@ -291,6 +339,13 @@ where _ => bail!("invalid proof"), } } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + self.db.read().await.get_chain_config(commitment).await + } } #[async_trait] @@ -338,6 +393,17 @@ impl StateCatchup for Box { ) -> anyhow::Result<()> { (**self).remember_blocks_merkle_tree(height, view, mt).await } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + (**self).try_fetch_chain_config(commitment).await + } + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + (**self).fetch_chain_config(commitment).await + } } #[async_trait] @@ -385,6 +451,17 @@ impl StateCatchup for Arc { ) -> anyhow::Result<()> { (**self).remember_blocks_merkle_tree(height, view, mt).await } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + (**self).try_fetch_chain_config(commitment).await + } + + async fn fetch_chain_config(&self, commitment: Commitment) -> ChainConfig { + (**self).fetch_chain_config(commitment).await + } } /// Catchup from multiple providers tries each provider in a round robin fashion until it succeeds. @@ -434,6 +511,22 @@ impl StateCatchup for Vec { bail!("could not fetch account from any provider"); } + + async fn try_fetch_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result { + for provider in self { + match provider.try_fetch_chain_config(commitment).await { + Ok(cf) => return Ok(cf), + Err(err) => { + tracing::warn!(?provider, "failed to fetch chain config: {err:#}"); + } + } + } + + bail!("could not fetch chain config from any provider"); + } } #[cfg(any(test, feature = "testing"))] @@ -496,5 +589,12 @@ pub mod mock { Ok(()) } + + async fn try_fetch_chain_config( + &self, + _commitment: Commitment, + ) -> anyhow::Result { + Ok(ChainConfig::default()) + } } } diff --git a/sequencer/src/genesis.rs b/sequencer/src/genesis.rs index 858e9e4810..9c013090d3 100644 --- a/sequencer/src/genesis.rs +++ b/sequencer/src/genesis.rs @@ -7,8 +7,12 @@ use anyhow::Context; use derive_more::{Display, From, Into}; use sequencer_utils::{impl_serde_from_string_or_integer, ser::FromStringOrInteger}; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, path::Path}; +use std::{ + collections::{BTreeMap, HashMap}, + path::Path, +}; use time::{format_description::well_known::Rfc3339 as TimestampFormat, OffsetDateTime}; +use vbs::version::Version; /// Initial configuration of an Espresso stake table. #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] @@ -98,6 +102,107 @@ pub struct Genesis { pub accounts: HashMap, pub l1_finalized: Option, pub header: GenesisHeader, + #[serde(rename = "upgrade", with = "upgrade_serialization")] + #[serde(default)] + pub upgrades: BTreeMap, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +#[serde(rename_all = "snake_case")] +pub enum UpgradeType { + // Note: Wrapping this in a tuple variant causes deserialization to fail because + // the 'chain_config' name is also provided in the TOML input. + ChainConfig { chain_config: ChainConfig }, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Upgrade { + pub view: u64, + pub propose_window: u64, + #[serde(flatten)] + pub upgrade_type: UpgradeType, +} + +mod upgrade_serialization { + use crate::genesis::{Upgrade, UpgradeType}; + use serde::ser::SerializeSeq; + use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serializer, + }; + use std::{collections::BTreeMap, fmt}; + use vbs::version::Version; + + pub fn serialize(map: &BTreeMap, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(map.len()))?; + for (version, upgrade) in map { + seq.serialize_element(&( + version.to_string(), + upgrade.view, + upgrade.propose_window, + upgrade.upgrade_type.clone(), + ))?; + } + seq.end() + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct VecToHashMap; + + impl<'de> Visitor<'de> for VecToHashMap { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a vector of tuples (key-value pairs)") + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: SeqAccess<'de>, + { + let mut map = BTreeMap::new(); + + #[derive(Deserialize)] + struct UpgradeFields { + version: String, + view: u64, + propose_window: u64, + #[serde(flatten)] + upgrade_type: UpgradeType, + } + + while let Some(fields) = seq.next_element::()? { + // add try_from in Version + let version: Vec<_> = fields.version.split('.').collect(); + + let version = Version { + major: version[0].parse().expect("invalid version"), + minor: version[1].parse().expect("invalid version"), + }; + + map.insert( + version, + Upgrade { + view: fields.view, + propose_window: fields.propose_window, + upgrade_type: fields.upgrade_type, + }, + ); + } + + Ok(map) + } + } + + deserializer.deserialize_seq(VecToHashMap) + } } impl Genesis { @@ -111,6 +216,7 @@ impl Genesis { let path = path.as_ref(); let bytes = std::fs::read(path).context(format!("genesis file {}", path.display()))?; let text = std::str::from_utf8(&bytes).context("genesis file must be UTF-8")?; + toml::from_str(text).context("malformed genesis file") } } @@ -118,6 +224,7 @@ impl Genesis { #[cfg(test)] mod test { use super::*; + use ethers::prelude::{Address, H160, H256}; use toml::toml; @@ -145,6 +252,18 @@ mod test { number = 64 timestamp = "0x123def" hash = "0x80f5dd11f2bdda2814cb1ad94ef30a47de02cf28ad68c89e104c00c4e51bb7a5" + + [[upgrade]] + version = "1.0" + view = 1 + propose_window = 10 + + [upgrade.chain_config] + chain_id = 12345 + max_block_size = 30000 + base_fee = 1 + fee_recipient = "0x0000000000000000000000000000000000000000" + fee_contract = "0x0000000000000000000000000000000000000000" } .to_string(); diff --git a/sequencer/src/header.rs b/sequencer/src/header.rs index 5a1e6a0384..1ebb25b544 100644 --- a/sequencer/src/header.rs +++ b/sequencer/src/header.rs @@ -2,6 +2,7 @@ use crate::{ block::NsTable, chain_config::ResolvableChainConfig, eth_signature_key::BuilderSignature, + genesis::UpgradeType, l1_client::L1Snapshot, state::{BlockMerkleCommitment, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment}, ChainConfig, L1BlockInfo, Leaf, NamespaceId, NodeState, SeqTypes, ValidatedState, @@ -9,7 +10,7 @@ use crate::{ use anyhow::{ensure, Context}; use ark_serialize::CanonicalSerialize; use committable::{Commitment, Committable, RawCommitmentBuilder}; -use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader}; +use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader, Resolvable}; use hotshot_types::{ traits::{ block_contents::{BlockHeader, BlockPayload, BuilderFee}, @@ -234,6 +235,31 @@ impl Header { builder_signature, }) } + + async fn get_chain_config( + validated_state: &ValidatedState, + instance_state: &NodeState, + ) -> ChainConfig { + let validated_cf = validated_state.chain_config; + let instance_cf = instance_state.chain_config; + + if validated_cf.commit() == instance_cf.commitment() { + return instance_cf; + } + + match validated_cf.resolve() { + Some(cf) => cf, + None => { + tracing::info!("fetching chain config {} from peers", validated_cf.commit()); + + instance_state + .peers + .as_ref() + .fetch_chain_config(validated_cf.commit()) + .await + } + } + } } #[derive(Debug, Snafu)] @@ -274,14 +300,29 @@ impl BlockHeader for Header { metadata: <::BlockPayload as BlockPayload>::Metadata, builder_fee: BuilderFee, _vid_common: VidCommon, - _version: Version, + version: Version, ) -> Result { - let chain_config = instance_state.chain_config; let height = parent_leaf.height(); let view = parent_leaf.view_number(); let mut validated_state = parent_state.clone(); + let chain_config = if version > instance_state.current_version { + match instance_state + .upgrades + .get(&version) + .map(|upgrade| match upgrade.upgrade_type { + UpgradeType::ChainConfig { chain_config } => chain_config, + }) { + Some(cf) => cf, + None => Header::get_chain_config(&validated_state, instance_state).await, + } + } else { + Header::get_chain_config(&validated_state, instance_state).await + }; + + validated_state.chain_config = chain_config.into(); + // Fetch the latest L1 snapshot. let l1_snapshot = instance_state.l1_client.snapshot().await; // Fetch the new L1 deposits between parent and current finalized L1 block. @@ -373,6 +414,7 @@ impl BlockHeader for Header { let ValidatedState { fee_merkle_tree, block_merkle_tree, + .. } = ValidatedState::genesis(instance_state).0; let block_merkle_tree_root = block_merkle_tree.commitment(); let fee_merkle_tree_root = fee_merkle_tree.commitment(); @@ -481,6 +523,7 @@ mod test_headers { }; use hotshot_types::{traits::signature_key::BuilderSignatureKey, vid::vid_scheme}; use jf_vid::VidScheme; + use vbs::version::{StaticVersion, StaticVersionType}; #[derive(Debug, Default)] #[must_use] @@ -533,6 +576,7 @@ mod test_headers { let mut validated_state = ValidatedState { block_merkle_tree: block_merkle_tree.clone(), fee_merkle_tree, + chain_config: genesis.instance_state.chain_config.into(), }; let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0); @@ -784,9 +828,11 @@ mod test_headers { parent_header.block_merkle_tree_root = block_merkle_tree_root; let mut proposal = parent_header.clone(); + let ver = StaticVersion::<1, 0>::version(); + // Pass a different chain config to trigger a chain config validation error. let state = validated_state - .apply_header(&genesis.instance_state, &parent_leaf, &proposal) + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) .await .unwrap() .0; @@ -809,7 +855,7 @@ mod test_headers { // Advance `proposal.height` to trigger validation error. let validated_state = validated_state - .apply_header(&genesis.instance_state, &parent_leaf, &proposal) + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) .await .unwrap() .0; @@ -833,7 +879,7 @@ mod test_headers { proposal.height += 1; let validated_state = validated_state - .apply_header(&genesis.instance_state, &parent_leaf, &proposal) + .apply_header(&genesis.instance_state, &parent_leaf, &proposal, ver) .await .unwrap() .0; @@ -918,7 +964,7 @@ mod test_headers { ns_table, builder_fee, vid_common.clone(), - hotshot_types::constants::BASE_VERSION, + hotshot_types::constants::Base::VERSION, ) .await .unwrap(); @@ -936,7 +982,12 @@ mod test_headers { block_merkle_tree.push(proposal.commit()).unwrap(); let proposal_state = proposal_state - .apply_header(&genesis_state, &parent_leaf, &proposal) + .apply_header( + &genesis_state, + &parent_leaf, + &proposal, + StaticVersion::<1, 0>::version(), + ) .await .unwrap() .0; diff --git a/sequencer/src/lib.rs b/sequencer/src/lib.rs index f17fd780ae..5f6fbb9f64 100644 --- a/sequencer/src/lib.rs +++ b/sequencer/src/lib.rs @@ -21,7 +21,7 @@ use context::SequencerContext; use ethers::types::U256; #[cfg(feature = "libp2p")] use futures::FutureExt; -use genesis::{GenesisHeader, L1Finalized}; +use genesis::{GenesisHeader, L1Finalized, Upgrade}; // Should move `STAKE_TABLE_CAPACITY` in the sequencer repo when we have variate stake table support @@ -42,8 +42,8 @@ use hotshot::{ traits::{ election::static_committee::GeneralStaticCommittee, implementations::{ - derive_libp2p_peer_id, KeyPair, MemoryNetwork, NetworkingMetricsValue, PushCdnNetwork, - Topic, WrappedSignatureKey, + derive_libp2p_peer_id, CdnMetricsValue, KeyPair, MemoryNetwork, PushCdnNetwork, Topic, + WrappedSignatureKey, }, }, types::SignatureKey, @@ -55,6 +55,7 @@ use hotshot_orchestrator::{ }; use hotshot_types::{ consensus::CommitmentMap, + constants::Base, data::{DaProposal, QuorumProposal, VidDisperseShare, ViewNumber}, event::HotShotAction, light_client::{StateKeyPair, StateSignKey}, @@ -76,7 +77,7 @@ use persistence::{PersistenceOptions, SequencerPersistence}; use serde::{Deserialize, Serialize}; use snafu::Snafu; use std::{collections::BTreeMap, fmt::Debug, marker::PhantomData, net::SocketAddr, sync::Arc}; -use vbs::version::StaticVersionType; +use vbs::version::{StaticVersionType, Version}; #[cfg(feature = "libp2p")] use std::time::Duration; @@ -181,6 +182,8 @@ pub struct NodeState { pub genesis_header: GenesisHeader, pub genesis_state: ValidatedState, pub l1_genesis: Option, + pub upgrades: BTreeMap, + pub current_version: Version, } impl NodeState { @@ -196,8 +199,13 @@ impl NodeState { l1_client, peers: Arc::new(catchup), genesis_header: Default::default(), - genesis_state: Default::default(), + genesis_state: ValidatedState { + chain_config: chain_config.into(), + ..Default::default() + }, l1_genesis: None, + upgrades: Default::default(), + current_version: Base::VERSION, } } @@ -225,6 +233,11 @@ impl NodeState { self.chain_config = cfg; self } + + pub fn with_upgrades(mut self, upgrades: BTreeMap) -> Self { + self.upgrades = upgrades; + self + } } // This allows us to turn on `Default` on InstanceState trait @@ -385,6 +398,15 @@ pub async fn init_node( } }; + let version = Ver::version(); + if let Some(upgrade) = genesis.upgrades.get(&version) { + let view = upgrade.view; + config.config.start_proposing_view = view; + config.config.stop_proposing_view = view + upgrade.propose_window; + config.config.start_voting_view = 1; + config.config.stop_voting_view = u64::MAX; + } + // If the `Libp2p` bootstrap nodes were supplied via the command line, override those // present in the config file. if let Some(bootstrap_nodes) = network_params.libp2p_bootstrap_nodes { @@ -423,6 +445,7 @@ pub async fn init_node( public_key: WrappedSignatureKey(my_config.public_key), private_key: my_config.private_key.clone(), }, + CdnMetricsValue::new(metrics), ) .with_context(|| "Failed to create CDN network")?; @@ -436,6 +459,7 @@ pub async fn init_node( // We need the private key so we can derive our Libp2p keypair // (using https://docs.rs/blake3/latest/blake3/fn.derive_key.html) &my_config.private_key, + hotshot::traits::implementations::Libp2pMetricsValue::new(metrics), ) .await .with_context(|| "Failed to create libp2p network")?; @@ -481,13 +505,10 @@ pub async fn init_node( _pd: Default::default(), }; - // The web server network doesn't have any metrics. By creating and dropping a - // `NetworkingMetricsValue`, we ensure the networking metrics are created, but just not - // populated, so that monitoring software built to work with network-related metrics doesn't - // crash horribly just because we're not using the P2P network yet. - let _ = NetworkingMetricsValue::new(metrics); - - let mut genesis_state = ValidatedState::default(); + let mut genesis_state = ValidatedState { + chain_config: genesis.chain_config.into(), + ..Default::default() + }; for (address, amount) in genesis.accounts { tracing::info!(%address, %amount, "Prefunding account for demo"); genesis_state.prefund_account(address, amount); @@ -513,6 +534,8 @@ pub async fn init_node( ) .await, node_id: node_index, + upgrades: genesis.upgrades, + current_version: Ver::VERSION, }; let mut ctx = SequencerContext::init( @@ -549,11 +572,12 @@ pub mod testing { future::join_all, stream::{Stream, StreamExt}, }; + use genesis::Upgrade; use hotshot::traits::{ implementations::{MasterMap, MemoryNetwork}, BlockPayload, }; - use hotshot::types::{EventType::Decide, Message}; + use hotshot::types::EventType::Decide; use hotshot_stake_table::vec_based::StakeTable; use hotshot_testing::block_builder::{ BuilderTask, SimpleBuilderConfig, SimpleBuilderImplementation, TestBuilderImplementation, @@ -566,12 +590,11 @@ pub mod testing { }; use portpicker::pick_unused_port; use std::time::Duration; + use vbs::version::Version; const STAKE_TABLE_CAPACITY_FOR_TEST: u64 = 10; - pub async fn run_test_builder( - port: Option, - ) -> (Option>>, Url) { + pub async fn run_test_builder(port: Option) -> (Box>, Url) { let builder_config = if let Some(port) = port { SimpleBuilderConfig { port } } else { @@ -580,6 +603,7 @@ pub mod testing { >::start( TestConfig::NUM_NODES, builder_config, + Default::default(), ) .await } @@ -589,7 +613,7 @@ pub mod testing { config: HotShotConfig, priv_keys: Vec, state_key_pairs: Vec, - master_map: Arc, PubKey>>, + master_map: Arc>, url: Url, } @@ -634,16 +658,20 @@ pub mod testing { my_own_validator_config: Default::default(), view_sync_timeout: Duration::from_secs(1), data_request_delay: Duration::from_secs(1), - builder_url: Url::parse(&format!( + builder_urls: vec1::vec1![Url::parse(&format!( "http://127.0.0.1:{}", pick_unused_port().unwrap() )) - .unwrap(), + .unwrap()], builder_timeout: Duration::from_secs(1), start_threshold: ( known_nodes_with_stake.clone().len() as u64, known_nodes_with_stake.clone().len() as u64, ), + start_proposing_view: 0, + stop_proposing_view: 0, + start_voting_view: 0, + stop_voting_view: 0, }; Self { @@ -657,7 +685,7 @@ pub mod testing { } impl TestConfig { - pub const NUM_NODES: usize = 4; + pub const NUM_NODES: usize = 5; pub fn num_nodes(&self) -> usize { self.priv_keys.len() @@ -667,8 +695,8 @@ pub mod testing { &self.config } - pub fn set_builder_url(&mut self, builder_url: Url) { - self.config.builder_url = builder_url; + pub fn set_builder_urls(&mut self, builder_urls: vec1::Vec1) { + self.config.builder_urls = builder_urls; } pub fn default_with_l1(l1: Url) -> Self { @@ -678,6 +706,19 @@ pub mod testing { } } + pub fn set_upgrade_parameters( + &mut self, + start_proposing_view: u64, + stop_proposing_view: u64, + start_voting_view: u64, + stop_voting_view: u64, + ) { + self.config.start_proposing_view = start_proposing_view; + self.config.stop_proposing_view = stop_proposing_view; + self.config.start_voting_view = start_voting_view; + self.config.stop_voting_view = stop_voting_view; + } + pub async fn init_nodes( &self, bind_version: Ver, @@ -691,6 +732,7 @@ pub mod testing { &NoMetrics, STAKE_TABLE_CAPACITY_FOR_TEST, bind_version, + Default::default(), ) .await })) @@ -729,6 +771,7 @@ pub mod testing { metrics: &dyn Metrics, stake_table_capacity: u64, bind_version: Ver, + upgrades: BTreeMap, ) -> SequencerContext { let mut config = self.config.clone(); let my_peer_config = &config.known_nodes_with_stake[i]; @@ -742,8 +785,7 @@ pub mod testing { let network = Arc::new(MemoryNetwork::new( config.my_own_validator_config.public_key, - NetworkingMetricsValue::new(metrics), - self.master_map.clone(), + &self.master_map, None, )); let networks = Networks { @@ -762,7 +804,8 @@ pub mod testing { L1Client::new(self.url.clone(), 1000), catchup::local_and_remote(persistence_opt.clone(), catchup).await, ) - .with_genesis(state); + .with_genesis(state) + .with_upgrades(upgrades); tracing::info!( i, @@ -856,16 +899,14 @@ mod test { let (builder_task, builder_url) = run_test_builder(None).await; - config.set_builder_url(builder_url); + config.set_builder_urls(vec1::vec1![builder_url]); let handles = config.init_nodes(ver).await; let handle_0 = &handles[0]; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.event_stream().await)); - } + builder_task.start(Box::new(handle_0.event_stream().await)); let mut events = handle_0.event_stream().await; @@ -898,7 +939,7 @@ mod test { let (builder_task, builder_url) = run_test_builder(None).await; - config.set_builder_url(builder_url); + config.set_builder_urls(vec1::vec1![builder_url]); let handles = config.init_nodes(ver).await; let handle_0 = &handles[0]; @@ -906,9 +947,7 @@ mod test { let mut events = handle_0.event_stream().await; // Hook the builder up to the event stream from the first node - if let Some(builder_task) = builder_task { - builder_task.start(Box::new(handle_0.event_stream().await)); - } + builder_task.start(Box::new(handle_0.event_stream().await)); for handle in handles.iter() { handle.start_consensus().await; diff --git a/sequencer/src/main.rs b/sequencer/src/main.rs index 91847558a2..4324328bdc 100644 --- a/sequencer/src/main.rs +++ b/sequencer/src/main.rs @@ -195,6 +195,7 @@ mod test { accounts: Default::default(), l1_finalized: Default::default(), header: Default::default(), + upgrades: Default::default(), }; genesis.to_file(&genesis_file).unwrap(); diff --git a/sequencer/src/network/cdn.rs b/sequencer/src/network/cdn.rs index e0b998e41c..a3e6b7074b 100644 --- a/sequencer/src/network/cdn.rs +++ b/sequencer/src/network/cdn.rs @@ -3,16 +3,33 @@ use std::marker::PhantomData; use bincode::Options; use cdn_broker::reexports::{ - connection::{ - protocols::{Quic, Tcp}, - NoMiddleware, TrustedMiddleware, UntrustedMiddleware, - }, + connection::protocols::{Quic, Tcp}, crypto::signature::{Serializable, SignatureScheme}, - def::{ConnectionDef, RunDef}, + def::{ConnectionDef, RunDef, Topic as TopicTrait}, discovery::{Embedded, Redis}, }; -use hotshot::{traits::implementations::Topic, types::SignatureKey}; +use hotshot::{traits::implementations::Topic as HotShotTopic, types::SignatureKey}; use hotshot_types::{traits::node_implementation::NodeType, utils::bincode_opts}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use static_assertions::const_assert_eq; + +/// The enum for the topics we can subscribe to in the Push CDN +#[repr(u8)] +#[derive(IntoPrimitive, TryFromPrimitive, Clone, PartialEq, Eq)] +pub enum Topic { + /// The global topic + Global = 0, + /// The DA topic + Da = 1, +} + +// Make sure the topics are the same as defined in `HotShot`. +const_assert_eq!(Topic::Global as u8, HotShotTopic::Global as u8); +const_assert_eq!(Topic::Da as u8, HotShotTopic::Da as u8); + +/// Implement the `TopicTrait` for our `Topic` enum. This lets us define +/// compatible topics at the broker-level. Others will be rejected. +impl TopicTrait for Topic {} /// A wrapped `SignatureKey`. We need to implement the Push CDN's `SignatureScheme` /// trait in order to sign and verify messages to/from the CDN. @@ -70,7 +87,6 @@ pub struct UserDef(PhantomData); impl ConnectionDef for UserDef { type Scheme = WrappedSignatureKey; type Protocol = Quic; - type Middleware = UntrustedMiddleware; } /// The broker definition for the Push CDN. @@ -79,7 +95,6 @@ pub struct BrokerDef(PhantomData); impl ConnectionDef for BrokerDef { type Scheme = WrappedSignatureKey; type Protocol = Tcp; - type Middleware = TrustedMiddleware; } /// The client definition for the Push CDN. Uses the Quic @@ -90,7 +105,6 @@ pub struct ClientDef(PhantomData); impl ConnectionDef for ClientDef { type Scheme = WrappedSignatureKey; type Protocol = Quic; - type Middleware = NoMiddleware; } /// The testing run definition for the Push CDN. diff --git a/sequencer/src/network/mod.rs b/sequencer/src/network/mod.rs index c229525c9e..0edf3e634f 100644 --- a/sequencer/src/network/mod.rs +++ b/sequencer/src/network/mod.rs @@ -1,13 +1,11 @@ -use hotshot_types::message::Message; - use super::*; pub mod cdn; pub mod libp2p; pub trait Type: 'static { - type DAChannel: ConnectedNetwork, PubKey>; - type QuorumChannel: ConnectedNetwork, PubKey>; + type DAChannel: ConnectedNetwork; + type QuorumChannel: ConnectedNetwork; } #[derive(Clone, Copy, Default)] @@ -29,6 +27,6 @@ impl Type for Production { pub struct Memory; impl Type for Memory { - type DAChannel = MemoryNetwork, PubKey>; - type QuorumChannel = MemoryNetwork, PubKey>; + type DAChannel = MemoryNetwork; + type QuorumChannel = MemoryNetwork; } diff --git a/sequencer/src/persistence.rs b/sequencer/src/persistence.rs index b78106396b..73ba384478 100644 --- a/sequencer/src/persistence.rs +++ b/sequencer/src/persistence.rs @@ -8,11 +8,13 @@ //! an extension that node operators can opt into. This module defines the minimum level of //! persistence which is _required_ to run a node. -use crate::{Leaf, NodeState, PubKey, SeqTypes, StateCatchup, ValidatedState, ViewNumber}; +use crate::{ + ChainConfig, Leaf, NodeState, PubKey, SeqTypes, StateCatchup, ValidatedState, ViewNumber, +}; use anyhow::{bail, ensure, Context}; use async_std::sync::Arc; use async_trait::async_trait; -use committable::Committable; +use committable::{Commitment, Committable}; use hotshot::{ traits::ValidatedState as _, types::{Event, EventType}, @@ -253,8 +255,18 @@ pub trait SequencerPersistence: Sized + Send + Sync + 'static { ) -> anyhow::Result<()>; } +#[async_trait] +pub trait ChainConfigPersistence: Sized + Send + Sync + 'static { + async fn insert_chain_config(&mut self, chain_config: ChainConfig) -> anyhow::Result<()>; + async fn load_chain_config( + &self, + commitment: Commitment, + ) -> anyhow::Result; +} + #[cfg(test)] mod testing { + use super::*; #[async_trait] diff --git a/sequencer/src/persistence/sql.rs b/sequencer/src/persistence/sql.rs index 47f02b9fc5..209d1af99c 100644 --- a/sequencer/src/persistence/sql.rs +++ b/sequencer/src/persistence/sql.rs @@ -253,19 +253,25 @@ pub struct Persistence { store_undecided_state: bool, } -async fn transaction( - persistence: &mut Persistence, +pub(crate) async fn transaction( + sql: &mut SqlStorage, f: impl FnOnce(Transaction) -> BoxFuture>, ) -> anyhow::Result<()> { - let tx = persistence.db.transaction().await?; + let tx = sql.transaction().await?; match f(tx).await { Ok(_) => { - persistence.db.commit().await?; + if let Err(err) = sql.commit().await { + tracing::warn!("transaction failed, reverting: {err:#}"); + sql.revert().await; + + return Err(err.into()); + } + Ok(()) } Err(err) => { tracing::warn!("transaction failed, reverting: {err:#}"); - persistence.db.revert().await; + sql.revert().await; Err(err) } } @@ -299,7 +305,7 @@ impl SequencerPersistence for Persistence { tracing::info!("saving config to Postgres"); let json = serde_json::to_value(cfg)?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.execute_one_with_retries( "INSERT INTO network_config (config) VALUES ($1)", @@ -314,7 +320,7 @@ impl SequencerPersistence for Persistence { } async fn collect_garbage(&mut self, view: ViewNumber) -> anyhow::Result<()> { - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { let stmt1 = "DELETE FROM vid_share where view <= $1"; tx.execute(stmt1, [&(view.u64() as i64)]).await?; @@ -360,7 +366,7 @@ impl SequencerPersistence for Persistence { let leaf_bytes = bincode::serialize(leaf)?; let qc_bytes = bincode::serialize(qc)?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.execute_one_with_retries( stmt, @@ -502,7 +508,7 @@ impl SequencerPersistence for Persistence { let view = data.view_number().u64(); let data_bytes = bincode::serialize(proposal).unwrap(); - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "vid_share", @@ -525,7 +531,7 @@ impl SequencerPersistence for Persistence { let view = data.view_number().u64(); let data_bytes = bincode::serialize(proposal).unwrap(); - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "da_proposal", @@ -549,7 +555,7 @@ impl SequencerPersistence for Persistence { INSERT INTO highest_voted_view (id, view) VALUES (0, $1) ON CONFLICT (id) DO UPDATE SET view = GREATEST(highest_voted_view.view, excluded.view)"; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.execute_one_with_retries(stmt, [view.u64() as i64]) .await?; @@ -571,7 +577,7 @@ impl SequencerPersistence for Persistence { let leaves_bytes = bincode::serialize(&leaves).context("serializing leaves")?; let state_bytes = bincode::serialize(&state).context("serializing state")?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "undecided_state", @@ -596,7 +602,7 @@ impl SequencerPersistence for Persistence { ) -> anyhow::Result<()> { let view_number = proposal.data.view_number().u64(); let proposal_bytes = bincode::serialize(&proposal).context("serializing proposal")?; - transaction(self, |mut tx| { + transaction(&mut self.db, |mut tx| { async move { tx.upsert( "quorum_proposals", @@ -613,7 +619,7 @@ impl SequencerPersistence for Persistence { } } -fn sql_param(param: &T) -> &(dyn ToSql + Sync) { +pub(crate) fn sql_param(param: &T) -> &(dyn ToSql + Sync) { param } diff --git a/sequencer/src/state.rs b/sequencer/src/state.rs index 9d7844a44a..4916461cfb 100644 --- a/sequencer/src/state.rs +++ b/sequencer/src/state.rs @@ -1,6 +1,8 @@ +use crate::chain_config::BlockSize; use crate::{ - api::data_source::CatchupDataSource, catchup::SqlStateCatchup, chain_config::BlockSize, - eth_signature_key::EthKeyPair, ChainConfig, Header, Leaf, NodeState, SeqTypes, + api::data_source::CatchupDataSource, catchup::SqlStateCatchup, + chain_config::ResolvableChainConfig, eth_signature_key::EthKeyPair, genesis::UpgradeType, + persistence::ChainConfigPersistence, ChainConfig, Header, Leaf, NodeState, SeqTypes, }; use anyhow::{bail, ensure, Context}; use ark_serialize::{ @@ -74,6 +76,7 @@ pub struct ValidatedState { pub block_merkle_tree: BlockMerkleTree, /// Fee Merkle Tree pub fee_merkle_tree: FeeMerkleTree, + pub chain_config: ResolvableChainConfig, } #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] @@ -99,9 +102,13 @@ impl Default for ValidatedState { Vec::<(FeeAccount, FeeAmount)>::new(), ) .unwrap(); + + let chain_config = ResolvableChainConfig::from(ChainConfig::default()); + Self { block_merkle_tree, fee_merkle_tree, + chain_config, } } } @@ -214,6 +221,7 @@ impl ValidatedState { block_merkle_tree: BlockMerkleTree::from_commitment( self.block_merkle_tree.commitment(), ), + chain_config: ResolvableChainConfig::from(self.chain_config.commit()), } } } @@ -299,6 +307,7 @@ pub fn validate_proposal( let ValidatedState { block_merkle_tree, fee_merkle_tree, + .. } = state; let block_merkle_tree_root = block_merkle_tree.commitment(); @@ -388,6 +397,7 @@ async fn compute_state_update( instance: &NodeState, parent_leaf: &LeafQueryData, proposed_leaf: &LeafQueryData, + version: Version, ) -> anyhow::Result<(ValidatedState, Delta)> { let proposed_leaf = proposed_leaf.leaf(); let parent_leaf = parent_leaf.leaf(); @@ -408,7 +418,9 @@ async fn compute_state_update( parent_header.fee_merkle_tree_root ); - state.apply_header(instance, parent_leaf, header).await + state + .apply_header(instance, parent_leaf, header, version) + .await } async fn store_state_update( @@ -420,6 +432,7 @@ async fn store_state_update( let ValidatedState { fee_merkle_tree, block_merkle_tree, + .. } = state; let Delta { fees_delta } = delta; @@ -489,10 +502,14 @@ async fn update_state_storage( instance: &NodeState, parent_leaf: &LeafQueryData, proposed_leaf: &LeafQueryData, + version: Version, ) -> anyhow::Result { - let (state, delta) = compute_state_update(parent_state, instance, parent_leaf, proposed_leaf) - .await - .context("computing state update")?; + let parent_chain_config = parent_state.chain_config; + + let (state, delta) = + compute_state_update(parent_state, instance, parent_leaf, proposed_leaf, version) + .await + .context("computing state update")?; let mut storage = storage.write().await; if let Err(err) = store_state_update(&mut *storage, proposed_leaf.height(), &state, delta).await @@ -501,11 +518,21 @@ async fn update_state_storage( return Err(err); } + if parent_chain_config != state.chain_config { + let cf = state + .chain_config + .resolve() + .context("failed to resolve to chain config")?; + + storage.insert_chain_config(cf).await? + } + Ok(state) } async fn store_genesis_state( storage: &mut impl SequencerStateDataSource, + chain_config: ChainConfig, state: &ValidatedState, ) -> anyhow::Result<()> { ensure!( @@ -533,6 +560,8 @@ async fn store_genesis_state( .context("failed to store fee merkle nodes")?; } + storage.insert_chain_config(chain_config).await?; + storage.commit().await?; Ok(()) } @@ -540,6 +569,7 @@ async fn store_genesis_state( pub(crate) async fn update_state_storage_loop( storage: Arc>, instance: impl Future, + version: Version, ) -> anyhow::Result<()> { let mut instance = instance.await; instance.peers = Arc::new(SqlStateCatchup::from(storage.clone())); @@ -565,7 +595,13 @@ pub(crate) async fn update_state_storage_loop( // never the result of a state update and thus is not inserted in the loop below. tracing::info!("storing genesis merklized state"); let mut storage = storage.write().await; - if let Err(err) = store_genesis_state(&mut *storage, &instance.genesis_state).await { + if let Err(err) = store_genesis_state( + &mut *storage, + instance.chain_config, + &instance.genesis_state, + ) + .await + { tracing::error!("failed to store genesis state: {err:#}"); storage.revert().await; return Err(err); @@ -574,8 +610,15 @@ pub(crate) async fn update_state_storage_loop( while let Some(leaf) = leaves.next().await { loop { - match update_state_storage(&parent_state, &storage, &instance, &parent_leaf, &leaf) - .await + match update_state_storage( + &parent_state, + &storage, + &instance, + &parent_leaf, + &leaf, + version, + ) + .await { Ok(state) => { parent_leaf = leaf; @@ -603,6 +646,7 @@ pub(crate) trait SequencerStateDataSource: + UpdateStateData + UpdateStateData + MerklizedStateHeightPersistence + + ChainConfigPersistence { } @@ -615,6 +659,7 @@ impl SequencerStateDataSource for T where + UpdateStateData + UpdateStateData + MerklizedStateHeightPersistence + + ChainConfigPersistence { } @@ -624,24 +669,37 @@ impl ValidatedState { instance: &NodeState, parent_leaf: &Leaf, proposed_header: &Header, + version: Version, ) -> anyhow::Result<(Self, Delta)> { // Clone state to avoid mutation. Consumer can take update // through returned value. - let l1_deposits = get_l1_deposits(instance, proposed_header, parent_leaf).await; - let mut validated_state = self.clone(); + validated_state.apply_upgrade(instance, version); + + let chain_config = validated_state + .get_chain_config(instance, &proposed_header.chain_config) + .await?; + + if Some(chain_config) != validated_state.chain_config.resolve() { + validated_state.chain_config = chain_config.into(); + } + + let l1_deposits = get_l1_deposits( + instance, + proposed_header, + parent_leaf, + chain_config.fee_contract, + ) + .await; // Find missing fee state entries. We will need to use the builder account which is paying a // fee and the recipient account which is receiving it, plus any counts receiving deposits // in this block. let missing_accounts = self.forgotten_accounts( - [ - proposed_header.fee_info.account, - instance.chain_config.fee_recipient, - ] - .into_iter() - .chain(l1_deposits.iter().map(|fee_info| fee_info.account)), + [proposed_header.fee_info.account, chain_config.fee_recipient] + .into_iter() + .chain(l1_deposits.iter().map(|fee_info| fee_info.account)), ); let parent_height = parent_leaf.height(); @@ -703,21 +761,71 @@ impl ValidatedState { &mut validated_state, &mut delta, proposed_header.fee_info, - instance.chain_config.fee_recipient, + chain_config.fee_recipient, )?; Ok((validated_state, delta)) } + + /// Updates the `ValidatedState` if a protocol upgrade has occurred. + pub(crate) fn apply_upgrade(&mut self, instance: &NodeState, version: Version) { + // Check for protocol upgrade based on sequencer version + if version <= instance.current_version { + return; + } + + let Some(upgrade) = instance.upgrades.get(&version) else { + return; + }; + + match upgrade.upgrade_type { + UpgradeType::ChainConfig { chain_config } => { + self.chain_config = chain_config.into(); + } + } + } + + /// Retrieves the `ChainConfig`. + /// + /// Returns the `NodeState` `ChainConfig` if the `ValidatedState` `ChainConfig` commitment matches the `NodeState` `ChainConfig`` commitment. + /// If the commitments do not match, it returns the `ChainConfig` available in either `ValidatedState` or proposed header. + /// If neither has the `ChainConfig`, it fetches the config from the peers. + /// + /// Returns an error if it fails to fetch the `ChainConfig` from the peers. + pub(crate) async fn get_chain_config( + &self, + instance: &NodeState, + header_cf: &ResolvableChainConfig, + ) -> anyhow::Result { + let state_cf = self.chain_config; + + if state_cf.commit() == instance.chain_config.commit() { + return Ok(instance.chain_config); + } + + let cf = match (state_cf.resolve(), header_cf.resolve()) { + (Some(cf), _) => cf, + (_, Some(cf)) if cf.commit() == state_cf.commit() => cf, + (_, Some(_)) | (None, None) => { + instance + .peers + .as_ref() + .fetch_chain_config(state_cf.commit()) + .await + } + }; + + Ok(cf) + } } pub async fn get_l1_deposits( instance: &NodeState, header: &Header, parent_leaf: &Leaf, + fee_contract_address: Option

, ) -> Vec { - if let (Some(addr), Some(block_info)) = - (instance.chain_config.fee_contract, header.l1_finalized) - { + if let (Some(addr), Some(block_info)) = (fee_contract_address, header.l1_finalized) { instance .l1_client .get_finalized_deposits( @@ -785,7 +893,7 @@ impl HotShotState for ValidatedState { parent_leaf: &Leaf, proposed_header: &Header, vid_common: VidCommon, - _version: Version, + version: Version, ) -> Result<(Self, Self::Delta), Self::Error> { //validate builder fee if let Err(err) = validate_builder_fee(proposed_header) { @@ -796,14 +904,19 @@ impl HotShotState for ValidatedState { // Unwrapping here is okay as we retry in a loop //so we should either get a validated state or until hotshot cancels the task let (validated_state, delta) = self - .apply_header(instance, parent_leaf, proposed_header) + .apply_header(instance, parent_leaf, proposed_header, version) .await .unwrap(); + let chain_config = validated_state + .chain_config + .resolve() + .expect("Chain Config not found in validated state"); + // validate the proposal if let Err(err) = validate_proposal( &validated_state, - instance.chain_config, + chain_config, parent_leaf, proposed_header, &vid_common, @@ -840,6 +953,7 @@ impl HotShotState for ValidatedState { Self { fee_merkle_tree, block_merkle_tree, + chain_config: block_header.chain_config, } } /// Construct a genesis validated state. diff --git a/sequencer/src/transaction.rs b/sequencer/src/transaction.rs index e4ac5f4ae1..7bea5b643f 100644 --- a/sequencer/src/transaction.rs +++ b/sequencer/src/transaction.rs @@ -84,11 +84,11 @@ impl NamespaceId { impl Namespace for NamespaceId { fn max() -> Self { - Self(u32::max_value() as u64) + Self(u32::MAX as u64) } fn min() -> Self { - Self(u32::min_value() as u64) + Self(u32::MIN as u64) } }