mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-03 09:29:46 +00:00
Initial validator sets pallet (#187)
* Initial work on a Validator Sets pallet * Update Validator Set docs per current discussions * Update validator-sets primitives and storage handling * Add validator set pallets to deny.toml * Remove Curve from primitives Since we aren't reusing keys across coins, there's no reason for it to be on-chain (as previously planned). * Update documentation on Validator Sets * Use Twox64Concat instead of Identity Ensures an even distribution of keys. While xxhash is breakable, these keys aren't manipulatable by users. * Add math ops on Amount and define a coin as 1e8 * Add validator-sets to the runtime and remove contracts Also removes the randomness pallet which was only required by the contracts runtime. Does not remove the contracts folder yet so they can still be referred to while validator-sets is under development. Does remove them from Cargo.toml. * Add vote function to validator-sets * Remove contracts folder * Create an event for the Validator Sets pallet * Remove old contracts crates from deny.toml * Remove line from staking branch * Remove staking from runtime * Correct VS Config in runtime * cargo update * Resolve a few PR comments on terminology * Create a serai-primitives crate Move types such as Amount/Coin out of validator-sets. Will be expanded in the future. * Fixes for last commit * Don't reserve set 0 * Further fixes * Add files meant for last commit * Remove Staking transfer
This commit is contained in:
parent
52913a6e8d
commit
e979883f2d
26 changed files with 572 additions and 1151 deletions
452
Cargo.lock
generated
452
Cargo.lock
generated
|
@ -137,14 +137,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "array-bytes"
|
||||
version = "4.1.0"
|
||||
source = "git+https://github.com/hack-ink/array-bytes?rev=994cd29b66bd2ab5c8c15f0b15a1618d4bb2d94c#994cd29b66bd2ab5c8c15f0b15a1618d4bb2d94c"
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||
checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
|
@ -274,7 +269,7 @@ checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c"
|
|||
dependencies = [
|
||||
"futures",
|
||||
"pharos",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1911,7 +1906,7 @@ dependencies = [
|
|||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
@ -1945,7 +1940,7 @@ dependencies = [
|
|||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"primitive-types",
|
||||
"scale-info",
|
||||
"uint",
|
||||
|
@ -3240,15 +3235,6 @@ dependencies = [
|
|||
"rlp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-serde"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-serde"
|
||||
version = "0.4.0"
|
||||
|
@ -3286,12 +3272,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap-nostd"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590"
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.16.2"
|
||||
|
@ -3304,184 +3284,6 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_allocator"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c9588a59a0e8997c0b2153cd11b5aaa77c06a0537a6b18f3811d1f1aa098b12"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_engine"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487c3b390b7feb0620496b0cd38683433c7d7e6946b1caabda51e1f23eb24b30"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"derive_more",
|
||||
"parity-scale-codec",
|
||||
"rand 0.8.5",
|
||||
"secp256k1",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_env"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a891d34301a3dbb1c7b7424c49ae184282b163491c54f9acd17fcbe14a80447b"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"blake2",
|
||||
"cfg-if",
|
||||
"derive_more",
|
||||
"ink_allocator",
|
||||
"ink_engine",
|
||||
"ink_metadata",
|
||||
"ink_prelude",
|
||||
"ink_primitives",
|
||||
"num-traits",
|
||||
"parity-scale-codec",
|
||||
"paste",
|
||||
"rand 0.8.5",
|
||||
"rlibc",
|
||||
"scale-info",
|
||||
"secp256k1",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_lang"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cca26e374e0f89c82cf5dabb4309ef3c76a01659ad95186f4e84455c5f4621a0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"ink_env",
|
||||
"ink_lang_macro",
|
||||
"ink_prelude",
|
||||
"ink_primitives",
|
||||
"ink_storage",
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_lang_codegen"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fe57826726d89c84fe0b1fafe0dee328f58c8e927be40f0290f04602aacc45c"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"derive_more",
|
||||
"either",
|
||||
"heck",
|
||||
"impl-serde 0.3.2",
|
||||
"ink_lang_ir",
|
||||
"itertools",
|
||||
"parity-scale-codec",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_lang_ir"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f47d16b2a5340df90f11b2ec2242b37907f5c8396dbbc72c52ec9f2b1a8c90c8"
|
||||
dependencies = [
|
||||
"blake2",
|
||||
"either",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_lang_macro"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81b858be42ac6cde2c15ce6d7fa75cef59b64a3baf37f7105f39208f2b84dadb"
|
||||
dependencies = [
|
||||
"ink_lang_codegen",
|
||||
"ink_lang_ir",
|
||||
"ink_primitives",
|
||||
"parity-scale-codec",
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_metadata"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74913aaed5751f5615af4631b7559328b8ed56c9cb821b89e14af0706176e849"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"impl-serde 0.3.2",
|
||||
"ink_prelude",
|
||||
"ink_primitives",
|
||||
"scale-info",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_prelude"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f031e6b8495594a7288b089bf4122e76c26b994959d1b2b693bdfe846b14c0e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_primitives"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12cf42dce81d060401c7cec95a392ad6d3c2f18661fa3083f619ce135133c33"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ink_prelude",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_storage"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c0a98b6acbd79eedf44720412437d713e7195d1407822604de5885b0ee6c7e1"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"cfg-if",
|
||||
"derive_more",
|
||||
"ink_env",
|
||||
"ink_metadata",
|
||||
"ink_prelude",
|
||||
"ink_primitives",
|
||||
"ink_storage_derive",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ink_storage_derive"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "babf1d8903dc9219ad8e8aa181eddb919d9794aad1da23ccdce770925b7de2ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
|
@ -5102,70 +4904,6 @@ dependencies = [
|
|||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts"
|
||||
version = "4.0.0-dev"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"impl-trait-for-tuples",
|
||||
"log",
|
||||
"pallet-contracts-primitives",
|
||||
"pallet-contracts-proc-macro",
|
||||
"parity-scale-codec",
|
||||
"rand 0.8.5",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"sp-api",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"wasm-instrument 0.4.0",
|
||||
"wasmi 0.20.0",
|
||||
"wasmparser-nostd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-primitives"
|
||||
version = "7.0.0"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"parity-scale-codec",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-weights",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-contracts-proc-macro"
|
||||
version = "4.0.0-dev"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-randomness-collective-flip"
|
||||
version = "4.0.0-dev"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"parity-scale-codec",
|
||||
"safe-mix",
|
||||
"scale-info",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-session"
|
||||
version = "4.0.0-dev"
|
||||
|
@ -5500,7 +5238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5717,7 +5455,7 @@ dependencies = [
|
|||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"uint",
|
||||
]
|
||||
|
@ -6211,7 +5949,7 @@ dependencies = [
|
|||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
|
@ -6226,12 +5964,6 @@ dependencies = [
|
|||
"digest 0.10.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlibc"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe"
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.5.2"
|
||||
|
@ -6317,15 +6049,6 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -6419,15 +6142,6 @@ version = "1.0.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
||||
|
||||
[[package]]
|
||||
name = "safe-mix"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d3d055a2582e6b00ed7a31c1524040aa391092bf636328350813f3a0605215c"
|
||||
dependencies = [
|
||||
"rustc_version 0.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "salsa20"
|
||||
version = "0.10.2"
|
||||
|
@ -6663,7 +6377,7 @@ dependencies = [
|
|||
"sp-version",
|
||||
"sp-wasm-interface",
|
||||
"tracing",
|
||||
"wasmi 0.13.2",
|
||||
"wasmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6675,8 +6389,8 @@ dependencies = [
|
|||
"sp-maybe-compressed-blob",
|
||||
"sp-wasm-interface",
|
||||
"thiserror",
|
||||
"wasm-instrument 0.3.0",
|
||||
"wasmi 0.13.2",
|
||||
"wasm-instrument",
|
||||
"wasmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6689,7 +6403,7 @@ dependencies = [
|
|||
"sc-executor-common",
|
||||
"sp-runtime-interface",
|
||||
"sp-wasm-interface",
|
||||
"wasmi 0.13.2",
|
||||
"wasmi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7484,15 +7198,6 @@ dependencies = [
|
|||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
|
@ -7514,30 +7219,6 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
|
||||
|
||||
[[package]]
|
||||
name = "serai-extension"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ink_env",
|
||||
"ink_lang",
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-multisig"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ink_env",
|
||||
"ink_lang",
|
||||
"ink_metadata",
|
||||
"ink_primitives",
|
||||
"ink_storage",
|
||||
"lazy_static",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-extension",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-node"
|
||||
version = "0.1.0"
|
||||
|
@ -7568,6 +7249,7 @@ dependencies = [
|
|||
"sc-tendermint",
|
||||
"sc-transaction-pool",
|
||||
"sc-transaction-pool-api",
|
||||
"serai-primitives",
|
||||
"serai-runtime",
|
||||
"sp-api",
|
||||
"sp-application-crypto",
|
||||
|
@ -7583,6 +7265,18 @@ dependencies = [
|
|||
"sp-timestamp",
|
||||
"substrate-build-script-utils",
|
||||
"substrate-frame-rpc-system",
|
||||
"validator-sets-pallet",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serai-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7617,9 +7311,6 @@ dependencies = [
|
|||
"frame-system-rpc-runtime-api",
|
||||
"hex-literal",
|
||||
"pallet-balances",
|
||||
"pallet-contracts",
|
||||
"pallet-contracts-primitives",
|
||||
"pallet-randomness-collective-flip",
|
||||
"pallet-session",
|
||||
"pallet-tendermint",
|
||||
"pallet-timestamp",
|
||||
|
@ -7640,6 +7331,7 @@ dependencies = [
|
|||
"sp-transaction-pool",
|
||||
"sp-version",
|
||||
"substrate-wasm-builder",
|
||||
"validator-sets-pallet",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -7865,7 +7557,7 @@ dependencies = [
|
|||
"curve25519-dalek 4.0.0-pre.5",
|
||||
"rand_core 0.6.4",
|
||||
"ring",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
"sha2 0.10.6",
|
||||
"subtle",
|
||||
]
|
||||
|
@ -8032,7 +7724,7 @@ dependencies = [
|
|||
"futures",
|
||||
"hash-db",
|
||||
"hash256-std-hasher",
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"lazy_static",
|
||||
"libsecp256k1",
|
||||
"log",
|
||||
|
@ -8058,7 +7750,7 @@ dependencies = [
|
|||
"substrate-bip39",
|
||||
"thiserror",
|
||||
"tiny-bip39",
|
||||
"wasmi 0.13.2",
|
||||
"wasmi",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
@ -8353,7 +8045,7 @@ name = "sp-storage"
|
|||
version = "7.0.0"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
|
@ -8451,7 +8143,7 @@ name = "sp-version"
|
|||
version = "5.0.0"
|
||||
source = "git+https://github.com/serai-dex/substrate#6dc38b0ba11dff62c1042ab0fa21d0b55a64bf6e"
|
||||
dependencies = [
|
||||
"impl-serde 0.4.0",
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
"parity-wasm",
|
||||
"scale-info",
|
||||
|
@ -8483,7 +8175,7 @@ dependencies = [
|
|||
"log",
|
||||
"parity-scale-codec",
|
||||
"sp-std",
|
||||
"wasmi 0.13.2",
|
||||
"wasmi",
|
||||
"wasmtime",
|
||||
]
|
||||
|
||||
|
@ -8509,12 +8201,6 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.6.0"
|
||||
|
@ -9423,6 +9109,29 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-sets-pallet"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-primitives",
|
||||
"validator-sets-primitives",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator-sets-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
@ -9567,15 +9276,6 @@ dependencies = [
|
|||
"parity-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-instrument"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a47ecb37b9734d1085eaa5ae1a81e60801fd8c28d4cabdd8aedb982021918bc"
|
||||
dependencies = [
|
||||
"parity-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-opt"
|
||||
version = "0.110.2"
|
||||
|
@ -9640,19 +9340,7 @@ checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422"
|
|||
dependencies = [
|
||||
"parity-wasm",
|
||||
"wasmi-validation",
|
||||
"wasmi_core 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmi"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01bf50edb2ea9d922aa75a7bf3c15e26a6c9e2d18c56e862b49737a582901729"
|
||||
dependencies = [
|
||||
"spin 0.9.4",
|
||||
"wasmi_arena",
|
||||
"wasmi_core 0.5.0",
|
||||
"wasmparser-nostd",
|
||||
"wasmi_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -9664,12 +9352,6 @@ dependencies = [
|
|||
"parity-wasm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmi_arena"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ea379cbb0b41f3a9f0bf7b47036d036aae7f43383d8cc487d4deccf40dee0a"
|
||||
|
||||
[[package]]
|
||||
name = "wasmi_core"
|
||||
version = "0.2.1"
|
||||
|
@ -9683,17 +9365,6 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmi_core"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5bf998ab792be85e20e771fe14182b4295571ad1d4f89d3da521c1bef5f597a"
|
||||
dependencies = [
|
||||
"downcast-rs",
|
||||
"libm 0.2.6",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.89.1"
|
||||
|
@ -9703,15 +9374,6 @@ dependencies = [
|
|||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser-nostd"
|
||||
version = "0.91.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c37f310b5a62bfd5ae7c0f1d8e6f98af16a5d6d84ba764e9c36439ec14e318b"
|
||||
dependencies = [
|
||||
"indexmap-nostd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmtime"
|
||||
version = "1.0.2"
|
||||
|
@ -10130,7 +9792,7 @@ dependencies = [
|
|||
"futures",
|
||||
"js-sys",
|
||||
"pharos",
|
||||
"rustc_version 0.4.0",
|
||||
"rustc_version",
|
||||
"send_wrapper",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -22,6 +22,11 @@ members = [
|
|||
|
||||
"processor",
|
||||
|
||||
"substrate/serai/primitives",
|
||||
|
||||
"substrate/validator-sets/primitives",
|
||||
"substrate/validator-sets/pallet",
|
||||
|
||||
"substrate/tendermint/machine",
|
||||
"substrate/tendermint/primitives",
|
||||
"substrate/tendermint/client",
|
||||
|
@ -29,9 +34,6 @@ members = [
|
|||
|
||||
"substrate/runtime",
|
||||
"substrate/node",
|
||||
|
||||
"contracts/extension",
|
||||
"contracts/multisig",
|
||||
]
|
||||
|
||||
# Always compile Monero (and a variety of dependencies) with optimizations due
|
||||
|
@ -53,8 +55,3 @@ monero-serai = { opt-level = 3 }
|
|||
|
||||
[profile.release]
|
||||
panic = "unwind"
|
||||
|
||||
[patch.crates-io]
|
||||
# array-bytes 4.1.0 is GPL-3.0.
|
||||
# array-bytes git, which has no code changes, includes a dual-license under Apache-2.0.
|
||||
array-bytes = { git = "https://github.com/hack-ink/array-bytes", rev = "994cd29b66bd2ab5c8c15f0b15a1618d4bb2d94c" }
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
[package]
|
||||
name = "serai-extension"
|
||||
version = "0.1.0"
|
||||
description = "An ink! extension for exposing Serai to ink"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/contracts/extension"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
|
||||
ink_env = { version = "3", default-features = false }
|
||||
ink_lang = { version = "3", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["ink_env/std"]
|
||||
ink-as-dependency = []
|
|
@ -1,122 +0,0 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use ink_lang as ink;
|
||||
use ink_env::{Environment, DefaultEnvironment, AccountId};
|
||||
|
||||
pub type Curve = u16;
|
||||
pub type Coin = u32;
|
||||
pub type GlobalValidatorSetId = u32;
|
||||
pub type ValidatorSetIndex = u8;
|
||||
pub type Key = Vec<u8>;
|
||||
|
||||
#[ink::chain_extension]
|
||||
pub trait SeraiExtension {
|
||||
type ErrorCode = ();
|
||||
|
||||
/// Returns the ID for the current global validator set.
|
||||
#[ink(extension = 0, handle_status = false, returns_result = false)]
|
||||
fn global_validator_set_id() -> GlobalValidatorSetId;
|
||||
|
||||
/// Returns the amount of active validator sets within the global validator set.
|
||||
#[ink(extension = 1, handle_status = false, returns_result = false)]
|
||||
fn validator_sets() -> u8;
|
||||
|
||||
/// Returns the amount of key shares used within the specified validator set.
|
||||
#[ink(extension = 2, handle_status = false, returns_result = false)]
|
||||
fn validator_set_shares(set: ValidatorSetIndex) -> u16;
|
||||
|
||||
/// Returns the validator set the specified account is in, along with their amount of shares in
|
||||
/// that validator set, if they are in a current validator
|
||||
#[ink(extension = 3, handle_status = false, returns_result = false)]
|
||||
fn active_validator(account: &AccountId) -> Option<(ValidatorSetIndex, u16)>;
|
||||
}
|
||||
|
||||
pub struct SeraiEnvironment;
|
||||
impl Environment for SeraiEnvironment {
|
||||
const MAX_EVENT_TOPICS: usize = <DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;
|
||||
|
||||
type AccountId = <DefaultEnvironment as Environment>::AccountId;
|
||||
type Balance = <DefaultEnvironment as Environment>::Balance;
|
||||
type Hash = <DefaultEnvironment as Environment>::Hash;
|
||||
type BlockNumber = <DefaultEnvironment as Environment>::BlockNumber;
|
||||
type Timestamp = <DefaultEnvironment as Environment>::Timestamp;
|
||||
|
||||
type ChainExtension = SeraiExtension;
|
||||
}
|
||||
|
||||
pub fn test_validators() -> Vec<AccountId> {
|
||||
vec![
|
||||
AccountId::from([1; 32]),
|
||||
AccountId::from([2; 32]),
|
||||
AccountId::from([3; 32]),
|
||||
AccountId::from([4; 32]),
|
||||
AccountId::from([5; 32]),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn test_register() {
|
||||
struct ExtensionId;
|
||||
impl ink_env::test::ChainExtension for ExtensionId {
|
||||
fn func_id(&self) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
fn call(&mut self, _: &[u8], output: &mut Vec<u8>) -> u32 {
|
||||
// Non-0 global validator set ID
|
||||
scale::Encode::encode_to(&1u32, output);
|
||||
0
|
||||
}
|
||||
}
|
||||
ink_env::test::register_chain_extension(ExtensionId);
|
||||
|
||||
struct ExtensionSets;
|
||||
impl ink_env::test::ChainExtension for ExtensionSets {
|
||||
fn func_id(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn call(&mut self, _: &[u8], output: &mut Vec<u8>) -> u32 {
|
||||
// 1 validator set
|
||||
scale::Encode::encode_to(&1u8, output);
|
||||
0
|
||||
}
|
||||
}
|
||||
ink_env::test::register_chain_extension(ExtensionSets);
|
||||
|
||||
struct ExtensionShares;
|
||||
impl ink_env::test::ChainExtension for ExtensionShares {
|
||||
fn func_id(&self) -> u32 {
|
||||
2
|
||||
}
|
||||
|
||||
fn call(&mut self, _: &[u8], output: &mut Vec<u8>) -> u32 {
|
||||
// 1 key share per validator
|
||||
scale::Encode::encode_to(&u16::try_from(test_validators().len()).unwrap(), output);
|
||||
0
|
||||
}
|
||||
}
|
||||
ink_env::test::register_chain_extension(ExtensionShares);
|
||||
|
||||
struct ExtensionActive;
|
||||
impl ink_env::test::ChainExtension for ExtensionActive {
|
||||
fn func_id(&self) -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
fn call(&mut self, input: &[u8], output: &mut Vec<u8>) -> u32 {
|
||||
use scale::Decode;
|
||||
let potential = AccountId::decode(&mut &input[1 ..]).unwrap(); // TODO: Why is this [1 ..]?
|
||||
|
||||
let mut presence = false;
|
||||
for validator in test_validators() {
|
||||
if potential == validator {
|
||||
presence = true;
|
||||
}
|
||||
}
|
||||
// Validator set 0, 1 key share
|
||||
scale::Encode::encode_to(&Some((0u8, 1u16)).filter(|_| presence), output);
|
||||
0
|
||||
}
|
||||
}
|
||||
ink_env::test::register_chain_extension(ExtensionActive);
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
[package]
|
||||
name = "serai-multisig"
|
||||
version = "0.1.0"
|
||||
description = "An ink! tracker for Serai's current multisig"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/contracts/multisig"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lib]
|
||||
name = "serai_multisig"
|
||||
path = "lib.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }
|
||||
|
||||
ink_primitives = { version = "3", default-features = false }
|
||||
ink_metadata = { version = "3", default-features = false, features = ["derive"], optional = true }
|
||||
ink_env = { version = "3", default-features = false }
|
||||
ink_storage = { version = "3", default-features = false }
|
||||
ink_lang = { version = "3", default-features = false }
|
||||
|
||||
serai-extension = { path = "../extension", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
lazy_static = "1"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
"ink_primitives/std",
|
||||
"ink_metadata/std",
|
||||
"ink_env/std",
|
||||
"ink_storage/std",
|
||||
|
||||
"serai-extension/std",
|
||||
]
|
||||
ink-as-dependency = []
|
|
@ -1,356 +0,0 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use ink_lang as ink;
|
||||
|
||||
use serai_extension::{Curve, GlobalValidatorSetId, ValidatorSetIndex, Key};
|
||||
|
||||
type KeysHash = [u8; 32];
|
||||
|
||||
#[allow(clippy::all)]
|
||||
#[ink::contract(env = serai_extension::SeraiEnvironment)]
|
||||
mod multisig {
|
||||
use scale::Encode;
|
||||
|
||||
use ink_storage::{traits::SpreadAllocate, Mapping};
|
||||
use ink_env::{hash::Blake2x256, hash_encoded};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A contract which tracks the current multisig keys.
|
||||
/// Mapping of each validator set to their multisigs.
|
||||
#[ink(storage)]
|
||||
#[derive(SpreadAllocate)]
|
||||
pub struct Multisig {
|
||||
/// Global validator set ID under which this multisig was updated.
|
||||
/// Used to track if the multisig has been updated to the latest instantiation of a validator
|
||||
/// set or not.
|
||||
/// May be behind, and still healthy, if a validator set didn't change despite the global
|
||||
/// validator set doing so.
|
||||
updated_at: Mapping<ValidatorSetIndex, GlobalValidatorSetId>,
|
||||
/// Mapping from a curve's index to the multisig's current public key for it, if it has one.
|
||||
// This is a mapping due to ink's eager loading. Considering we're right now only considering
|
||||
// Secp256k1 and Ed25519, it may be notably more efficient to use a Vec here.
|
||||
// In practice, we're likely discussing up to 7 curves in total, so it may always be better to
|
||||
// simply use a Vec here, especially since it'd be Vec<Option<Key>>.
|
||||
keys: Mapping<(ValidatorSetIndex, Curve), Key>,
|
||||
/// Validator + Keys -> Voted already or not.
|
||||
/// Prevents voting multiple times on the same set of keys.
|
||||
voted: Mapping<(AccountId, KeysHash), ()>,
|
||||
/// Global Validator Set ID + Validator + Keys -> Vote Count.
|
||||
/// Including the GVSID locks it to a specific time period, preventing a validator from joining
|
||||
/// a set, voting on old keys, and then moving their bond to a new account to vote again.
|
||||
votes: Mapping<(GlobalValidatorSetId, ValidatorSetIndex, KeysHash), u16>,
|
||||
}
|
||||
|
||||
/// Event emitted when a new set of multisig keys is voted on.
|
||||
#[ink(event)]
|
||||
pub struct Vote {
|
||||
/// Validator who issued the vote.
|
||||
#[ink(topic)]
|
||||
validator: AccountId,
|
||||
/// Global validator set ID under which keys are being generated.
|
||||
#[ink(topic)]
|
||||
global_validator_set: GlobalValidatorSetId,
|
||||
/// Validator set for which keys are being generated.
|
||||
#[ink(topic)]
|
||||
validator_set: ValidatorSetIndex,
|
||||
/// Hash of the keys voted on.
|
||||
#[ink(topic)]
|
||||
hash: KeysHash,
|
||||
/// Keys voted on. Only present in the first event for a given set of keys.
|
||||
keys: Option<Vec<Option<Key>>>,
|
||||
}
|
||||
|
||||
/// Event emitted when the new keys are fully generated for a validator set, having been fully
|
||||
/// voted on.
|
||||
#[ink(event)]
|
||||
pub struct KeyGen {
|
||||
#[ink(topic)]
|
||||
global_validator_set: GlobalValidatorSetId,
|
||||
#[ink(topic)]
|
||||
validator_set: ValidatorSetIndex,
|
||||
#[ink(topic)]
|
||||
hash: KeysHash,
|
||||
}
|
||||
|
||||
/// The Multisig error types.
|
||||
#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
|
||||
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
|
||||
pub enum Error {
|
||||
/// Returned if a validator set hasn't had keys registered for it yet.
|
||||
NonExistentValidatorSet,
|
||||
/// Returned if a validator set and curve index doesn't have a key registered for it.
|
||||
NonExistentKey,
|
||||
/// Returned if a curve index doesn't exist.
|
||||
NonExistentCurve,
|
||||
/// Returned if a non-validator is voting.
|
||||
NotValidator,
|
||||
/// Returned if this validator set already generated keys.
|
||||
AlreadyGeneratedKeys,
|
||||
/// Returned if this validator has already voted for these keys.
|
||||
AlreadyVoted,
|
||||
}
|
||||
|
||||
/// The Multisig result type.
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
impl Multisig {
|
||||
/// Deploys the Multisig contract.
|
||||
#[ink(constructor)]
|
||||
pub fn new() -> Self {
|
||||
ink_lang::utils::initialize_contract(|_| {})
|
||||
}
|
||||
|
||||
/// Global validator set ID under which a validator set updated their multisig.
|
||||
#[ink(message)]
|
||||
pub fn updated_at(&self, validator_set: ValidatorSetIndex) -> Result<GlobalValidatorSetId> {
|
||||
self.updated_at.get(validator_set).ok_or(Error::NonExistentValidatorSet)
|
||||
}
|
||||
|
||||
/// Returns the key currently in-use for a given validator set and curve.
|
||||
/// This is then bound to a given chain by applying a network-specific additive offset, as done
|
||||
/// by the processor. Each chain then has its own way of receiving funds to these keys, leaving
|
||||
/// this not for usage by wallets, nor the processor which is expected to track events for this
|
||||
/// information. This is really solely for debugging purposes.
|
||||
#[ink(message)]
|
||||
pub fn key(&self, validator_set: ValidatorSetIndex, curve: Curve) -> Result<Key> {
|
||||
self.keys.get((validator_set, curve)).ok_or(Error::NonExistentKey)
|
||||
}
|
||||
|
||||
// TODO: voted
|
||||
// TODO: votes
|
||||
|
||||
fn hash<T: Encode>(value: &T) -> KeysHash {
|
||||
let mut output = KeysHash::default();
|
||||
hash_encoded::<Blake2x256, _>(value, &mut output);
|
||||
output
|
||||
}
|
||||
|
||||
/// Vote for a given set of keys.
|
||||
#[ink(message)]
|
||||
pub fn vote(&mut self, keys: Vec<Option<Key>>) -> Result<()> {
|
||||
if keys.len() > 256 {
|
||||
Err(Error::NonExistentCurve)?;
|
||||
}
|
||||
|
||||
// Make sure they're a valid validator.
|
||||
let validator = self.env().caller();
|
||||
let active_validator = self.env().extension().active_validator(&validator);
|
||||
if active_validator.is_none() {
|
||||
Err(Error::NotValidator)?;
|
||||
}
|
||||
let (validator_set, shares) = active_validator.unwrap();
|
||||
|
||||
// Prevent a validator set from generating keys multiple times. Only the first-voted-in keys
|
||||
// should be acknowledged.
|
||||
let global_validator_set = self.env().extension().global_validator_set_id();
|
||||
if self.updated_at.get(validator_set) == Some(global_validator_set) {
|
||||
Err(Error::AlreadyGeneratedKeys)?;
|
||||
}
|
||||
|
||||
// Prevent a validator from voting on keys multiple times.
|
||||
let keys_hash = Self::hash(&keys);
|
||||
if self.voted.get((validator, keys_hash)).is_some() {
|
||||
Err(Error::AlreadyVoted)?;
|
||||
}
|
||||
self.voted.insert((validator, keys_hash), &());
|
||||
|
||||
let votes =
|
||||
if let Some(votes) = self.votes.get((global_validator_set, validator_set, keys_hash)) {
|
||||
self.env().emit_event(Vote {
|
||||
validator,
|
||||
global_validator_set,
|
||||
validator_set,
|
||||
hash: keys_hash,
|
||||
keys: None,
|
||||
});
|
||||
votes + shares
|
||||
} else {
|
||||
self.env().emit_event(Vote {
|
||||
validator,
|
||||
global_validator_set,
|
||||
validator_set,
|
||||
hash: keys_hash,
|
||||
keys: Some(keys.clone()),
|
||||
});
|
||||
shares
|
||||
};
|
||||
// We could skip writing this if we've reached consensus, yet best to keep our ducks in a row
|
||||
self.votes.insert((global_validator_set, validator_set, keys_hash), &votes);
|
||||
|
||||
// If we've reached consensus, action this.
|
||||
if votes == self.env().extension().validator_set_shares(validator_set) {
|
||||
self.updated_at.insert(validator_set, &global_validator_set);
|
||||
for (k, key) in keys.iter().enumerate() {
|
||||
if let Some(key) = key {
|
||||
self.keys.insert((validator_set, Curve::try_from(k).unwrap()), key);
|
||||
}
|
||||
}
|
||||
self.env().emit_event(KeyGen { global_validator_set, validator_set, hash: keys_hash });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use ink_env::{
|
||||
hash::{CryptoHash, Blake2x256},
|
||||
AccountId,
|
||||
topics::PrefixedValue,
|
||||
};
|
||||
use ink_lang as ink;
|
||||
|
||||
use serai_extension::{test_validators, test_register};
|
||||
|
||||
use super::*;
|
||||
|
||||
type Event = <Multisig as ::ink_lang::reflect::ContractEventBase>::Type;
|
||||
|
||||
lazy_static! {
|
||||
static ref EXPECTED_GLOBAL_VALIDATOR_SET: GlobalValidatorSetId = 1;
|
||||
static ref EXPECTED_VALIDATOR_SET: ValidatorSetIndex = 0;
|
||||
static ref KEYS: Vec<Option<Key>> = vec![Some(vec![0, 1]), Some(vec![2, 3])];
|
||||
static ref EXPECTED_HASH: KeysHash = {
|
||||
let mut hash = KeysHash::default();
|
||||
ink_env::hash_encoded::<Blake2x256, _>(&*KEYS, &mut hash);
|
||||
hash
|
||||
};
|
||||
}
|
||||
|
||||
fn hash_prefixed<T: scale::Encode>(prefixed: PrefixedValue<T>) -> [u8; 32] {
|
||||
let encoded = prefixed.encode();
|
||||
let mut hash = KeysHash::default();
|
||||
if encoded.len() < 32 {
|
||||
hash[.. encoded.len()].copy_from_slice(&encoded);
|
||||
} else {
|
||||
Blake2x256::hash(&encoded, &mut hash);
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
fn assert_vote(
|
||||
event: &ink_env::test::EmittedEvent,
|
||||
expected_validator: AccountId,
|
||||
expected_keys: Option<()>,
|
||||
) {
|
||||
let decoded_event = <Event as scale::Decode>::decode(&mut &event.data[..])
|
||||
.expect("encountered invalid contract event data buffer");
|
||||
|
||||
if let Event::Vote(Vote {
|
||||
validator,
|
||||
global_validator_set,
|
||||
validator_set,
|
||||
hash,
|
||||
keys: actual_keys,
|
||||
}) = decoded_event
|
||||
{
|
||||
assert_eq!(validator, expected_validator);
|
||||
assert_eq!(global_validator_set, *EXPECTED_GLOBAL_VALIDATOR_SET);
|
||||
assert_eq!(validator_set, *EXPECTED_VALIDATOR_SET);
|
||||
assert_eq!(hash, *EXPECTED_HASH);
|
||||
assert_eq!(actual_keys.as_ref(), expected_keys.map(|_| &*KEYS));
|
||||
} else {
|
||||
panic!("invalid Vote event")
|
||||
}
|
||||
|
||||
let expected_topics = vec![
|
||||
hash_prefixed(PrefixedValue { prefix: b"", value: b"Multisig::Vote" }),
|
||||
hash_prefixed(PrefixedValue {
|
||||
prefix: b"Multisig::Vote::validator",
|
||||
value: &expected_validator,
|
||||
}),
|
||||
hash_prefixed(PrefixedValue {
|
||||
prefix: b"Multisig::Vote::global_validator_set",
|
||||
value: &*EXPECTED_GLOBAL_VALIDATOR_SET,
|
||||
}),
|
||||
hash_prefixed(PrefixedValue {
|
||||
prefix: b"Multisig::Vote::validator_set",
|
||||
value: &*EXPECTED_VALIDATOR_SET,
|
||||
}),
|
||||
hash_prefixed(PrefixedValue { prefix: b"Multisig::Vote::hash", value: &*EXPECTED_HASH }),
|
||||
];
|
||||
|
||||
for (n, (actual_topic, expected_topic)) in
|
||||
event.topics.iter().zip(expected_topics).enumerate()
|
||||
{
|
||||
assert_eq!(actual_topic, &expected_topic, "encountered invalid topic at {}", n);
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_key_gen(event: &ink_env::test::EmittedEvent) {
|
||||
let decoded_event = <Event as scale::Decode>::decode(&mut &event.data[..])
|
||||
.expect("encountered invalid contract event data buffer");
|
||||
|
||||
if let Event::KeyGen(KeyGen { global_validator_set, validator_set, hash }) = decoded_event {
|
||||
assert_eq!(global_validator_set, *EXPECTED_GLOBAL_VALIDATOR_SET);
|
||||
assert_eq!(validator_set, *EXPECTED_VALIDATOR_SET);
|
||||
assert_eq!(hash, *EXPECTED_HASH);
|
||||
} else {
|
||||
panic!("invalid KeyGen event")
|
||||
}
|
||||
|
||||
let expected_topics = vec![
|
||||
hash_prefixed(PrefixedValue { prefix: b"", value: b"Multisig::KeyGen" }),
|
||||
hash_prefixed(PrefixedValue {
|
||||
prefix: b"Multisig::KeyGen::global_validator_set",
|
||||
value: &*EXPECTED_GLOBAL_VALIDATOR_SET,
|
||||
}),
|
||||
hash_prefixed(PrefixedValue {
|
||||
prefix: b"Multisig::KeyGen::validator_set",
|
||||
value: &*EXPECTED_VALIDATOR_SET,
|
||||
}),
|
||||
hash_prefixed(PrefixedValue { prefix: b"Multisig::KeyGen::hash", value: &*EXPECTED_HASH }),
|
||||
];
|
||||
|
||||
for (n, (actual_topic, expected_topic)) in
|
||||
event.topics.iter().zip(expected_topics).enumerate()
|
||||
{
|
||||
assert_eq!(actual_topic, &expected_topic, "encountered invalid topic at {}", n);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default constructor does its job.
|
||||
#[ink::test]
|
||||
fn new() {
|
||||
let multisig = Multisig::new();
|
||||
assert_eq!(multisig.updated_at(0), Err(Error::NonExistentValidatorSet));
|
||||
}
|
||||
|
||||
/// Non-existent keys error accordingly.
|
||||
#[ink::test]
|
||||
fn non_existent_key() {
|
||||
assert_eq!(Multisig::new().key(0, 0), Err(Error::NonExistentKey));
|
||||
}
|
||||
|
||||
#[ink::test]
|
||||
fn success() {
|
||||
test_register();
|
||||
let mut multisig = Multisig::new();
|
||||
|
||||
// Test voting on keys works without issue, emitting the keys for the first vote
|
||||
let mut emitted_events = vec![];
|
||||
for (i, validator) in test_validators().iter().enumerate() {
|
||||
ink_env::test::set_caller::<ink_env::DefaultEnvironment>(*validator);
|
||||
multisig.vote(KEYS.clone()).unwrap();
|
||||
|
||||
emitted_events = ink_env::test::recorded_events().collect::<Vec<_>>();
|
||||
// If this is the last validator, it should also trigger a keygen event, hence the + 1
|
||||
assert_eq!(emitted_events.len(), (i + 1) + (i / (test_validators().len() - 1)));
|
||||
assert_vote(
|
||||
&emitted_events[i],
|
||||
*validator,
|
||||
// Only the first event for this hash should have the keys
|
||||
Some(()).filter(|_| i == 0),
|
||||
);
|
||||
}
|
||||
|
||||
// Since this should have key gen'd, verify that
|
||||
assert_eq!(multisig.updated_at(0).unwrap(), *EXPECTED_GLOBAL_VALIDATOR_SET);
|
||||
assert_key_gen(&emitted_events[test_validators().len()]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,8 +42,10 @@ exceptions = [
|
|||
{ allow = ["AGPL-3.0"], name = "ethereum-serai" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-processor" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "serai-extension" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-multisig" },
|
||||
{ allow = ["AGPL-3.0"], name = "serai-primitives" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "validator-sets-primitives" },
|
||||
{ allow = ["AGPL-3.0"], name = "validator-sets-pallet" },
|
||||
|
||||
{ allow = ["AGPL-3.0"], name = "sp-tendermint" },
|
||||
{ allow = ["AGPL-3.0"], name = "pallet-tendermint" },
|
||||
|
|
|
@ -5,29 +5,10 @@ for various connected networks, offering secure decentralized custody of foreign
|
|||
assets to applications built on it.
|
||||
|
||||
Serai is exemplified by Serai DEX, an automated-market-maker (AMM) decentralized
|
||||
exchange, allowing swapping BTC, ETH, USDC, DAI, and XMR. It is the premier
|
||||
exchange, allowing swapping Bitcoin, Ether, DAI, and Monero. It is the premier
|
||||
application of Serai.
|
||||
|
||||
### Substrate
|
||||
|
||||
Serai is based on [Substrate](https://docs.substrate.io), a blockchain framework
|
||||
offering a robust infrastructure.
|
||||
|
||||
### Smart Contracts
|
||||
|
||||
Serai offers WASM-based smart contracts. All applications are built over these
|
||||
contracts, enabling composable interactions within a mutual layer. These
|
||||
contracts are primarily written in [ink!](https://ink.substrate.io/), a
|
||||
framework for building contracts in Rust.
|
||||
|
||||
Initially, smart contract deployment will not be enabled. Solely Serai DEX will
|
||||
be available, due to the variety of economic considerations around securing the
|
||||
multisig. Serai may expand in the future with more explicitly added
|
||||
applications, each with tailored economic models, or may enable arbitrary
|
||||
contract deployment. At this time, we solely plan for Serai DEX's availabiliy.
|
||||
|
||||
### Application Calls
|
||||
|
||||
Applications, such as Serai DEX, may be called via calling their relevant smart
|
||||
contracts. At a low level, this is done via specifying the address of the
|
||||
contract being interacted with, along with SCALE-encoded calldata.
|
||||
|
|
|
@ -5,41 +5,30 @@
|
|||
These are the list of types used to represent various properties within the
|
||||
protocol.
|
||||
|
||||
| Alias | Shorthand | Type |
|
||||
|-------------------------|-----------|----------|
|
||||
| Amount | Amount | u64 |
|
||||
| Curve | Curve | u16 |
|
||||
| Coin | Coin | u32 |
|
||||
| Global Validator Set ID | GVSID | u32 |
|
||||
| Validator Set Index | VS | u8 |
|
||||
| Key | Key | Vec\<u8> |
|
||||
|
||||
### Curves
|
||||
|
||||
Integer IDs for various curves. It should be noted some curves may be the same,
|
||||
yet have distinct IDs due to having different basepoints, and accordingly
|
||||
different keys. For such cases, the processor is expected to create one secret
|
||||
per curve, and then use DLEq proofs to port keys to other basepoints as needed.
|
||||
|
||||
| Curve | ID |
|
||||
|-----------|----|
|
||||
| Secp256k1 | 0 |
|
||||
| Ed25519 | 1 |
|
||||
| Alias | Type |
|
||||
|------------------------|--------------------------------|
|
||||
| Amount | u64 |
|
||||
| Coin | u32 |
|
||||
| Session | u32 |
|
||||
| Validator Set Index | u16 |
|
||||
| Validator Set Instance | (Session, Validator Set Index) |
|
||||
| Key | Vec\<u8> |
|
||||
|
||||
### Networks
|
||||
|
||||
Every network connected to Serai operates over a specific curve. While the
|
||||
processor generates keys for curves, these keys are bound to specific networks
|
||||
via an additive offset created by hashing the network's name (among other
|
||||
things). The network's key is used for all coins on that network.
|
||||
Every network connected to Serai operates over a specific curve. The processor
|
||||
generates a distinct set of keys per network. Beyond the key-generation itself
|
||||
being isolated, the generated keys are further bound to their respective
|
||||
networks via an additive offset created by hashing the network's name (among
|
||||
other properties). The network's key is used for all coins on that network.
|
||||
|
||||
Networks are not acknowledged by the Serai network, solely by the processor.
|
||||
|
||||
| Network | Curve |
|
||||
|----------|-------|
|
||||
| Bitcoin | 0 |
|
||||
| Ethereum | 0 |
|
||||
| Monero | 1 |
|
||||
|----------|-----------|
|
||||
| Bitcoin | Secp256k1 |
|
||||
| Ethereum | Secp256k1 |
|
||||
| Monero | Ed25519 |
|
||||
|
||||
### Coins
|
||||
|
||||
|
@ -48,7 +37,6 @@ Coins exist over a network and have a distinct integer ID.
|
|||
| Coin | Network | ID |
|
||||
|----------|----------|----|
|
||||
| Bitcoin | Bitcoin | 0 |
|
||||
| Ethereum | Ethereum | 1 |
|
||||
| USDC | Ethereum | 2 |
|
||||
| DAI | Ethereum | 3 |
|
||||
| Monero | Monero | 4 |
|
||||
| Ether | Ethereum | 1 |
|
||||
| DAI | Ethereum | 2 |
|
||||
| Monero | Monero | 3 |
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
# Multisig
|
||||
|
||||
Multisigs are confirmed on-chain by the `Multisig` contract. While the processor
|
||||
does create the multisig, and sign for it, making it irrelevant to the chain,
|
||||
confirming it on-chain solves the question of if the multisig was successfully
|
||||
created or not. If each processor simply asked all other processors for
|
||||
confirmation, votes lost to the network would create an inconsistent view. This
|
||||
is a form of the Byzantine Generals Problem, which can be resolved by placing
|
||||
votes within a BFT system.
|
||||
|
||||
Confirmation requires all participants confirm the new set of keys. While this
|
||||
isn't BFT, despite the voting process being BFT, it avoids the scenario where
|
||||
only t (where t is the BFT threshold, as used in the t-of-n multisig)
|
||||
successfully generated shares, actually creating a t-of-t multisig in practice,
|
||||
which is not BFT. This does mean a single node can delay a churn, which is
|
||||
expected to be handled via a combination of slashing, and if necessary, removal.
|
||||
|
||||
Validators are allowed to vote multiple times across sets of keys, with the
|
||||
first set to be confirmed becoming the set of keys for that validator set. These
|
||||
keys remain valid for the validator set until it is changed. If a validator set
|
||||
remains consistent despite the global validator set updating, their keys carry.
|
||||
If a validator set adds a new member, and then loses them, their historical keys
|
||||
are not reused.
|
||||
|
||||
Once new keys are confirmed for a given validator set, they become tracked and
|
||||
the recommended set of keys for incoming funds. The old keys are still eligible
|
||||
to receive funds for a provided grace period, requiring the current validator
|
||||
set to track both sets of keys. The old keys are also still used to handle all
|
||||
outgoing payments as well, until the end of the grace period, at which point
|
||||
they're no longer eligible to receive funds and they forward all of their funds
|
||||
to the new set of keys.
|
||||
|
||||
### `vote(keys: Vec<Option<Key>>)`
|
||||
|
||||
Lets a validator vote on a set of keys for their validator set.
|
19
docs/protocol/Staking.md
Normal file
19
docs/protocol/Staking.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Staking
|
||||
|
||||
Serai's staking pallet offers a DPoS system. All stake which enters the system
|
||||
is delegated somewhere. Delegates can then bond their stake to different
|
||||
validator sets, justifying their inclusion and providing financial security.
|
||||
|
||||
Delegators may transfer stake whenever, so long as that stake isn't actively
|
||||
bonded. Delegators may also unstake whenever, so long as the prior condition
|
||||
is still met.
|
||||
|
||||
### Stake (message)
|
||||
|
||||
- `delegate` (Address): Address to delegate the newly added stake to.
|
||||
- `amount` (Amount): Amount to stake and delegate.
|
||||
|
||||
### Unstake (message)
|
||||
|
||||
- `delegate` (Address): Address the stake is currently delegated to.
|
||||
- `amount` (Amount): Amount to unstake.
|
|
@ -2,29 +2,80 @@
|
|||
|
||||
Validator Sets are defined at the protocol level, with the following parameters:
|
||||
|
||||
- `index` (VS): Validator set index, a global key atomically increasing
|
||||
from 0.
|
||||
- `bond` (Amount): Amount of bond per key-share of this validator set.
|
||||
- `coins` (Vec\<Coin>): Coins managed by this validator set.
|
||||
- `bond` (Amount): Amount of bond per key-share.
|
||||
- `coins` (Vec\<Coin>): List of coins within this set.
|
||||
- `participants` (Vec\<Coin>): List of participants within this set.
|
||||
|
||||
At launch, there will solely be validator set 0, managing Bitcoin, Ethereum,
|
||||
USDC, DAI, and Monero.
|
||||
Validator Sets are referred to by `ValidatorSetIndex` yet have their data
|
||||
accessible via `ValidatorSetInstance`.
|
||||
|
||||
### Multisig Management
|
||||
|
||||
Every validator set is expected to form a t-of-n multisig, where n is the amount
|
||||
of key shares in the validator set and t is `n / 3 * 2 + 1`, per curve required
|
||||
by its coins. This multisig is secure to hold funds up to 67% of the validator
|
||||
set's bond value. If funds exceed that threshold, there's more value in the
|
||||
multisig than in the supermajority of bond that must be put forth to control it.
|
||||
At launch, there will solely be Validator Set 0, managing Bitcoin, Ether, DAI,
|
||||
and Monero.
|
||||
|
||||
### Participation in the BFT process
|
||||
|
||||
All validator sets participate in the BFT process. Specifically, a block
|
||||
containing `Oraclization`s for a coin must be approved by the BFT majority of
|
||||
the validator set responsible for it, along with the BFT majority of the network
|
||||
by bond.
|
||||
All Validator Sets participate in the BFT process described under
|
||||
[Consensus](./Consensus.md). Specifically, a block containing In Instructions
|
||||
for a coin must be approved by the BFT majority of the Validator Set responsible
|
||||
for it, along with the BFT majority of the network by bond.
|
||||
|
||||
At this time, `Oraclization`s for a coin are only expected to be included when a
|
||||
validator from the validator set managing the coin is the producer of the block
|
||||
At this time, In Instructions for a coin are only expected to be included when a
|
||||
validator from the Validator Set managing the coin is the producer of the block
|
||||
in question.
|
||||
|
||||
Since there is currently only one Validator Set, the aforementioned BFT
|
||||
conditions collapse to simply the BFT majority by bond. Ensuring BFT majority
|
||||
per responsible Validator Set is accordingly unimplemented for now.
|
||||
|
||||
### Multisig
|
||||
|
||||
Every Validator Set is expected to form a `t`-of-`n` multisig, where `n` is the
|
||||
amount of key shares in the Validator Set and `t` is `n * 2 / 3 + 1`, for each
|
||||
of its networks. This multisig is secure to hold coins up to 67% of the
|
||||
Validator Set's bonded value. If the coins exceed that threshold, there's more
|
||||
value in the multisig than in the supermajority of bond that must be put forth
|
||||
to control it. Accordingly, it'd be no longer financially secure, and it MUST
|
||||
reject newly added coins which would cross that threshold.
|
||||
|
||||
### Multisig Creation
|
||||
|
||||
Multisigs are created by processors, communicating via their Coordinators.
|
||||
They're then confirmed on chain via the `validator-sets` pallet. This is done by
|
||||
having 100% of participants agree on the resulting group key. While this isn't
|
||||
fault tolerant, a malicious actor who forces a `t`-of-`n` multisig to be
|
||||
`t`-of-`n-1` reduces the fault tolerance of the multisig which is a greater
|
||||
issue. If a node does prevent multisig creation, other validators should issue
|
||||
slashes for it/remove it from the Validator Set entirely.
|
||||
|
||||
Due to the fact multiple key generations may occur to account for
|
||||
faulty/malicious nodes, voting on multiple keys for a single coin is allowed,
|
||||
with the first key to be confirmed becoming the key for that coin.
|
||||
|
||||
Placing it on chain also solves the question of if the multisig was successfully
|
||||
created or not. Processors cannot simply ask each other if they succeeded
|
||||
without creating an instance of the Byzantine Generals Problem. Placing results
|
||||
within a Byzantine Fault Tolerant system resolves this.
|
||||
|
||||
### Multisig Lifetime
|
||||
|
||||
The keys for a Validator Set remain valid until its participants change. If a
|
||||
Validator Set adds a new member, and then they leave, the set's historical keys
|
||||
are not reused.
|
||||
|
||||
### Multisig Handoffs
|
||||
|
||||
Once new keys are confirmed for a given Validator Set, they become tracked and
|
||||
the recommended set of keys for incoming coins. The old keys are still eligible
|
||||
to receive coins for a provided grace period, requiring the current Validator
|
||||
Set to track both sets of keys. The old keys are also prioritized for handling
|
||||
outbound transfers, until the end of the grace period, at which point they're
|
||||
no longer eligible to receive coins and they forward all of their coins to the
|
||||
new set of keys. It is only then that validators in the previous instance of the
|
||||
set, yet not the current instance, may unbond their stake.
|
||||
|
||||
### Vote (message)
|
||||
|
||||
- `coin` (Coin): Coin whose key is being voted for.
|
||||
- `key` (Key): Key being voted on.
|
||||
|
||||
Once a key is voted on by every member, it's adopted as detailed above.
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
# Validators
|
||||
|
||||
### Register (message)
|
||||
|
||||
- `validator` (signer): Address which will be the validator on Substrate.
|
||||
- `manager` (signer): Address which will manage this validator.
|
||||
- `set` (VS): Validator set being joined.
|
||||
|
||||
Marks `validator` as a validator candidate for the specified validator set,
|
||||
enabling delegation.
|
||||
|
||||
### Delegate (message)
|
||||
|
||||
- `delegator` (signer): Address delegating funds to `validator`.
|
||||
- `validator` (address): Registered validator being delegated to.
|
||||
- `amount` (Amount): Amount of funds being delegated to `validator`.
|
||||
|
||||
Delegated funds will be removed from `delegator`'s wallet and moved to
|
||||
`validator`'s bond. `amount` must be a multiple of the validator set's bond, and
|
||||
`delegator` must be `validator`'s manager.
|
||||
|
||||
### Undelegate (message)
|
||||
|
||||
- `delegator` (signer): Address removing delegated funds from `validator`.
|
||||
- `validator` (address): Registered validator no longer being delegated to.
|
||||
- `amount` (Amount): Amount of funds no longer being delegated to
|
||||
`validator`.
|
||||
|
||||
`delegator` must be `validator`'s manager, and `amount` must be a multiple of
|
||||
the validator set's bond. `validator` is scheduled to lose an according amount
|
||||
of key shares at the next churn, and once they do, the specified amount will be
|
||||
moved from `validator`'s bond to `delegator`'s wallet.
|
||||
|
||||
`validator`'s bond must be at least the validator set's bond after the
|
||||
undelegation.
|
||||
|
||||
### Resign (message)
|
||||
|
||||
- `manager` (signer): Manager of `validator`.
|
||||
- `validator` (address): Validator being removed from the pool/candidacy.
|
||||
|
||||
If `validator` is active, they will be removed at the next churn. If they are
|
||||
solely a candidate, they will no longer be eligible for delegations. All bond is
|
||||
refunded after their removal.
|
|
@ -59,6 +59,10 @@ sc-rpc-api = { git = "https://github.com/serai-dex/substrate" }
|
|||
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" }
|
||||
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" }
|
||||
|
||||
serai-primitives = { path = "../serai/primitives" }
|
||||
|
||||
validator-sets-pallet = { path = "../validator-sets/pallet" }
|
||||
|
||||
sp-tendermint = { path = "../tendermint/primitives" }
|
||||
pallet-tendermint = { path = "../tendermint/pallet", default-features = false }
|
||||
serai-runtime = { path = "../runtime" }
|
||||
|
@ -73,5 +77,5 @@ runtime-benchmarks = [
|
|||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-benchmarking-cli/runtime-benchmarks",
|
||||
|
||||
"serai-runtime/runtime-benchmarks"
|
||||
"serai-runtime/runtime-benchmarks",
|
||||
]
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use sp_core::{Pair as PairTrait, sr25519::Pair};
|
||||
use sc_service::ChainType;
|
||||
|
||||
use sp_core::{Pair as PairTrait, sr25519::Pair};
|
||||
use serai_primitives::{Amount, COIN, Coin};
|
||||
use pallet_tendermint::crypto::Public;
|
||||
|
||||
use serai_runtime::{
|
||||
WASM_BINARY, AccountId, opaque::SessionKeys, GenesisConfig, SystemConfig, BalancesConfig,
|
||||
SessionConfig,
|
||||
ValidatorSetsConfig, SessionConfig,
|
||||
};
|
||||
|
||||
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
|
||||
|
@ -34,6 +35,12 @@ fn testnet_genesis(
|
|||
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
|
||||
},
|
||||
transaction_payment: Default::default(),
|
||||
|
||||
validator_sets: ValidatorSetsConfig {
|
||||
bond: Amount(1_000_000) * COIN,
|
||||
coins: Coin(4),
|
||||
participants: validators.iter().map(|name| account_id_from_name(name)).collect(),
|
||||
},
|
||||
session: SessionConfig { keys: validators.iter().map(|name| session_key(*name)).collect() },
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,13 +37,10 @@ frame-executive = { git = "https://github.com/serai-dex/substrate", default-feat
|
|||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
||||
|
||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
pallet-randomness-collective-flip = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
pallet-balances = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
pallet-contracts-primitives = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
pallet-contracts = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
validator-sets-pallet = { path = "../validator-sets/pallet", default-features = false }
|
||||
pallet-session = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
pallet-tendermint = { path = "../tendermint/pallet", default-features = false }
|
||||
|
||||
|
@ -77,13 +74,10 @@ std = [
|
|||
"frame-executive/std",
|
||||
|
||||
"pallet-timestamp/std",
|
||||
"pallet-randomness-collective-flip/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-transaction-payment/std",
|
||||
|
||||
"pallet-contracts/std",
|
||||
"pallet-contracts-primitives/std",
|
||||
|
||||
"validator-sets-pallet/std",
|
||||
"pallet-session/std",
|
||||
"pallet-tendermint/std",
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use sp_version::NativeVersion;
|
|||
use sp_version::RuntimeVersion;
|
||||
|
||||
use frame_support::{
|
||||
traits::{ConstBool, ConstU8, ConstU32, ConstU64},
|
||||
traits::{ConstU8, ConstU32, ConstU64},
|
||||
weights::{
|
||||
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
|
||||
IdentityFee, Weight,
|
||||
|
@ -130,7 +130,6 @@ parameter_types! {
|
|||
.get(DispatchClass::Normal)
|
||||
.max_total
|
||||
.unwrap_or(BlockWeights::get().max_block);
|
||||
pub Schedule: pallet_contracts::Schedule<Runtime> = Default::default();
|
||||
}
|
||||
|
||||
impl frame_system::Config for Runtime {
|
||||
|
@ -163,8 +162,6 @@ impl frame_system::Config for Runtime {
|
|||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
}
|
||||
|
||||
impl pallet_randomness_collective_flip::Config for Runtime {}
|
||||
|
||||
impl pallet_timestamp::Config for Runtime {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
|
@ -193,34 +190,6 @@ impl pallet_transaction_payment::Config for Runtime {
|
|||
type FeeMultiplierUpdate = ();
|
||||
}
|
||||
|
||||
impl pallet_contracts::Config for Runtime {
|
||||
type Time = Timestamp;
|
||||
type Randomness = RandomnessCollectiveFlip;
|
||||
type Currency = Balances;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
|
||||
type CallFilter = frame_support::traits::Nothing;
|
||||
type DepositPerItem = DepositPerItem;
|
||||
type DepositPerByte = DepositPerByte;
|
||||
type CallStack = [pallet_contracts::Frame<Self>; 31];
|
||||
type WeightPrice = pallet_transaction_payment::Pallet<Self>;
|
||||
type WeightInfo = pallet_contracts::weights::SubstrateWeight<Self>;
|
||||
type ChainExtension = ();
|
||||
type DeletionQueueDepth = DeletionQueueDepth;
|
||||
type DeletionWeightLimit = DeletionWeightLimit;
|
||||
type Schedule = Schedule;
|
||||
type AddressGenerator = pallet_contracts::DefaultAddressGenerator;
|
||||
|
||||
type MaxCodeLen = ConstU32<{ 128 * 1024 }>;
|
||||
type MaxStorageKeyLen = ConstU32<128>;
|
||||
|
||||
type UnsafeUnstableInterface = ConstBool<false>;
|
||||
type MaxDebugBufferLen = ConstU32<255>;
|
||||
}
|
||||
|
||||
impl pallet_tendermint::Config for Runtime {}
|
||||
|
||||
const SESSION_LENGTH: BlockNumber = 5 * DAYS;
|
||||
type Sessions = PeriodicSessions<ConstU32<{ SESSION_LENGTH }>, ConstU32<{ SESSION_LENGTH }>>;
|
||||
|
||||
|
@ -231,6 +200,10 @@ impl Convert<Public, Option<Public>> for IdentityValidatorIdOf {
|
|||
}
|
||||
}
|
||||
|
||||
impl validator_sets_pallet::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
impl pallet_session::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ValidatorId = AccountId;
|
||||
|
@ -243,6 +216,8 @@ impl pallet_session::Config for Runtime {
|
|||
type WeightInfo = pallet_session::weights::SubstrateWeight<Runtime>;
|
||||
}
|
||||
|
||||
impl pallet_tendermint::Config for Runtime {}
|
||||
|
||||
pub type Address = AccountId;
|
||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||
|
@ -274,11 +249,11 @@ construct_runtime!(
|
|||
UncheckedExtrinsic = UncheckedExtrinsic
|
||||
{
|
||||
System: frame_system,
|
||||
RandomnessCollectiveFlip: pallet_randomness_collective_flip,
|
||||
Timestamp: pallet_timestamp,
|
||||
Balances: pallet_balances,
|
||||
TransactionPayment: pallet_transaction_payment,
|
||||
Contracts: pallet_contracts,
|
||||
|
||||
ValidatorSets: validator_sets_pallet,
|
||||
Session: pallet_session,
|
||||
Tendermint: pallet_tendermint,
|
||||
}
|
||||
|
|
25
substrate/serai/primitives/Cargo.toml
Normal file
25
substrate/serai/primitives/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "serai-primitives"
|
||||
version = "0.1.0"
|
||||
description = "Primitives for the Serai blockchain"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/serai/primitives"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
[features]
|
||||
std = ["scale/std", "scale-info/std", "serde", "sp-core/std", "sp-std/std"]
|
||||
default = ["std"]
|
36
substrate/serai/primitives/src/lib.rs
Normal file
36
substrate/serai/primitives/src/lib.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use core::ops::{Add, Mul};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// The type used for amounts.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct Amount(pub u64);
|
||||
|
||||
impl Add<Amount> for Amount {
|
||||
type Output = Amount;
|
||||
fn add(self, other: Amount) -> Amount {
|
||||
Amount(self.0 + other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Amount> for Amount {
|
||||
type Output = Amount;
|
||||
fn mul(self, other: Amount) -> Amount {
|
||||
Amount(self.0 * other.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// One whole coin with eight decimals.
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
pub const COIN: Amount = Amount(1_000_000_00);
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct Coin(pub u32);
|
41
substrate/validator-sets/pallet/Cargo.toml
Normal file
41
substrate/validator-sets/pallet/Cargo.toml
Normal file
|
@ -0,0 +1,41 @@
|
|||
[package]
|
||||
name = "validator-sets-pallet"
|
||||
version = "0.1.0"
|
||||
description = "Validator sets pallet"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/validator-sets/pallet"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../serai/primitives", default-features = false }
|
||||
validator-sets-primitives = { path = "../primitives", default-features = false }
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
"frame-system/std",
|
||||
"frame-support/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"validator-sets-primitives/std",
|
||||
]
|
||||
|
||||
runtime-benchmarks = [
|
||||
"frame-system/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
]
|
||||
|
||||
default = ["std"]
|
207
substrate/validator-sets/pallet/src/lib.rs
Normal file
207
substrate/validator-sets/pallet/src/lib.rs
Normal file
|
@ -0,0 +1,207 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use scale::{Encode, Decode};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use frame_system::pallet_prelude::*;
|
||||
use frame_support::pallet_prelude::*;
|
||||
|
||||
use serai_primitives::*;
|
||||
use validator_sets_primitives::*;
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config + TypeInfo {
|
||||
type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;
|
||||
}
|
||||
|
||||
#[pallet::genesis_config]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen)]
|
||||
pub struct GenesisConfig<T: Config> {
|
||||
/// Bond requirement to join the initial validator set.
|
||||
/// Every participant at genesis will automatically be assumed to have this much bond.
|
||||
/// This bond cannot be withdrawn however as there's no stake behind it.
|
||||
pub bond: Amount,
|
||||
/// Amount of coins to spawn the network with in the initial validator set.
|
||||
pub coins: Coin,
|
||||
/// List of participants to place in the genesis set.
|
||||
pub participants: Vec<T::AccountId>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Config> Default for GenesisConfig<T> {
|
||||
fn default() -> Self {
|
||||
GenesisConfig { bond: Amount(1), coins: Coin(0), participants: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
// Max of 16 coins per validator set
|
||||
// At launch, we'll have BTC, ETH, DAI, and XMR
|
||||
// In the future, these will be split into separate validator sets, so we're already not
|
||||
// planning expansion beyond just a few coins per validator set
|
||||
// The only case which really makes sense for multiple coins in a validator set is:
|
||||
// 1) The coins are small, easy to run, and make no sense to be in their own set
|
||||
// In this case, it's still hard to ask validators to run 16 different nodes
|
||||
// 2) The coins are all on the same network yet there's no DEX on-chain
|
||||
// In these cases, it'd be hard to find and justify 16 different coins from that single chain
|
||||
// This could probably be just 8, yet 16 is a hedge for the unforseen
|
||||
// If necessary, this can be increased with a fork
|
||||
type MaxCoinsPerSet = ConstU32<16>;
|
||||
|
||||
// Support keys up to 96 bytes (BLS12-381 G2)
|
||||
const MAX_KEY_LEN: u32 = 96;
|
||||
type MaxKeyLen = ConstU32<MAX_KEY_LEN>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
pub struct ValidatorSet<T: Config> {
|
||||
bond: Amount,
|
||||
coins: BoundedVec<Coin, MaxCoinsPerSet>,
|
||||
|
||||
// Participant and their amount bonded to this set
|
||||
// Limit each set to 100 participants for now
|
||||
participants: BoundedVec<(T::AccountId, Amount), ConstU32<100>>,
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
#[pallet::generate_store(pub(super) trait Store)]
|
||||
pub struct Pallet<T>(PhantomData<T>);
|
||||
|
||||
/// The details of a validator set instance.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn validator_set)]
|
||||
pub type ValidatorSets<T: Config> =
|
||||
StorageMap<_, Twox64Concat, ValidatorSetInstance, ValidatorSet<T>, OptionQuery>;
|
||||
|
||||
type Key = BoundedVec<u8, MaxKeyLen>;
|
||||
|
||||
/// The key for a given validator set instance coin.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn key)]
|
||||
pub type Keys<T: Config> =
|
||||
StorageMap<_, Twox64Concat, (ValidatorSetInstance, Coin), Key, OptionQuery>;
|
||||
|
||||
/// If an account has voted for a specific key or not. Prevents them from voting multiple times.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn voted)]
|
||||
pub type Voted<T: Config> = StorageMap<_, Blake2_128Concat, (T::AccountId, Key), (), OptionQuery>;
|
||||
|
||||
/// How many times a key has been voted for. Once consensus is reached, the keys will be adopted.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn vote_count)]
|
||||
pub type VoteCount<T: Config> =
|
||||
StorageMap<_, Blake2_128Concat, (ValidatorSetInstance, Coin, Key), u16, ValueQuery>;
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
|
||||
fn build(&self) {
|
||||
let mut coins = Vec::new();
|
||||
for coin in 0 .. self.coins.0 {
|
||||
coins.push(Coin(coin));
|
||||
}
|
||||
|
||||
let mut participants = Vec::new();
|
||||
for participant in self.participants.clone() {
|
||||
participants.push((participant, self.bond));
|
||||
}
|
||||
|
||||
ValidatorSets::<T>::set(
|
||||
ValidatorSetInstance(Session(0), ValidatorSetIndex(0)),
|
||||
Some(ValidatorSet {
|
||||
bond: self.bond,
|
||||
coins: BoundedVec::try_from(coins).unwrap(),
|
||||
participants: BoundedVec::try_from(participants).unwrap(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
Vote {
|
||||
voter: T::AccountId,
|
||||
instance: ValidatorSetInstance,
|
||||
coin: Coin,
|
||||
key: Key,
|
||||
// Amount of votes the key now has
|
||||
votes: u16,
|
||||
},
|
||||
KeyGen {
|
||||
instance: ValidatorSetInstance,
|
||||
coin: Coin,
|
||||
key: Key,
|
||||
},
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
/// Validator Set doesn't exist.
|
||||
NonExistentValidatorSet,
|
||||
/// Non-validator is voting.
|
||||
NotValidator,
|
||||
/// Validator Set already generated keys.
|
||||
AlreadyGeneratedKeys,
|
||||
/// Vvalidator has already voted for these keys.
|
||||
AlreadyVoted,
|
||||
}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(0)] // TODO
|
||||
pub fn vote(
|
||||
origin: OriginFor<T>,
|
||||
index: ValidatorSetIndex,
|
||||
coin: Coin,
|
||||
key: Key,
|
||||
) -> DispatchResult {
|
||||
let signer = ensure_signed(origin)?;
|
||||
// TODO: Do we need to check the key is within the length bounds?
|
||||
// The docs suggest the BoundedVec will create/write, yet not read, which could be an issue
|
||||
// if it can be passed in
|
||||
|
||||
// TODO: Get session
|
||||
let session: Session = Session(0);
|
||||
|
||||
// Confirm a key hasn't been set for this set instance
|
||||
let instance = ValidatorSetInstance(session, index);
|
||||
if Keys::<T>::get((instance, coin)).is_some() {
|
||||
Err(Error::<T>::AlreadyGeneratedKeys)?;
|
||||
}
|
||||
|
||||
// Confirm the signer is a validator in the set
|
||||
let set = ValidatorSets::<T>::get(instance).ok_or(Error::<T>::NonExistentValidatorSet)?;
|
||||
|
||||
if set.participants.iter().any(|participant| participant.0 == signer) {
|
||||
Err(Error::<T>::NotValidator)?;
|
||||
}
|
||||
|
||||
// Confirm this signer hasn't already voted for these keys
|
||||
if Voted::<T>::get((&signer, &key)).is_some() {
|
||||
Err(Error::<T>::AlreadyVoted)?;
|
||||
}
|
||||
Voted::<T>::set((&signer, &key), Some(()));
|
||||
|
||||
// Add their vote
|
||||
let votes = VoteCount::<T>::mutate((instance, coin, &key), |value| {
|
||||
*value += 1;
|
||||
*value
|
||||
});
|
||||
|
||||
Self::deposit_event(Event::Vote { voter: signer, instance, coin, key: key.clone(), votes });
|
||||
|
||||
// If we've reached consensus, set the key
|
||||
if usize::try_from(votes).unwrap() == set.participants.len() {
|
||||
Keys::<T>::set((instance, coin), Some(key.clone()));
|
||||
Self::deposit_event(Event::KeyGen { instance, coin, key });
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support session rotation
|
||||
}
|
||||
|
||||
pub use pallet::*;
|
25
substrate/validator-sets/primitives/Cargo.toml
Normal file
25
substrate/validator-sets/primitives/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "validator-sets-primitives"
|
||||
version = "0.1.0"
|
||||
description = "Primitives for validator sets"
|
||||
license = "AGPL-3.0-only"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/substrate/validator-sets/primitives"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
[features]
|
||||
std = ["scale/std", "scale-info/std", "serde", "sp-core/std", "sp-std/std"]
|
||||
default = ["std"]
|
15
substrate/validator-sets/primitives/LICENSE
Normal file
15
substrate/validator-sets/primitives/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
|||
AGPL-3.0-only license
|
||||
|
||||
Copyright (c) 2022 Luke Parker
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License Version 3 as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
21
substrate/validator-sets/primitives/src/lib.rs
Normal file
21
substrate/validator-sets/primitives/src/lib.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// The type used to identify a specific session of validators.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct Session(pub u32);
|
||||
|
||||
/// The type used to identify a validator set.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct ValidatorSetIndex(pub u16);
|
||||
|
||||
/// The type used to identify a specific validator set during a specific session.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
pub struct ValidatorSetInstance(pub Session, pub ValidatorSetIndex);
|
Loading…
Reference in a new issue