From 3e47d75708fc2c297e669c46ff59415169af3288 Mon Sep 17 00:00:00 2001 From: akorchyn Date: Thu, 21 Dec 2023 15:34:25 +0200 Subject: [PATCH 01/11] integrated dkg again --- Cargo.lock | 1245 +++++++++++++++++++++++++++++-- Cargo.toml | 8 + flake.nix | 1 + node/Cargo.toml | 6 + node/src/benchmarking.rs | 6 +- node/src/cli.rs | 4 + node/src/runtime/testnet.rs | 12 + node/src/service/brooklyn.rs | 50 +- runtime/brooklyn/Cargo.toml | 11 + runtime/brooklyn/src/lib.rs | 117 ++- runtime/brooklyn/src/mpc.rs | 155 ++++ runtime/brooklyn/src/pos.rs | 3 +- runtime/brooklyn/src/prelude.rs | 8 +- 13 files changed, 1538 insertions(+), 88 deletions(-) create mode 100644 runtime/brooklyn/src/mpc.rs diff --git a/Cargo.lock b/Cargo.lock index e4db8dd3..95035079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -51,6 +51,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "aead" version = "0.5.2" @@ -68,7 +77,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.4.4", "cpufeatures", ] @@ -78,9 +87,9 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ - "aead", + "aead 0.5.2", "aes", - "cipher", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -331,7 +340,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "paste", "rustc_version 0.4.0", @@ -354,7 +363,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", "proc-macro2", "quote", @@ -409,7 +418,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.4", ] [[package]] @@ -729,6 +738,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + [[package]] name = "async-task" version = "4.5.0" @@ -861,6 +892,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + [[package]] name = "base16ct" version = "0.2.0" @@ -949,6 +986,38 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49f6183038e081170ebbbadee6678966c7d54728938a3e7de7f4e780770318f" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log 0.4.20", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + [[package]] name = "bindgen" version = "0.65.1" @@ -1146,7 +1215,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ - "block-padding", + "block-padding 0.1.5", "byte-tools", "byteorder", "generic-array 0.12.4", @@ -1158,6 +1227,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ + "block-padding 0.2.1", "generic-array 0.14.7", ] @@ -1179,6 +1249,12 @@ dependencies = [ "byte-tools", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "blocking" version = "1.5.1" @@ -1379,6 +1455,20 @@ dependencies = [ "semver 0.6.0", ] +[[package]] +name = "bulletproof-kzen" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1ad3c6d12dfa84b0f3d33d3aafccd81a8a493c449216cb5246b66c846900f3" +dependencies = [ + "curv-kzen", + "generic-array 0.14.7", + "itertools 0.7.11", + "serde", + "serde_derive", + "sha2 0.9.9", +] + [[package]] name = "bumpalo" version = "3.14.0" @@ -1516,6 +1606,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.83" @@ -1526,6 +1625,21 @@ dependencies = [ "libc", ] +[[package]] +name = "centipede" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c30db9451346358a37cbabe951e4dfdfb34b0c0804df4bfdfc2978cb790a512" +dependencies = [ + "bulletproof-kzen", + "curv-kzen", + "generic-array 0.14.7", + "rayon", + "serde", + "serde_derive", + "sha2 0.9.9", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -1562,6 +1676,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -1569,20 +1695,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.4.4", "cpufeatures", ] +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20 0.8.2", + "cipher 0.3.0", + "poly1305 0.7.2", + "zeroize", +] + [[package]] name = "chacha20poly1305" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", + "aead 0.5.2", + "chacha20 0.9.1", + "cipher 0.4.4", + "poly1305 0.8.0", "zeroize", ] @@ -1613,6 +1752,15 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1672,7 +1820,7 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.40", @@ -1733,7 +1881,7 @@ dependencies = [ "coins-core", "digest 0.10.7", "hmac 0.12.1", - "k256", + "k256 0.13.2", "serde", "sha2 0.10.8", "thiserror", @@ -1792,6 +1940,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "compact_str" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b5c3ee2b4ffa00ac2b00d1645cd9229ade668139bccf95f15fadcf374127b" +dependencies = [ + "castaway", + "itoa 1.0.10", + "ryu", +] + [[package]] name = "compare_fields" version = "0.2.0" @@ -2123,6 +2282,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -2166,13 +2337,19 @@ dependencies = [ "subtle", ] +[[package]] +name = "cryptoxide" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35f15e1a0699dd988fed910dd78fdc6407f44654cd12589c91fa44ea67d9159" + [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -2195,6 +2372,40 @@ dependencies = [ "traits", ] +[[package]] +name = "curv-kzen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a616b5f766fd80307f0e83de6326ccd9fe7b3ba4225fe4e12ae3a692a939d07b" +dependencies = [ + "cryptoxide", + "curve25519-dalek 3.2.0", + "digest 0.9.0", + "ff-zeroize", + "generic-array 0.14.7", + "hex", + "hmac 0.11.0", + "lazy_static", + "merkle-cbt", + "num-integer", + "num-traits", + "p256", + "pairing-plus", + "rand 0.6.5", + "rand 0.7.3", + "rust-gmp-kzen", + "secp256k1 0.20.3", + "serde", + "serde_bytes", + "serde_derive", + "sha2 0.8.2", + "sha2 0.9.9", + "sha3 0.9.1", + "thiserror", + "typenum", + "zeroize", +] + [[package]] name = "curve25519-dalek" version = "2.1.3" @@ -2437,6 +2648,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "der" version = "0.7.8" @@ -2456,7 +2677,7 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "rusticata-macros", ] @@ -2632,6 +2853,149 @@ dependencies = [ "syn 2.0.40", ] +[[package]] +name = "dkg-gadget" +version = "0.0.1" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "async-stream", + "async-trait", + "atomic", + "auto_impl", + "bincode2", + "curv-kzen", + "dkg-logging", + "dkg-mock-blockchain", + "dkg-primitives", + "dkg-runtime-primitives", + "futures 0.3.29", + "hex", + "itertools 0.10.5", + "lazy_static", + "linked-hash-map", + "multi-party-ecdsa", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "round-based", + "sc-client-api", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-network-sync", + "scale-info", + "serde", + "serde_json", + "sp-api", + "sp-application-crypto 7.0.0", + "sp-arithmetic 6.0.0", + "sp-blockchain", + "sp-consensus", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", + "strum 0.21.0", + "substrate-prometheus-endpoint", + "sync_wrapper", + "thiserror", + "tokio 1.35.0", + "tokio-stream", + "uuid 1.6.1", + "webb-proposals", + "wsts", +] + +[[package]] +name = "dkg-logging" +version = "0.1.0" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "hex", + "lazy_static", + "parking_lot 0.12.1", + "serde", + "serde_json", + "sp-core 7.0.0", + "tokio 1.35.0", + "tracing", + "tracing-filter", + "tracing-subscriber 0.3.18", +] + +[[package]] +name = "dkg-mock-blockchain" +version = "0.1.0" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "async-trait", + "atomic", + "bincode2", + "bytes 1.5.0", + "dkg-logging", + "dkg-runtime-primitives", + "futures 0.3.29", + "humantime-serde", + "log 0.4.20", + "parity-scale-codec", + "sc-client-api", + "sc-network", + "sc-utils", + "serde", + "sp-consensus", + "sp-runtime 7.0.0", + "tokio 1.35.0", + "tokio-util 0.7.10", + "toml 0.7.8", + "uuid 1.6.1", +] + +[[package]] +name = "dkg-primitives" +version = "0.0.1" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "chacha20poly1305 0.9.1", + "clap", + "curv-kzen", + "dkg-runtime-primitives", + "hex", + "libsecp256k1", + "log 0.4.20", + "multi-party-ecdsa", + "parity-scale-codec", + "rand 0.8.5", + "sc-cli", + "sc-service", + "serde_json", + "sha3 0.9.1", + "sp-core 7.0.0", + "sp-keystore 0.13.0", + "sp-runtime 7.0.0", +] + +[[package]] +name = "dkg-runtime-primitives" +version = "0.0.1" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "ethabi 18.0.0", + "ethereum", + "ethereum-types 0.14.1", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto 7.0.0", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "tiny-keccak 2.0.2", + "webb-proposals", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -2707,18 +3071,30 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + [[package]] name = "ecdsa" version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der", + "der 0.7.8", "digest 0.10.7", - "elliptic-curve", - "rfc6979", + "elliptic-curve 0.13.8", + "rfc6979 0.4.0", "signature 2.2.0", - "spki", + "spki 0.7.3", ] [[package]] @@ -2736,7 +3112,7 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8", + "pkcs8 0.10.2", "signature 2.2.0", ] @@ -2788,21 +3164,41 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + [[package]] name = "elliptic-curve" version = "0.13.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ - "base16ct", - "crypto-bigint", + "base16ct 0.2.0", + "crypto-bigint 0.5.5", "digest 0.10.7", - "ff", + "ff 0.13.0", "generic-array 0.14.7", - "group", - "pkcs8", + "group 0.13.0", + "pkcs8 0.10.2", "rand_core 0.6.4", - "sec1", + "sec1 0.7.3", "subtle", "zeroize", ] @@ -2834,7 +3230,7 @@ dependencies = [ "base64 0.21.5", "bytes 1.5.0", "hex", - "k256", + "k256 0.13.2", "log 0.4.20", "rand 0.8.5", "rlp", @@ -2849,7 +3245,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", @@ -2981,7 +3377,7 @@ dependencies = [ "sha2 0.10.8", "sha3 0.10.8", "thiserror", - "uuid", + "uuid 0.8.2", ] [[package]] @@ -3059,7 +3455,7 @@ dependencies = [ "ethereum_hashing", "hex", "lazy_static", - "num-bigint", + "num-bigint 0.4.4", "serde", "serde_derive", "serde_yaml", @@ -3328,10 +3724,10 @@ dependencies = [ "cargo_metadata 0.18.1", "chrono", "const-hex", - "elliptic-curve", + "elliptic-curve 0.13.8", "ethabi 18.0.0", "generic-array 0.14.7", - "k256", + "k256 0.13.2", "num_enum 0.7.1", "once_cell", "open-fastrlp", @@ -3438,7 +3834,7 @@ dependencies = [ "coins-bip32", "coins-bip39", "const-hex", - "elliptic-curve", + "elliptic-curve 0.13.8", "eth-keystore", "ethers-core", "rand 0.8.5", @@ -3832,13 +4228,49 @@ dependencies = [ ] [[package]] -name = "ff" -version = "0.13.0" +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff-zeroize" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02169a2e8515aa316ce516eaaf6318a76617839fbf904073284bc2576b029ee" +dependencies = [ + "byteorder", + "ff_derive-zeroize", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "ff_derive-zeroize" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +checksum = "b24d4059bc0d0a0bf26b740aa21af1f96a984f0ab7a21356d00b32475388b53a" dependencies = [ - "rand_core 0.6.4", - "subtle", + "num-bigint 0.2.6", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -4257,7 +4689,7 @@ dependencies = [ "frame-metadata 15.1.0", "frame-support-procedural", "impl-trait-for-tuples", - "k256", + "k256 0.13.2", "log 0.4.20", "once_cell", "parity-scale-codec", @@ -4657,6 +5089,8 @@ dependencies = [ "bitcoin 1.2.0", "btc-relay-rpc", "clap", + "dkg-gadget", + "dkg-primitives", "env_logger 0.10.1", "eth-types", "ethers", @@ -4688,7 +5122,7 @@ dependencies = [ "mmr-gadget", "mmr-rpc", "nix 0.26.4", - "num-bigint", + "num-bigint 0.4.4", "oracle-rpc", "pallet-balances", "pallet-eth2-light-client-relayer-gadget", @@ -4761,6 +5195,7 @@ dependencies = [ "btc-relay-rpc-runtime-api", "clients-info", "currency", + "dkg-runtime-primitives", "escrow", "fee", "fp-evm", @@ -4799,6 +5234,9 @@ dependencies = [ "pallet-contracts", "pallet-contracts-primitives", "pallet-conviction-voting", + "pallet-dkg-metadata", + "pallet-dkg-proposal-handler", + "pallet-dkg-proposals", "pallet-dynamic-fee", "pallet-election-provider-multi-phase", "pallet-eth2-light-client", @@ -5067,13 +5505,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.0", "rand_core 0.6.4", "subtle", ] @@ -5250,6 +5699,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -5276,6 +5734,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -5440,6 +5901,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "0.10.16" @@ -6001,6 +6472,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "issue" version = "1.2.0" @@ -6057,6 +6534,15 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "itertools" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.5" @@ -6208,7 +6694,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44e8ab85614a08792b9bff6c8feee23be78c98d0182d4c622c05256ab553892a" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-crate 1.1.3", "proc-macro2", "quote", @@ -6277,6 +6763,18 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + [[package]] name = "k256" version = "0.13.2" @@ -6284,8 +6782,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" dependencies = [ "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", "once_cell", "sha2 0.10.8", "signature 2.2.0", @@ -6353,6 +6851,17 @@ dependencies = [ "smallvec 1.11.2", ] +[[package]] +name = "kzen-paillier" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b7e469348f203838b2ed1d46f068ba63162e7a1e63be868763a80bbd5d046f" +dependencies = [ + "curv-kzen", + "rayon", + "serde", +] + [[package]] name = "lalrpop" version = "0.20.0" @@ -6751,7 +7260,7 @@ version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" dependencies = [ - "heck", + "heck 0.4.1", "quote", "syn 1.0.109", ] @@ -6854,7 +7363,7 @@ version = "0.11.0+8.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3386f101bcb4bd252d8e9d2fb41ec3b0862a15a62b478c355b2982efa469e3e" dependencies = [ - "bindgen", + "bindgen 0.65.1", "bzip2-sys", "cc", "glob", @@ -7137,6 +7646,15 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matches" version = "0.1.10" @@ -7263,6 +7781,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "merkle-cbt" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171d2f700835121c3b04ccf0880882987a050fd5c7ae88148abf537d33dd3a56" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "merkle-generator" version = "0.1.0" @@ -7331,6 +7858,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "miette" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c" +dependencies = [ + "atty", + "backtrace", + "miette-derive", + "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "milagro_bls" version = "1.5.0" @@ -7509,6 +8067,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "multi-party-ecdsa" +version = "0.8.2" +source = "git+https://github.com/webb-tools/multi-party-ecdsa.git#a9a666d7debc1fc416d835d0b99ed071da76ab9f" +dependencies = [ + "centipede", + "curv-kzen", + "derivative", + "kzen-paillier", + "log 0.4.20", + "round-based", + "serde", + "sha2 0.9.9", + "subtle", + "thiserror", + "zeroize", + "zk-paillier", +] + [[package]] name = "multiaddr" version = "0.17.1" @@ -7845,13 +8422,23 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-complex", "num-integer", "num-iter", @@ -7859,6 +8446,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -7929,7 +8527,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg 1.1.0", - "num-bigint", + "num-bigint 0.4.4", "num-integer", "num-traits", ] @@ -8319,6 +8917,12 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owning_ref" version = "0.4.1" @@ -8328,6 +8932,61 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.8", +] + +[[package]] +name = "p256k1" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e81c2cb5a1936d3f26278f9d698932239d03ddf0d5818392d91cd5f98ffc79" +dependencies = [ + "bindgen 0.64.0", + "bitvec", + "bs58 0.4.0", + "cc", + "hex", + "itertools 0.10.5", + "num-traits", + "primitive-types", + "proc-macro2", + "quote", + "rand_core 0.6.4", + "rustfmt-wrapper", + "serde", + "sha2 0.10.8", + "syn 2.0.40", +] + +[[package]] +name = "pairing-plus" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cda4f22e8e6720f3c254049960c8cc4f93cb82b5ade43bddd2622b5f39ea62" +dependencies = [ + "byteorder", + "digest 0.8.1", + "ff-zeroize", + "rand 0.4.6", + "rand_core 0.5.1", + "rand_xorshift 0.2.0", + "zeroize", +] + [[package]] name = "pallet-assets" version = "4.0.0-dev" @@ -8570,6 +9229,71 @@ dependencies = [ "sp-std 5.0.0", ] +[[package]] +name = "pallet-dkg-metadata" +version = "0.2.0" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "dkg-runtime-primitives", + "frame-support", + "frame-system", + "hex", + "libsecp256k1", + "log 0.4.20", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-std 5.0.0", + "webb-proposals", +] + +[[package]] +name = "pallet-dkg-proposal-handler" +version = "0.1.0" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "dkg-runtime-primitives", + "ethabi 18.0.0", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-dkg-metadata", + "parity-scale-codec", + "scale-info", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-staking", + "sp-std 5.0.0", + "webb-proposals", +] + +[[package]] +name = "pallet-dkg-proposals" +version = "1.0.0" +source = "git+https://github.com/ggxchain/dkg-substrate.git?branch=polkadot-v0.9.43#a09f56758f6d92407e62c08cfe1a6810ed3666df" +dependencies = [ + "dkg-runtime-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "k256 0.11.6", + "log 0.4.20", + "pallet-balances", + "pallet-dkg-metadata", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core 7.0.0", + "sp-io 7.0.0", + "sp-runtime 7.0.0", + "sp-staking", + "sp-std 5.0.0", +] + [[package]] name = "pallet-dynamic-fee" version = "4.0.0-dev" @@ -8954,7 +9678,7 @@ dependencies = [ "hex-literal 0.3.4", "libsecp256k1", "log 0.4.20", - "num-bigint", + "num-bigint 0.4.4", "num_enum 0.5.11", "pallet-balances", "pallet-evm", @@ -9986,14 +10710,24 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + [[package]] name = "pkcs8" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der", - "spki", + "der 0.7.8", + "spki 0.7.3", ] [[package]] @@ -10131,6 +10865,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -10139,7 +10884,17 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", +] + +[[package]] +name = "polynomial" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27abb6e4638dcecc65a92b50d7f1d87dd6dea987ba71db987b6bf881f4877e9d" +dependencies = [ + "num-traits", + "serde", ] [[package]] @@ -10151,7 +10906,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "opaque-debug 0.3.0", - "universal-hash", + "universal-hash 0.5.1", ] [[package]] @@ -10455,7 +11210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes 1.5.0", - "heck", + "heck 0.4.1", "itertools 0.10.5", "lazy_static", "log 0.4.20", @@ -10779,16 +11534,25 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", ] [[package]] name = "rand_xorshift" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ - "rand_core 0.3.1", + "rand_core 0.5.1", ] [[package]] @@ -11170,6 +11934,17 @@ dependencies = [ "sp-api", ] +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac 0.12.1", + "zeroize", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -11256,6 +12031,19 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "round-based" +version = "0.1.7" +source = "git+https://github.com/webb-tools/round-based-protocol#959126f9f6edce16d4ee95954091b93e33a83140" +dependencies = [ + "async-stream", + "futures 0.3.29", + "log 0.4.20", + "serde", + "thiserror", + "tokio 1.35.0", +] + [[package]] name = "rpassword" version = "7.3.1" @@ -11369,6 +12157,17 @@ dependencies = [ "smallvec 1.11.2", ] +[[package]] +name = "rust-gmp-kzen" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e654bb304958a567aefa09e83cc313251388202c40bfc245fac19a0e2dd8d08" +dependencies = [ + "libc", + "num-traits", + "serde", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -11411,6 +12210,19 @@ dependencies = [ "semver 1.0.20", ] +[[package]] +name = "rustfmt-wrapper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1adc9dfed5cc999077978cc7163b9282c5751c8d39827c4ea8c8c220ca5a440" +dependencies = [ + "serde", + "tempfile", + "thiserror", + "toml 0.8.8", + "toolchain_find", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -11644,7 +12456,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -11891,7 +12703,7 @@ dependencies = [ "fork-tree", "futures 0.3.29", "log 0.4.20", - "num-bigint", + "num-bigint 0.4.4", "num-rational", "num-traits", "parity-scale-codec", @@ -12723,8 +13535,8 @@ dependencies = [ "sp-tracing 6.0.0", "thiserror", "tracing", - "tracing-log", - "tracing-subscriber", + "tracing-log 0.1.4", + "tracing-subscriber 0.2.25", ] [[package]] @@ -13101,16 +13913,30 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + [[package]] name = "sec1" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "base16ct", - "der", + "base16ct 0.2.0", + "der 0.7.8", "generic-array 0.14.7", - "pkcs8", + "pkcs8 0.10.2", "subtle", "zeroize", ] @@ -13123,6 +13949,17 @@ dependencies = [ "secp256k1-sys 0.4.0", ] +[[package]] +name = "secp256k1" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" +dependencies = [ + "rand 0.6.5", + "secp256k1-sys 0.4.2", + "serde", +] + [[package]] name = "secp256k1" version = "0.24.3" @@ -13150,6 +13987,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957da2573cde917463ece3570eab4a0b3f19de6f1646cde62e6fd3868f566036" +dependencies = [ + "cc", +] + [[package]] name = "secp256k1-sys" version = "0.6.1" @@ -13477,6 +14323,18 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.0", +] + [[package]] name = "sha3" version = "0.10.8" @@ -13516,6 +14374,10 @@ name = "signature" version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] [[package]] name = "signature" @@ -13546,7 +14408,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "num-bigint", + "num-bigint 0.4.4", "num-traits", "thiserror", "time 0.3.17", @@ -13612,6 +14474,12 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + [[package]] name = "smol" version = "1.3.0" @@ -13667,7 +14535,7 @@ dependencies = [ "merlin 3.0.0", "no-std-net", "nom", - "num-bigint", + "num-bigint 0.4.4", "num-rational", "num-traits", "pbkdf2 0.12.2", @@ -13733,7 +14601,7 @@ checksum = "58021967fd0a5eeeb23b08df6cc244a4d4a5b4aec1d27c9e02fad1a58b4cd74e" dependencies = [ "aes-gcm", "blake2", - "chacha20poly1305", + "chacha20poly1305 0.10.1", "curve25519-dalek 4.1.1", "rand_core 0.6.4", "ring 0.17.7", @@ -14962,7 +15830,7 @@ dependencies = [ "sp-std 5.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -14975,7 +15843,7 @@ dependencies = [ "sp-std 6.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -14988,7 +15856,7 @@ dependencies = [ "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -15223,6 +16091,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + [[package]] name = "spki" version = "0.7.3" @@ -15230,7 +16108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.8", ] [[package]] @@ -15379,6 +16257,15 @@ dependencies = [ "syn 2.0.40", ] +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +dependencies = [ + "strum_macros 0.21.1", +] + [[package]] name = "strum" version = "0.24.1" @@ -15397,13 +16284,25 @@ dependencies = [ "strum_macros 0.25.3", ] +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "strum_macros" version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -15416,7 +16315,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -15666,7 +16565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e2f231d97c145c564bd544212c0cc0c29c09ff516af199f4ce00c8e055f8138" dependencies = [ "frame-metadata 15.1.0", - "heck", + "heck 0.4.1", "hex", "jsonrpsee", "parity-scale-codec", @@ -15685,7 +16584,7 @@ version = "0.31.0" source = "git+https://github.com/paritytech/subxt?tag=v0.31.0#059723e4313d91e8ca0bcd84b0dd7dd66686ca50" dependencies = [ "frame-metadata 16.0.0", - "heck", + "heck 0.4.1", "hex", "jsonrpsee", "parity-scale-codec", @@ -15797,6 +16696,34 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "supports-color" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f" +dependencies = [ + "atty", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406" +dependencies = [ + "atty", +] + +[[package]] +name = "supports-unicode" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2" +dependencies = [ + "atty", +] + [[package]] name = "svm-rs" version = "0.3.3" @@ -15848,6 +16775,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" @@ -15985,6 +16918,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "termtree" version = "0.4.1" @@ -16012,6 +16955,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "textwrap" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.50" @@ -16653,6 +17607,19 @@ dependencies = [ "winnow", ] +[[package]] +name = "toolchain_find" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc8c9a7f0a2966e1acdaf0461023d0b01471eeead645370cf4c3f5cff153f2a" +dependencies = [ + "home", + "once_cell", + "regex", + "semver 1.0.20", + "walkdir", +] + [[package]] name = "tower" version = "0.4.13" @@ -16727,6 +17694,24 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-filter" +version = "0.1.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558d46b3870ac7328dbe682541d286b46588b7430c08484b821db23c2ee799a8" +dependencies = [ + "compact_str", + "matchers 0.1.0", + "miette", + "once_cell", + "regex", + "thiserror", + "thread_local", + "tracing", + "tracing-core", + "tracing-subscriber 0.3.18", +] + [[package]] name = "tracing-futures" version = "0.2.5" @@ -16748,6 +17733,17 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log 0.4.20", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -16767,7 +17763,7 @@ dependencies = [ "ansi_term", "chrono", "lazy_static", - "matchers", + "matchers 0.0.1", "parking_lot 0.11.2", "regex", "serde", @@ -16777,7 +17773,28 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.1.4", + "tracing-serde", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers 0.1.0", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec 1.11.2", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.2.0", "tracing-serde", ] @@ -16797,7 +17814,7 @@ dependencies = [ "impl-trait-for-tuples", "interbtc-primitives", "log 0.4.20", - "num-bigint", + "num-bigint 0.4.4", "num-traits", "parity-scale-codec", "scale-info", @@ -17175,6 +18192,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -17184,6 +18207,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.11" @@ -17196,6 +18225,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + [[package]] name = "universal-hash" version = "0.5.1" @@ -17280,6 +18319,16 @@ dependencies = [ "serde", ] +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom 0.2.11", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" @@ -18739,6 +19788,25 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wsts" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08b241e8440197fcd6e214a26fcaac82d7cdfde3ae964586f4b1a7c41a21ba6f" +dependencies = [ + "bs58 0.5.0", + "hashbrown 0.14.3", + "hex", + "num-traits", + "p256k1", + "polynomial", + "primitive-types", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "wyz" version = "0.5.1" @@ -18967,6 +20035,23 @@ dependencies = [ "zstd 0.11.2+zstd.1.5.2", ] +[[package]] +name = "zk-paillier" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "072972ea8d7c9829c9d87492ac021cf4b665b90c6c9b58fbeb6f71d036e66a04" +dependencies = [ + "bit-vec", + "curv-kzen", + "digest 0.9.0", + "kzen-paillier", + "rand 0.6.5", + "rayon", + "serde", + "sha2 0.9.9", + "thiserror", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/Cargo.toml b/Cargo.toml index cc8151dd..e33b6544 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -294,6 +294,14 @@ replace-rpc-runtime-api = { git = "https://github.com/ggxchain/interbtc.git", br reward-rpc-runtime-api = { git = "https://github.com/ggxchain/interbtc.git", branch = "polkadot-v0.9.43", default-features = false } vault-registry-rpc-runtime-api = { git = "https://github.com/ggxchain/interbtc.git", branch = "polkadot-v0.9.43", default-features = false } +# DKG substrate +dkg-gadget = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43" } +dkg-primitives = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43" } +dkg-runtime-primitives = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43", default-features = false } +pallet-dkg-metadata = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43", default-features = false } +pallet-dkg-proposal-handler = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43", default-features = false } +pallet-dkg-proposals = { git = "https://github.com/ggxchain/dkg-substrate.git", branch = "polkadot-v0.9.43", default-features = false } + # [patch."https://github.com/ggxchain/pallet-eth2-light-client.git"] # webb-consensus-types = { path = "../pallet-eth2-light-client/crates/consensus-types/" } # pallet-eth2-light-client = { path = "../pallet-eth2-light-client/pallets/eth2-light-client/" } diff --git a/flake.nix b/flake.nix index ff48c88a..0ffdb010 100644 --- a/flake.nix +++ b/flake.nix @@ -391,6 +391,7 @@ nodePackages.markdownlint-cli2 jq awscli2 + gmp ]; inputsFrom = [ diff --git a/node/Cargo.toml b/node/Cargo.toml index e8934ba7..197c4dbf 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -100,6 +100,10 @@ replace-rpc.workspace = true reward-rpc.workspace = true vault-registry-rpc.workspace = true +# DKG +dkg-gadget = { workspace = true, optional = true } +dkg-primitives = { workspace = true, optional = true } + [dev-dependencies] alloy-rlp = { workspace = true } assert_cmd = { workspace = true } @@ -133,6 +137,8 @@ default = ["brooklyn"] sydney = ["fast-runtime", "ggxchain-runtime-sydney"] brooklyn = [ "ggxchain-runtime-brooklyn", + "dkg-gadget", + "dkg-primitives", ] fast-runtime = [ "ggxchain-runtime-sydney?/fast-runtime", diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs index 072f5626..636179ee 100644 --- a/node/src/benchmarking.rs +++ b/node/src/benchmarking.rs @@ -149,7 +149,11 @@ pub fn create_benchmark_extrinsic( )), frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(0), + ( + pallet_transaction_payment::ChargeTransactionPayment::::from(0), + #[cfg(feature = "allowlist")] + account_filter::account_filter::AllowAccount::new(), + ), ); let raw_payload = runtime::SignedPayload::from_raw( diff --git a/node/src/cli.rs b/node/src/cli.rs index a9da5bfe..d5b09a73 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -54,6 +54,10 @@ pub struct Cli { #[clap(flatten)] pub relayer_cmd: pallet_eth2_light_client_relayer_gadget_cli::LightClientRelayerCmd, + + #[cfg(feature = "brooklyn")] + #[arg(long, short = 'o')] + pub output_path: Option, } #[derive(Debug, clap::Subcommand)] diff --git a/node/src/runtime/testnet.rs b/node/src/runtime/testnet.rs index 9cec2aed..83398694 100644 --- a/node/src/runtime/testnet.rs +++ b/node/src/runtime/testnet.rs @@ -30,6 +30,7 @@ impl ValidatorIdentity { grandpa: get_from_seed::(s), im_online: get_from_seed::(s), beefy: get_from_seed::(s), + dkg: get_from_seed::(s), }, } } @@ -50,6 +51,7 @@ impl ValidatorIdentity { grandpa: ed, im_online: sr.into(), beefy: ecdsa.into(), + dkg: ecdsa.into(), }, } } @@ -334,5 +336,15 @@ pub fn testnet_genesis( max_exchange_rate: Rate::from_inner(loans::DEFAULT_MAX_EXCHANGE_RATE), min_exchange_rate: Rate::from_inner(loans::DEFAULT_MIN_EXCHANGE_RATE), }, + dkg: DKGConfig { + authorities: initial_authorities + .iter() + .map(|x| x.session_keys.dkg.clone()) + .collect::<_>(), + keygen_threshold: 2, + signature_threshold: 1, + authority_ids: initial_authorities.into_iter().map(|x| x.id).collect::<_>(), + }, + dkg_proposals: Default::default(), } } diff --git a/node/src/service/brooklyn.rs b/node/src/service/brooklyn.rs index 2384d538..5adf94ff 100644 --- a/node/src/service/brooklyn.rs +++ b/node/src/service/brooklyn.rs @@ -19,7 +19,7 @@ use sc_client_api::{ backend::{Backend, StateBackend}, AuxStore, BlockchainEvents, StorageProvider, }; -use sc_network::NetworkService; +use sc_network::{NetworkService, NetworkStateInfo}; use sc_network_sync::SyncingService; use sc_rpc::SubscriptionTaskExecutor; use sc_service::{error::Error as ServiceError, Configuration, TaskManager, TransactionPool}; @@ -245,6 +245,7 @@ pub fn new_full(mut config: Configuration, cli: &Cli) -> Result Result Result Result, + debug_logger, + }; + + // Start the DKG gadget. + task_manager.spawn_essential_handle().spawn_blocking( + "dkg-gadget", + None, + dkg_gadget::start_dkg_gadget::<_, _, _>(dkg_params), + ); + } + // if the node isn't actively participating in consensus then it doesn't // need a keystore, regardless of which protocol we use below. let keystore_opt = if role.is_authority() { diff --git a/runtime/brooklyn/Cargo.toml b/runtime/brooklyn/Cargo.toml index 6e216dc8..cf12d825 100644 --- a/runtime/brooklyn/Cargo.toml +++ b/runtime/brooklyn/Cargo.toml @@ -151,6 +151,11 @@ replace-rpc-runtime-api.workspace = true reward-rpc-runtime-api.workspace = true vault-registry-rpc-runtime-api.workspace = true +dkg-runtime-primitives.workspace = true +pallet-dkg-metadata.workspace = true +pallet-dkg-proposal-handler.workspace = true +pallet-dkg-proposals.workspace = true + [build-dependencies] substrate-wasm-builder.workspace = true @@ -282,6 +287,12 @@ std = [ "replace-rpc-runtime-api/std", "reward-rpc-runtime-api/std", "vault-registry-rpc-runtime-api/std", + + # DKG + "pallet-dkg-metadata/std", + "pallet-dkg-proposal-handler/std", + "pallet-dkg-proposals/std", + "dkg-runtime-primitives/std", ] aura = [] allowlist = [] diff --git a/runtime/brooklyn/src/lib.rs b/runtime/brooklyn/src/lib.rs index 0b56686e..2dab9f59 100644 --- a/runtime/brooklyn/src/lib.rs +++ b/runtime/brooklyn/src/lib.rs @@ -21,10 +21,12 @@ pub mod governance; mod ibc; mod ink; pub mod light_client; +pub mod mpc; pub mod pos; mod prelude; - mod version; + +pub use mpc::DKGId; pub use version::VERSION; use core::cmp::Ordering; @@ -156,12 +158,15 @@ pub mod opaque { /// Opaque block identifier type. pub type BlockId = generic::BlockId; + // TODO: create migration + impl_opaque_keys! { pub struct SessionKeys { pub aura: Aura, pub grandpa: Grandpa, pub im_online: ImOnline, pub beefy: Beefy, + pub dkg: DKG, } } } @@ -192,6 +197,7 @@ pub type OptionalSignedExtension = ( pub type OptionalSignedExtension = (pallet_transaction_payment::ChargeTransactionPayment,); pub type SignedExtra = ( + frame_system::CheckNonZeroSender, frame_system::CheckSpecVersion, frame_system::CheckTxVersion, frame_system::CheckGenesis, @@ -357,9 +363,7 @@ impl frame_system::Config for Runtime { type MaxConsumers = ConstU32<16>; } -parameter_types! { - pub const MaxAuthorities: u32 = 100; -} +pub type MaxAuthorities = dkg_runtime_primitives::CustomU32Getter<1024>; impl pallet_aura::Config for Runtime { type AuthorityId = AuraId; @@ -602,6 +606,12 @@ construct_runtime!( System: frame_system, Timestamp: pallet_timestamp, RuntimeSpecification: chain_spec, + + // DKG / offchain worker - the order and position of these pallet should not change + DKG: pallet_dkg_metadata, + DKGProposals: pallet_dkg_proposals, + DKGProposalHandler: pallet_dkg_proposal_handler, + Balances: pallet_balances, Aura: pallet_aura, ImOnline: pallet_im_online, @@ -823,6 +833,105 @@ impl_runtime_apis! { } } + impl dkg_runtime_primitives::DKGApi for Runtime { + fn authority_set() -> dkg_runtime_primitives::AuthoritySet { + let authorities = DKG::authorities(); + let authority_set_id = DKG::authority_set_id(); + + dkg_runtime_primitives::AuthoritySet { + authorities, + id: authority_set_id + } + } + + fn queued_authority_set() -> dkg_runtime_primitives::AuthoritySet { + let queued_authorities = DKG::next_authorities(); + let queued_authority_set_id = DKG::authority_set_id() + 1u64; + + dkg_runtime_primitives::AuthoritySet { + authorities: queued_authorities, + id: queued_authority_set_id + } + } + + fn signature_threshold() -> u16 { + DKG::signature_threshold() + } + + fn keygen_threshold() -> u16 { + DKG::keygen_threshold() + } + + fn next_signature_threshold() -> u16 { + DKG::next_signature_threshold() + } + + fn next_keygen_threshold() -> u16 { + DKG::next_keygen_threshold() + } + + fn should_refresh(block_number: BlockNumber) -> bool { + DKG::should_refresh(block_number) + } + + fn next_dkg_pub_key() -> Option<(dkg_runtime_primitives::AuthoritySetId, Vec)> { + DKG::next_dkg_public_key().map(|pub_key| (pub_key.0, pub_key.1.into())) + } + + fn next_pub_key_sig() -> Option> { + DKG::next_public_key_signature().map(|pub_key_sig| pub_key_sig.into()) + } + + fn get_current_session_progress(block_number: BlockNumber) -> Option { + use frame_support::traits::EstimateNextSessionRotation; + as EstimateNextSessionRotation>::estimate_current_session_progress(block_number).0 + } + + fn dkg_pub_key() -> (dkg_runtime_primitives::AuthoritySetId, Vec) { + (DKG::dkg_public_key().0, DKG::dkg_public_key().1.into()) + } + + fn get_best_authorities() -> Vec<(u16, mpc::DKGId)> { + DKG::best_authorities().into() + } + + fn get_next_best_authorities() -> Vec<(u16, mpc::DKGId)> { + DKG::next_best_authorities().into() + } + + fn get_unsigned_proposal_batches() -> Vec> { + DKGProposalHandler::get_unsigned_proposal_batches() + } + + fn get_authority_accounts() -> (Vec, Vec) { + (DKG::current_authorities_accounts().into(), DKG::next_authorities_accounts().into()) + } + + fn get_reputations(authorities: Vec) -> Vec<(mpc::DKGId, mpc::Reputation)> { + authorities.iter().map(|a| (a.clone(), DKG::authority_reputations(a))).collect() + } + + fn get_keygen_jailed(set: Vec) -> Vec { + set.iter().filter(|a| pallet_dkg_metadata::JailedKeygenAuthorities::::contains_key(a)).cloned().collect() + } + + fn get_signing_jailed(set: Vec) -> Vec { + set.iter().filter(|a| pallet_dkg_metadata::JailedSigningAuthorities::::contains_key(a)).cloned().collect() + } + + fn refresh_nonce() -> u32 { + DKG::refresh_nonce() + } + + fn should_execute_new_keygen() -> (bool, bool) { + DKG::should_execute_new_keygen() + } + + fn should_submit_proposer_vote() -> bool { + DKG::should_submit_proposer_vote() + } + } + impl sp_transaction_pool::runtime_api::TaggedTransactionQueue for Runtime { fn validate_transaction( source: TransactionSource, diff --git a/runtime/brooklyn/src/mpc.rs b/runtime/brooklyn/src/mpc.rs new file mode 100644 index 00000000..1f71d5d9 --- /dev/null +++ b/runtime/brooklyn/src/mpc.rs @@ -0,0 +1,155 @@ +use crate::{ + pos::{PeriodicSessions, SessionPeriod}, + prelude::*, + SignedPayload, +}; + +use super::{DKGProposalHandler, DKGProposals, Historical, Offences, DKG}; + +use dkg_runtime_primitives::{ + MaxKeyLength, MaxProposalLength, MaxReporters, MaxSignatureLength, TypedChainId, +}; + +pub use dkg_runtime_primitives::crypto::AuthorityId as DKGId; +use frame_support::PalletId; + +parameter_types! { + pub const DecayPercentage: Percent = Percent::from_percent(50); + pub const UnsignedPriority: u64 = 1 << 20; + pub const UnsignedInterval: BlockNumber = 1; + #[derive(Default, Clone, Encode, Decode, Debug, Eq, PartialEq, scale_info::TypeInfo, Ord, PartialOrd, scale_codec::MaxEncodedLen)] + pub const VoteLength: u32 = 64; +} + +pub type Reputation = u128; + +impl pallet_dkg_metadata::Config for Runtime { + type DKGId = DKGId; + type RuntimeEvent = RuntimeEvent; + type OnAuthoritySetChangeHandler = DKGProposals; + type OnDKGPublicKeyChangeHandler = (); + type OffChainAuthId = dkg_runtime_primitives::offchain::crypto::OffchainAuthId; + type NextSessionRotation = PeriodicSessions; + type KeygenJailSentence = SessionPeriod; + type SigningJailSentence = SessionPeriod; + type DecayPercentage = DecayPercentage; + type Reputation = Reputation; + type ForceOrigin = EnsureRoot; + type UnsignedPriority = UnsignedPriority; + type SessionPeriod = SessionPeriod; + type UnsignedInterval = UnsignedInterval; + type AuthorityIdOf = pallet_dkg_metadata::AuthorityIdOf; + type ProposalHandler = DKGProposalHandler; + type MaxKeyLength = MaxKeyLength; + type MaxSignatureLength = MaxSignatureLength; + type DKGAuthorityToMerkleLeaf = pallet_dkg_proposals::DKGEcdsaToEthereumAddress; + type MaxReporters = MaxReporters; + type MaxAuthorities = MaxAuthorities; + type VoteLength = VoteLength; + type MaxProposalLength = MaxProposalLength; + type WeightInfo = pallet_dkg_metadata::weights::WebbWeight; +} + +parameter_types! { + pub const ChainIdentifier: TypedChainId = TypedChainId::Substrate(888866); + pub storage ProposalLifetime: BlockNumber = Hours::get() / 5; + pub const DKGAccountId: PalletId = PalletId(*b"dw/dkgac"); + pub const RefreshDelay: Permill = Permill::from_percent(90); + pub const TimeToRestart: BlockNumber = 3; + pub storage UnsignedProposalExpiry: BlockNumber = SessionPeriod::get() / 4; +} + +impl pallet_dkg_proposal_handler::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ForceOrigin = EnsureRoot; + type OffChainAuthId = dkg_runtime_primitives::offchain::crypto::OffchainAuthId; + type UnsignedProposalExpiry = UnsignedProposalExpiry; + type SignedProposalHandler = (DKG,); + type MaxProposalsPerBatch = dkg_runtime_primitives::CustomU32Getter<10>; + type BatchId = u32; + type ValidatorSet = Historical; + type ReportOffences = Offences; + type WeightInfo = pallet_dkg_proposal_handler::weights::WebbWeight; +} + +parameter_types! { + #[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, scale_info::TypeInfo, Ord, PartialOrd)] + pub const MaxVotes : u32 = 100; + #[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, scale_info::TypeInfo, Ord, PartialOrd, Serialize, Deserialize)] + pub const MaxResources : u32 = 1000; + #[derive(Clone, Encode, Decode, Debug, Eq, PartialEq, scale_info::TypeInfo, Ord, PartialOrd)] + pub const MaxProposers : u32 = 1000; +} + +impl pallet_dkg_proposals::Config for Runtime { + type AdminOrigin = frame_system::EnsureRoot; + type DKGAuthorityToMerkleLeaf = pallet_dkg_proposals::DKGEcdsaToEthereumAddress; + type DKGId = DKGId; + type ChainIdentifier = ChainIdentifier; + type RuntimeEvent = RuntimeEvent; + type NextSessionRotation = PeriodicSessions; + type MaxProposalLength = MaxProposalLength; + type ProposalLifetime = ProposalLifetime; + type ProposalHandler = DKGProposalHandler; + type Period = SessionPeriod; + type MaxVotes = MaxVotes; + type MaxResources = MaxResources; + type MaxProposers = MaxProposers; + type VotingKeySize = MaxKeyLength; + type WeightInfo = pallet_dkg_proposals::WebbWeight; +} + +impl frame_system::offchain::CreateSignedTransaction for Runtime +where + RuntimeCall: From, +{ + fn create_transaction>( + call: RuntimeCall, + public: ::Signer, + account: AccountId, + nonce: u32, + ) -> Option<( + RuntimeCall, + ::SignaturePayload, + )> { + use sp_runtime::traits::StaticLookup; + + let tip = 0; + // take the biggest period possible. + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + let current_block = System::block_number() + // The `System::block_number` is initialized with `n+1`, + // so the actual block number is `n`. + .saturating_sub(1); + let era = sp_runtime::generic::Era::mortal(period, current_block.into()); + let extra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckEra::::from(era), + frame_system::CheckNonce::::from(nonce), + frame_system::CheckWeight::::new(), + (pallet_transaction_payment::ChargeTransactionPayment::< + Runtime, + >::from(tip),), + ); + let raw_payload = SignedPayload::new(call, extra) + .map_err(|e| { + log::warn!("Unable to create signed payload: {:?}", e); + }) + .ok()?; + let signature = raw_payload.using_encoded(|payload| C::sign(payload, public))?; + let address = crate::Indices::unlookup(account); + let (call, extra, _) = raw_payload.deconstruct(); + Some((call, (address, signature, extra))) + } +} + +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} diff --git a/runtime/brooklyn/src/pos.rs b/runtime/brooklyn/src/pos.rs index b87a18e4..18cbc94c 100644 --- a/runtime/brooklyn/src/pos.rs +++ b/runtime/brooklyn/src/pos.rs @@ -111,7 +111,8 @@ parameter_types! { } -pub type PeriodicSessions = pallet_session::PeriodicSessions; +pub type PeriodicSessions = + pallet_dkg_metadata::DKGPeriodicSessions; impl pallet_treasury::Config for Runtime { type PalletId = TreasuryPalletId; diff --git a/runtime/brooklyn/src/prelude.rs b/runtime/brooklyn/src/prelude.rs index e35d4c1a..7ff9f125 100644 --- a/runtime/brooklyn/src/prelude.rs +++ b/runtime/brooklyn/src/prelude.rs @@ -1,6 +1,12 @@ pub use scale_codec::{Decode, Encode}; +pub use serde::{Deserialize, Serialize}; pub use super::{ - AccountId, Balance, Balances, Runtime, RuntimeEvent, RuntimeSpecification, GGX, MILLIGGX, + AccountId, Balance, Balances, BlockHashCount, BlockNumber, EpochDurationInBlocks, Hours, + MaxAuthorities, Runtime, RuntimeCall, RuntimeEvent, RuntimeSpecification, Signature, System, + UncheckedExtrinsic, GGX, MILLIGGX, }; + pub use frame_support::parameter_types; +pub use frame_system::EnsureRoot; +pub use sp_runtime::{traits, Perbill, Percent, Permill}; From aeabdf1a210bc44e3eb806b28e387b5e132401ce Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Mon, 17 Jun 2024 14:09:00 +0300 Subject: [PATCH 02/11] Formatting --- node/src/benchmarking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/benchmarking.rs b/node/src/benchmarking.rs index 636179ee..3b8fac4e 100644 --- a/node/src/benchmarking.rs +++ b/node/src/benchmarking.rs @@ -152,7 +152,7 @@ pub fn create_benchmark_extrinsic( ( pallet_transaction_payment::ChargeTransactionPayment::::from(0), #[cfg(feature = "allowlist")] - account_filter::account_filter::AllowAccount::new(), + account_filter::account_filter::AllowAccount::::new(), ), ); From 798e0cab8f7139e02a7de2c37c3c93dea5013919 Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Wed, 19 Jun 2024 15:07:46 +0300 Subject: [PATCH 03/11] Increase the number of authorities for Brooklyn devnet --- node/src/chain_spec.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index be104966..c6da1da8 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -87,7 +87,14 @@ pub fn development_config() -> Result { ), ], // Initial PoA authorities + // DKG requires greater than 1 authorities + #[cfg(not(feature = "brooklyn"))] vec![ValidatorIdentity::from_seed("Alice")], + #[cfg(feature = "brooklyn")] + vec![ + ValidatorIdentity::from_seed("Alice"), + ValidatorIdentity::from_seed("Bob"), + ], CHAIN_ID, true, 0, From 100556984c224b576026ee271437dfad92957f3e Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Wed, 19 Jun 2024 15:24:01 +0300 Subject: [PATCH 04/11] Update eth_light_client_brooklyn.scale --- .../scale/eth_light_client_brooklyn.scale | Bin 362573 -> 389857 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/node/tests/data/scale/eth_light_client_brooklyn.scale b/node/tests/data/scale/eth_light_client_brooklyn.scale index 001cdf472473ba5d034a0293dfa65f8ddf581a1e..9aeaaf72938712426c2a4cb45fe71126f407865b 100644 GIT binary patch delta 60816 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 From 543c1e69ed58fd30f3f117e2c88577be9ce46e6d Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Mon, 1 Jul 2024 16:24:42 +0300 Subject: [PATCH 05/11] Update tests --- node/tests/common.rs | 47 ++++++++++++++++++++++++-- node/tests/eth_light_client.rs | 10 +++--- node/tests/evm_burn_token.rs | 2 +- node/tests/evm_set_session_keys.rs | 2 +- node/tests/evm_zk_verify_precompile.rs | 2 +- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/node/tests/common.rs b/node/tests/common.rs index d79f92ce..44c97171 100644 --- a/node/tests/common.rs +++ b/node/tests/common.rs @@ -29,6 +29,7 @@ use std::{ ops::{Deref, DerefMut}, path::Path, process::{self, Child, Command, ExitStatus}, + sync::atomic, time::Duration, }; @@ -54,6 +55,8 @@ pub const CHAIN_ID: u64 = 8886u64; #[cfg(feature = "brooklyn")] pub const CHAIN_ID: u64 = 888866u64; +static DEV_P2P_PORT: atomic::AtomicUsize = atomic::AtomicUsize::new(30333); + /// Wait for the given `child` the given number of `secs`. /// /// Returns the `Some(exit status)` or `None` if the process did not finish in the given time. @@ -269,14 +272,54 @@ impl Node { } } -pub async fn start_node_for_local_chain(validator_name: &str, chain: &str) -> Node { +pub async fn start_dev_node() -> Node { + start_node_for_local_chain( + "alice", + "dev", + DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + ) + .await +} + +pub async fn start_dev_nodes() -> (Node, Node) { + ( + start_node_for_local_chain( + "alice", + "dev", + DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + ) + .await, + start_node_for_local_chain( + "bob", + "dev", + DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + ) + .await, + ) +} + +pub async fn start_node_for_local_chain(validator_name: &str, chain: &str, port: usize) -> Node { let base_path = tempdir().expect("could not create a temp dir"); let (stderr_file, output_path) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); + let mut node_args = vec![ + format!("--{validator_name}"), + format!("--chain={chain}"), + format!("--port={port}"), + ]; + if validator_name == "bob" { + const ALICE_NODE_ID: &str = "12D3KooWL3zHdztTgt1Vp45rgojFhEjMx32924s5mhrzhaQWAxVU"; + node_args.push(format!( + "--bootnodes=/ip4/127.0.0.1/tcp/{}/p2p/{}", + port - 1, + ALICE_NODE_ID + )); + }; + let cmd = Command::new(cargo_bin("ggxchain-node")) .stdout(process::Stdio::piped()) .stderr(process::Stdio::from(stderr_file)) - .args([&format!("--{validator_name}"), &format!("--chain={chain}")]) + .args(node_args) .arg("--rpc-cors=all") .arg("-d") .arg(base_path.path()) diff --git a/node/tests/eth_light_client.rs b/node/tests/eth_light_client.rs index c62ed575..94d96e66 100644 --- a/node/tests/eth_light_client.rs +++ b/node/tests/eth_light_client.rs @@ -145,8 +145,7 @@ mod test { #[tokio::test] async fn eth_light_client_loads_data_and_accepts_merkle_proof( ) -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; - println!("Alice ws url: {}", alice.ws_url); + let (mut alice, mut bob) = common::start_dev_nodes().await; common::wait_n_finalized_blocks_from(1, &alice.ws_url).await; @@ -156,6 +155,7 @@ mod test { load_light_client_data(&api, &alice_pair).await; alice.kill(); + bob.kill(); Ok(()) } @@ -163,7 +163,7 @@ mod test { #[tokio::test] async fn eth_light_client_data_could_be_fetched_from_evm_by_precompile( ) -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; + let (mut alice, mut bob) = common::start_dev_nodes().await; let api = OnlineClient::::from_url(alice.ws_url.clone()).await?; let alice_pair = subxt_signer::sr25519::dev::alice(); @@ -209,6 +209,7 @@ mod test { } alice.kill(); + bob.kill(); Ok(()) } @@ -216,7 +217,7 @@ mod test { #[tokio::test] async fn eth_light_client_data_could_be_fetched_from_chain_extension( ) -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; + let (mut alice, mut bob) = common::start_dev_nodes().await; let api = OnlineClient::::from_url(alice.ws_url.clone()).await?; let alice_pair = subxt_signer::sr25519::dev::alice(); @@ -234,6 +235,7 @@ mod test { .await; alice.kill(); + bob.kill(); Ok(()) } diff --git a/node/tests/evm_burn_token.rs b/node/tests/evm_burn_token.rs index 97f0caf0..d57d8dbe 100644 --- a/node/tests/evm_burn_token.rs +++ b/node/tests/evm_burn_token.rs @@ -49,7 +49,7 @@ async fn _print_balances( #[cfg(unix)] #[tokio::test] async fn evm_burn_token_test() -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; + let mut alice = common::start_dev_node().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; diff --git a/node/tests/evm_set_session_keys.rs b/node/tests/evm_set_session_keys.rs index 567e7754..4828d877 100644 --- a/node/tests/evm_set_session_keys.rs +++ b/node/tests/evm_set_session_keys.rs @@ -48,7 +48,7 @@ async fn call_set_session_key( #[cfg(unix)] #[tokio::test] async fn evm_set_session_key_test() -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; + let mut alice = common::start_dev_node().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; diff --git a/node/tests/evm_zk_verify_precompile.rs b/node/tests/evm_zk_verify_precompile.rs index 651d54e1..60b77db3 100644 --- a/node/tests/evm_zk_verify_precompile.rs +++ b/node/tests/evm_zk_verify_precompile.rs @@ -87,7 +87,7 @@ async fn call_zk_groth16_verify( #[cfg(unix)] #[tokio::test] async fn evm_zk_verify_test() -> Result<(), Box> { - let mut alice = common::start_node_for_local_chain("alice", "dev").await; + let mut alice = common::start_dev_node().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; From 2a8ccd5511fd984a8a2523ca98ee807829fd7699 Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Tue, 2 Jul 2024 15:39:30 +0300 Subject: [PATCH 06/11] Update tests --- node/tests/common.rs | 56 +++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/node/tests/common.rs b/node/tests/common.rs index 44c97171..dea7f0d0 100644 --- a/node/tests/common.rs +++ b/node/tests/common.rs @@ -56,6 +56,7 @@ pub const CHAIN_ID: u64 = 8886u64; pub const CHAIN_ID: u64 = 888866u64; static DEV_P2P_PORT: atomic::AtomicUsize = atomic::AtomicUsize::new(30333); +static DEV_RPC_PORT: atomic::AtomicUsize = atomic::AtomicUsize::new(9944); /// Wait for the given `child` the given number of `secs`. /// @@ -277,44 +278,55 @@ pub async fn start_dev_node() -> Node { "alice", "dev", DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + DEV_RPC_PORT.fetch_add(1, atomic::Ordering::SeqCst), ) .await } pub async fn start_dev_nodes() -> (Node, Node) { + let (alice_p2p_port, bob_p2p_port, alice_rpc_port, bob_rpc_port) = ( + DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), + DEV_RPC_PORT.fetch_add(1, atomic::Ordering::SeqCst), + DEV_RPC_PORT.fetch_add(1, atomic::Ordering::SeqCst), + ); ( - start_node_for_local_chain( - "alice", - "dev", - DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), - ) - .await, - start_node_for_local_chain( - "bob", - "dev", - DEV_P2P_PORT.fetch_add(1, atomic::Ordering::SeqCst), - ) - .await, + start_node_for_local_chain("alice", "dev", alice_p2p_port, alice_rpc_port).await, + start_node_for_local_chain("bob", "dev", bob_p2p_port, bob_rpc_port).await, ) } -pub async fn start_node_for_local_chain(validator_name: &str, chain: &str, port: usize) -> Node { +pub async fn start_node_for_local_chain( + validator_name: &str, + chain: &str, + p2p_port: usize, + rpc_port: usize, +) -> Node { let base_path = tempdir().expect("could not create a temp dir"); let (stderr_file, output_path) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); let mut node_args = vec![ format!("--{validator_name}"), format!("--chain={chain}"), - format!("--port={port}"), + format!("--port={p2p_port}"), + format!("--rpc-port={rpc_port}"), + // "--unsafe-rpc-external".to_string(), ]; - if validator_name == "bob" { - const ALICE_NODE_ID: &str = "12D3KooWL3zHdztTgt1Vp45rgojFhEjMx32924s5mhrzhaQWAxVU"; - node_args.push(format!( - "--bootnodes=/ip4/127.0.0.1/tcp/{}/p2p/{}", - port - 1, - ALICE_NODE_ID - )); - }; + match validator_name { + "alice" => node_args.push( + "--node-key=0000000000000000000000000000000000000000000000000000000000000001" + .to_string(), + ), + "bob" => { + const ALICE_NODE_ID: &str = "12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp"; + node_args.push(format!( + "--bootnodes=/ip4/127.0.0.1/tcp/{}/p2p/{}", + p2p_port - 1, + ALICE_NODE_ID + )); + } + _ => (), + } let cmd = Command::new(cargo_bin("ggxchain-node")) .stdout(process::Stdio::piped()) From b401e6e15ca4a2d2fc1a0605f95fcbc5ef08087c Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Tue, 2 Jul 2024 20:47:50 +0300 Subject: [PATCH 07/11] Update tests --- node/tests/common.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/node/tests/common.rs b/node/tests/common.rs index dea7f0d0..915cb975 100644 --- a/node/tests/common.rs +++ b/node/tests/common.rs @@ -302,7 +302,19 @@ pub async fn start_node_for_local_chain( p2p_port: usize, rpc_port: usize, ) -> Node { - let base_path = tempdir().expect("could not create a temp dir"); + let base_path = match std::env::var("TEST_DATA_DIR") { + Ok(test_data_dir) => { + let dir = format!("{test_data_dir}/{chain}-{validator_name}-{rpc_port}"); + std::fs::create_dir_all(&dir).expect(&format!("could not create directory {dir}")); + dir + } + Err(_) => tempdir() + .expect("could not create a temp dir") + .path() + .to_str() + .unwrap() + .to_string(), + }; let (stderr_file, output_path) = tempfile::NamedTempFile::new().unwrap().keep().unwrap(); let mut node_args = vec![ @@ -310,7 +322,8 @@ pub async fn start_node_for_local_chain( format!("--chain={chain}"), format!("--port={p2p_port}"), format!("--rpc-port={rpc_port}"), - // "--unsafe-rpc-external".to_string(), + format!("--base-path={base_path}"), + "--rpc-cors=all".to_string(), ]; match validator_name { "alice" => node_args.push( @@ -332,9 +345,6 @@ pub async fn start_node_for_local_chain( .stdout(process::Stdio::piped()) .stderr(process::Stdio::from(stderr_file)) .args(node_args) - .arg("--rpc-cors=all") - .arg("-d") - .arg(base_path.path()) .spawn() .unwrap(); From 2a6a4248edda160bb6ba64ed310931ac397a17fe Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Tue, 2 Jul 2024 20:51:16 +0300 Subject: [PATCH 08/11] Update CI config --- .github/workflows/ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e8a819bb..37600776 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -125,5 +125,8 @@ jobs: - name: test if: steps.git-diff.outputs.diff + env: + TEST_DATA_DIR: test-data run: | + rm -rf "node/${TEST_DATA_DIR}" cargo test --locked --package=ggxchain-node --release --no-default-features --features=${{ matrix.runtime }} From 2d28b57557b936735a563405ad7d9d826f057ca9 Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Wed, 3 Jul 2024 16:26:04 +0300 Subject: [PATCH 09/11] Update tests --- node/tests/evm_burn_token.rs | 3 ++- node/tests/evm_set_session_keys.rs | 3 ++- node/tests/evm_zk_verify_precompile.rs | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/node/tests/evm_burn_token.rs b/node/tests/evm_burn_token.rs index d57d8dbe..bee03dbd 100644 --- a/node/tests/evm_burn_token.rs +++ b/node/tests/evm_burn_token.rs @@ -49,7 +49,7 @@ async fn _print_balances( #[cfg(unix)] #[tokio::test] async fn evm_burn_token_test() -> Result<(), Box> { - let mut alice = common::start_dev_node().await; + let (mut alice, mut bob) = common::start_dev_nodes().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; @@ -95,6 +95,7 @@ async fn evm_burn_token_test() -> Result<(), Box> { // Stop the process alice.kill(); + bob.kill(); Ok(()) } diff --git a/node/tests/evm_set_session_keys.rs b/node/tests/evm_set_session_keys.rs index 4828d877..c0f55719 100644 --- a/node/tests/evm_set_session_keys.rs +++ b/node/tests/evm_set_session_keys.rs @@ -48,7 +48,7 @@ async fn call_set_session_key( #[cfg(unix)] #[tokio::test] async fn evm_set_session_key_test() -> Result<(), Box> { - let mut alice = common::start_dev_node().await; + let (mut alice, mut bob) = common::start_dev_nodes().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; @@ -77,6 +77,7 @@ async fn evm_set_session_key_test() -> Result<(), Box> { // Stop the process alice.kill(); + bob.kill(); Ok(()) } diff --git a/node/tests/evm_zk_verify_precompile.rs b/node/tests/evm_zk_verify_precompile.rs index 60b77db3..aa8850d7 100644 --- a/node/tests/evm_zk_verify_precompile.rs +++ b/node/tests/evm_zk_verify_precompile.rs @@ -87,7 +87,7 @@ async fn call_zk_groth16_verify( #[cfg(unix)] #[tokio::test] async fn evm_zk_verify_test() -> Result<(), Box> { - let mut alice = common::start_dev_node().await; + let (mut alice, mut bob) = common::start_dev_nodes().await; // Let it produce some blocks. let _ = common::wait_n_finalized_blocks(1, 30, &alice.ws_url).await; @@ -111,6 +111,7 @@ async fn evm_zk_verify_test() -> Result<(), Box> { // Stop the process alice.kill(); + bob.kill(); Ok(()) } From 47fa4abb1d6be920e9cb193d2d17594503bd5d10 Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Wed, 10 Jul 2024 16:56:11 +0300 Subject: [PATCH 10/11] Update tests --- node/tests/evm_set_session_keys.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/node/tests/evm_set_session_keys.rs b/node/tests/evm_set_session_keys.rs index c0f55719..993b9225 100644 --- a/node/tests/evm_set_session_keys.rs +++ b/node/tests/evm_set_session_keys.rs @@ -12,7 +12,7 @@ pub mod common; use common::CHAIN_ID; -const SESSION_KEYS: &str= "6a8357e87e163a03ed9c03ce2852bcf673121fc67c9fa7b839797879547c155c5c9479d0fea15172526450eb3bda80d9830fabf07e4fe4b7c020bfd0e6dbd321bc7e65505f0967481fb2c7d5226072d14efaae9d65c1d732548c1cf07d675927038602d835e19cd18df04a40a0c3991fa76f254b89fe9b98401961bde94f15bc6e"; +const SESSION_KEYS: &str= "dc2a5a4d7d9cd10807ba90f4cc2ca6af94414d3f9a4a7c47ae3371263ee9894706361d59c0503c78196ffb531c244edc78c6585680c6ca97068c850bea7a8abe2eeda6e65141736b2a609e6ee8dac17dc37c4ec3cc1f807e6552fce0ff60d32f0325ff58ef8784f6aef99c3870b697f8d7511f905d67f42d17fb5886718ba2f62a03ebc395e821e76e61e783b3325ce6fc84f3da4b61a8ee2759bf8a2a0f78c0f461"; type Client = SignerMiddleware, Wallet>; @@ -57,6 +57,10 @@ async fn evm_set_session_key_test() -> Result<(), Box> { alice.child.try_wait().unwrap().is_none(), "the process should still be running" ); + assert!( + bob.child.try_wait().unwrap().is_none(), + "the process should still be running" + ); let provider: Provider = Provider::::try_from(alice.http_url.clone())?; // Change to correct network From 323a20c54950ebd7a510efb7b94e6a364c55d453 Mon Sep 17 00:00:00 2001 From: Obsessed Cake Date: Wed, 10 Jul 2024 17:43:32 +0300 Subject: [PATCH 11/11] Update tests --- node/tests/evm_set_session_keys.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/node/tests/evm_set_session_keys.rs b/node/tests/evm_set_session_keys.rs index 993b9225..842cd156 100644 --- a/node/tests/evm_set_session_keys.rs +++ b/node/tests/evm_set_session_keys.rs @@ -12,8 +12,12 @@ pub mod common; use common::CHAIN_ID; +#[cfg(feature = "brooklyn")] const SESSION_KEYS: &str= "dc2a5a4d7d9cd10807ba90f4cc2ca6af94414d3f9a4a7c47ae3371263ee9894706361d59c0503c78196ffb531c244edc78c6585680c6ca97068c850bea7a8abe2eeda6e65141736b2a609e6ee8dac17dc37c4ec3cc1f807e6552fce0ff60d32f0325ff58ef8784f6aef99c3870b697f8d7511f905d67f42d17fb5886718ba2f62a03ebc395e821e76e61e783b3325ce6fc84f3da4b61a8ee2759bf8a2a0f78c0f461"; +#[cfg(not(feature = "brooklyn"))] +const SESSION_KEYS: &str= "6a8357e87e163a03ed9c03ce2852bcf673121fc67c9fa7b839797879547c155c5c9479d0fea15172526450eb3bda80d9830fabf07e4fe4b7c020bfd0e6dbd321bc7e65505f0967481fb2c7d5226072d14efaae9d65c1d732548c1cf07d675927038602d835e19cd18df04a40a0c3991fa76f254b89fe9b98401961bde94f15bc6e"; + type Client = SignerMiddleware, Wallet>; // Sends some native currency