mirror of
https://github.com/serai-dex/serai.git
synced 2025-04-22 22:18:15 +00:00
Redo primitives, abi
Consolidates all primitives into a single crate. We didn't benefit from its fragmentation. I'm hesitant to say the new internal-organization is better (it may be just as clunky), but it's at least in a single crate (not spread out over micro-crates). The ABI is the most distinct. We now entirely own it. Block header hashes don't directly commit to any BABE data (avoiding potentially ~4 KB headers upon session changes), and are hashed as borsh (a more widely used codec than SCALE). There are still Substrate variants, using SCALE and with the BABE data, but they're prunable from a protocol design perspective. Defines a transaction as a Vec of Calls, allowing atomic operations.
This commit is contained in:
parent
dd95494d9c
commit
2d8f70036a
52 changed files with 2340 additions and 2217 deletions
Cargo.lock
substrate
abi
coins/primitives/src
genesis-liquidity/primitives/src
in-instructions/primitives/src
primitives
runtime
signals/primitives/src
validator-sets/primitives/src
242
Cargo.lock
generated
242
Cargo.lock
generated
|
@ -1052,7 +1052,7 @@ dependencies = [
|
|||
"bitflags 2.8.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"proc-macro2",
|
||||
|
@ -2685,7 +2685,7 @@ checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
|
|||
[[package]]
|
||||
name = "fork-tree"
|
||||
version = "13.0.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
@ -2718,7 +2718,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
|
|||
[[package]]
|
||||
name = "frame-benchmarking"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-support-procedural",
|
||||
|
@ -2742,7 +2742,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-executive"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
|
@ -2771,7 +2771,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-support"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"bitflags 1.3.2",
|
||||
|
@ -2806,7 +2806,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-support-procedural"
|
||||
version = "31.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"cfg-expr",
|
||||
|
@ -2825,7 +2825,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-support-procedural-tools"
|
||||
version = "13.0.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support-procedural-tools-derive",
|
||||
"proc-macro-crate 3.2.0",
|
||||
|
@ -2837,7 +2837,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-support-procedural-tools-derive"
|
||||
version = "12.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2847,7 +2847,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-system"
|
||||
version = "39.1.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"frame-support",
|
||||
|
@ -2866,7 +2866,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-system-rpc-runtime-api"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"sp-api",
|
||||
|
@ -2875,7 +2875,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "frame-try-runtime"
|
||||
version = "0.45.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"parity-scale-codec",
|
||||
|
@ -5618,7 +5618,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-authorship"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
|
@ -5631,7 +5631,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-babe"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
@ -5654,7 +5654,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-grandpa"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
@ -5676,7 +5676,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-session"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
|
@ -5697,7 +5697,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-timestamp"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
@ -5715,7 +5715,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-transaction-payment"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
|
@ -5731,7 +5731,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-transaction-payment-rpc"
|
||||
version = "42.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"jsonrpsee",
|
||||
"pallet-transaction-payment-rpc-runtime-api",
|
||||
|
@ -5747,7 +5747,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "pallet-transaction-payment-rpc-runtime-api"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"pallet-transaction-payment",
|
||||
"parity-scale-codec",
|
||||
|
@ -6306,7 +6306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d0f3e5beed80eb580c68e2c600937ac2c4eedabdfd5ef1e5b7ea4f3fba84497b"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"itertools 0.13.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
|
@ -6326,7 +6326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.13.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
|
@ -7076,7 +7076,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-allocator"
|
||||
version = "30.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"log",
|
||||
"sp-core",
|
||||
|
@ -7087,7 +7087,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-authority-discovery"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -7117,7 +7117,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-basic-authorship"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"futures-timer",
|
||||
|
@ -7139,7 +7139,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-block-builder"
|
||||
version = "0.43.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"sp-api",
|
||||
|
@ -7154,7 +7154,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-chain-spec"
|
||||
version = "41.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"log",
|
||||
|
@ -7180,7 +7180,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-chain-spec-derive"
|
||||
version = "12.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
|
@ -7191,7 +7191,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-cli"
|
||||
version = "0.50.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"chrono",
|
||||
|
@ -7232,7 +7232,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-client-api"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"futures",
|
||||
|
@ -7258,7 +7258,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-client-db"
|
||||
version = "0.45.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"kvdb",
|
||||
|
@ -7284,7 +7284,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-consensus"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -7308,7 +7308,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-consensus-babe"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fork-tree",
|
||||
|
@ -7344,7 +7344,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-consensus-epochs"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"fork-tree",
|
||||
"parity-scale-codec",
|
||||
|
@ -7357,7 +7357,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-consensus-grandpa"
|
||||
version = "0.33.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"array-bytes",
|
||||
|
@ -7401,7 +7401,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-consensus-slots"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -7424,7 +7424,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-executor"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.12.3",
|
||||
|
@ -7446,7 +7446,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-executor-common"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"sc-allocator",
|
||||
"sp-maybe-compressed-blob",
|
||||
|
@ -7458,7 +7458,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-executor-wasmtime"
|
||||
version = "0.36.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"log",
|
||||
|
@ -7474,7 +7474,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-informant"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"console",
|
||||
"futures",
|
||||
|
@ -7491,7 +7491,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-keystore"
|
||||
version = "34.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"parking_lot 0.12.3",
|
||||
|
@ -7505,7 +7505,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"async-channel",
|
||||
|
@ -7551,7 +7551,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-common"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"parity-scale-codec",
|
||||
|
@ -7562,7 +7562,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-gossip"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"futures",
|
||||
|
@ -7581,7 +7581,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-light"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"async-channel",
|
||||
|
@ -7602,7 +7602,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-sync"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"async-channel",
|
||||
|
@ -7637,7 +7637,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-transactions"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"futures",
|
||||
|
@ -7656,7 +7656,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-network-types"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"ed25519-dalek",
|
||||
|
@ -7674,7 +7674,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-offchain"
|
||||
version = "43.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"num_cpus",
|
||||
|
@ -7698,7 +7698,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-proposer-metrics"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"log",
|
||||
"substrate-prometheus-endpoint",
|
||||
|
@ -7707,7 +7707,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-rpc"
|
||||
version = "43.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"jsonrpsee",
|
||||
|
@ -7737,7 +7737,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-rpc-api"
|
||||
version = "0.47.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"jsonrpsee",
|
||||
"parity-scale-codec",
|
||||
|
@ -7756,7 +7756,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-rpc-server"
|
||||
version = "20.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"forwarded-header-value",
|
||||
|
@ -7780,7 +7780,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-rpc-spec-v2"
|
||||
version = "0.48.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"futures",
|
||||
|
@ -7812,7 +7812,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-service"
|
||||
version = "0.49.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"directories",
|
||||
|
@ -7875,7 +7875,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-state-db"
|
||||
version = "0.37.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
|
@ -7886,7 +7886,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-sysinfo"
|
||||
version = "41.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"futures",
|
||||
|
@ -7907,7 +7907,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-telemetry"
|
||||
version = "28.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures",
|
||||
|
@ -7927,7 +7927,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-tracing"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"console",
|
||||
|
@ -7954,7 +7954,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-tracing-proc-macro"
|
||||
version = "11.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.2.0",
|
||||
"proc-macro2",
|
||||
|
@ -7965,7 +7965,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-transaction-pool"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -7996,7 +7996,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-transaction-pool-api"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -8012,7 +8012,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sc-utils"
|
||||
version = "18.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"futures",
|
||||
|
@ -8266,19 +8266,9 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bitvec",
|
||||
"borsh",
|
||||
"frame-support",
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
"serai-coins-primitives",
|
||||
"serai-emissions-primitives",
|
||||
"serai-genesis-liquidity-primitives",
|
||||
"serai-in-instructions-primitives",
|
||||
"serai-primitives",
|
||||
"serai-signals-primitives",
|
||||
"serai-validator-sets-primitives",
|
||||
"serde",
|
||||
"sp-consensus-babe",
|
||||
"sp-consensus-grandpa",
|
||||
"sp-core",
|
||||
"sp-runtime",
|
||||
]
|
||||
|
@ -8885,18 +8875,12 @@ dependencies = [
|
|||
name = "serai-primitives"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bech32",
|
||||
"bitvec",
|
||||
"borsh",
|
||||
"ciphersuite",
|
||||
"frame-support",
|
||||
"parity-scale-codec",
|
||||
"rand_core",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-application-crypto",
|
||||
"dkg",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
@ -9599,7 +9583,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-api"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"log",
|
||||
|
@ -9620,7 +9604,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-api-proc-macro"
|
||||
version = "21.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"blake2",
|
||||
|
@ -9634,7 +9618,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-application-crypto"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
|
@ -9646,7 +9630,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-arithmetic"
|
||||
version = "26.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"integer-sqrt",
|
||||
"num-traits",
|
||||
|
@ -9659,7 +9643,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-authority-discovery"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
|
@ -9671,7 +9655,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-block-builder"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"sp-api",
|
||||
"sp-inherents",
|
||||
|
@ -9681,7 +9665,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-blockchain"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"parity-scale-codec",
|
||||
|
@ -9700,7 +9684,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-consensus"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"futures",
|
||||
|
@ -9715,7 +9699,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-consensus-babe"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"parity-scale-codec",
|
||||
|
@ -9733,7 +9717,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-consensus-grandpa"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"finality-grandpa",
|
||||
"log",
|
||||
|
@ -9750,7 +9734,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-consensus-slots"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
|
@ -9761,7 +9745,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-core"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"array-bytes",
|
||||
"bitflags 1.3.2",
|
||||
|
@ -9802,7 +9786,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-crypto-hashing"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
|
@ -9813,7 +9797,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-crypto-hashing-proc-macro"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"sp-crypto-hashing",
|
||||
|
@ -9823,7 +9807,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-database"
|
||||
version = "10.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"kvdb",
|
||||
"parking_lot 0.12.3",
|
||||
|
@ -9832,7 +9816,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-debug-derive"
|
||||
version = "14.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -9842,7 +9826,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-externalities"
|
||||
version = "0.30.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"environmental",
|
||||
"parity-scale-codec",
|
||||
|
@ -9852,7 +9836,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-genesis-builder"
|
||||
version = "0.16.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
|
@ -9864,7 +9848,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-inherents"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"impl-trait-for-tuples",
|
||||
|
@ -9877,7 +9861,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-io"
|
||||
version = "39.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"log",
|
||||
|
@ -9898,7 +9882,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-keyring"
|
||||
version = "40.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"sp-core",
|
||||
"sp-runtime",
|
||||
|
@ -9908,7 +9892,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-keystore"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"parking_lot 0.12.3",
|
||||
|
@ -9919,7 +9903,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-maybe-compressed-blob"
|
||||
version = "11.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"thiserror 2.0.11",
|
||||
"zstd 0.13.2",
|
||||
|
@ -9928,7 +9912,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-metadata-ir"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-metadata",
|
||||
"parity-scale-codec",
|
||||
|
@ -9938,7 +9922,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-offchain"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"sp-api",
|
||||
"sp-core",
|
||||
|
@ -9948,7 +9932,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-panic-handler"
|
||||
version = "13.0.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"regex",
|
||||
|
@ -9957,7 +9941,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-rpc"
|
||||
version = "33.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"rustc-hash 1.1.0",
|
||||
"serde",
|
||||
|
@ -9967,7 +9951,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-runtime"
|
||||
version = "40.1.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"either",
|
||||
"hash256-std-hasher",
|
||||
|
@ -9993,7 +9977,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-runtime-interface"
|
||||
version = "29.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"impl-trait-for-tuples",
|
||||
|
@ -10011,7 +9995,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-runtime-interface-proc-macro"
|
||||
version = "18.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"expander",
|
||||
|
@ -10024,7 +10008,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-session"
|
||||
version = "37.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"scale-info",
|
||||
|
@ -10038,7 +10022,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-staking"
|
||||
version = "37.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples",
|
||||
"parity-scale-codec",
|
||||
|
@ -10051,7 +10035,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-state-machine"
|
||||
version = "0.44.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"hash-db",
|
||||
"log",
|
||||
|
@ -10071,12 +10055,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-std"
|
||||
version = "14.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
|
||||
[[package]]
|
||||
name = "sp-storage"
|
||||
version = "22.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
|
@ -10088,7 +10072,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-timestamp"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"parity-scale-codec",
|
||||
|
@ -10100,7 +10084,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-tracing"
|
||||
version = "17.0.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"tracing",
|
||||
|
@ -10111,7 +10095,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-transaction-pool"
|
||||
version = "35.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"sp-api",
|
||||
"sp-runtime",
|
||||
|
@ -10120,7 +10104,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-trie"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hash-db",
|
||||
|
@ -10142,7 +10126,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-version"
|
||||
version = "38.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"impl-serde",
|
||||
"parity-scale-codec",
|
||||
|
@ -10159,7 +10143,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-version-proc-macro"
|
||||
version = "15.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"proc-macro-warning",
|
||||
|
@ -10171,7 +10155,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-wasm-interface"
|
||||
version = "21.0.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"impl-trait-for-tuples",
|
||||
|
@ -10183,7 +10167,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "sp-weights"
|
||||
version = "31.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"bounded-collections",
|
||||
"parity-scale-codec",
|
||||
|
@ -10344,7 +10328,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "substrate-bip39"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"pbkdf2",
|
||||
|
@ -10369,12 +10353,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "substrate-build-script-utils"
|
||||
version = "11.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
|
||||
[[package]]
|
||||
name = "substrate-frame-rpc-system"
|
||||
version = "42.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"frame-system-rpc-runtime-api",
|
||||
"futures",
|
||||
|
@ -10393,7 +10377,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "substrate-prometheus-endpoint"
|
||||
version = "0.17.1"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"http-body-util",
|
||||
"hyper 1.4.1",
|
||||
|
@ -10407,7 +10391,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "substrate-wasm-builder"
|
||||
version = "25.0.0"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#fe70c9f0508b97f72c5f5ecf4d488feb35fa5f25"
|
||||
source = "git+https://github.com/serai-dex/polkadot-sdk?branch=serai-next#1aac7f6d602ea1c4f2059c4e80caacba3cbf3286"
|
||||
dependencies = [
|
||||
"build-helper",
|
||||
"cargo_metadata",
|
||||
|
|
|
@ -12,76 +12,38 @@ rust-version = "1.80"
|
|||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["serde"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
|
||||
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
||||
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
||||
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["serde"], optional = true }
|
||||
|
||||
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
||||
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1", default-features = false }
|
||||
serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1", default-features = false }
|
||||
serai-emissions-primitives = { path = "../emissions/primitives", version = "0.1", default-features = false }
|
||||
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1", default-features = false }
|
||||
serai-signals-primitives = { path = "../signals/primitives", version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"borsh/std",
|
||||
|
||||
"bitvec/std",
|
||||
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
"borsh?/std",
|
||||
"serde?/std",
|
||||
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
|
||||
"sp-consensus-babe/std",
|
||||
"sp-consensus-grandpa/std",
|
||||
|
||||
"frame-support/std",
|
||||
"serde?/std",
|
||||
"scale?/std",
|
||||
"scale-info?/std",
|
||||
"sp-runtime?/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"serai-coins-primitives/std",
|
||||
"serai-validator-sets-primitives/std",
|
||||
"serai-genesis-liquidity-primitives/std",
|
||||
"serai-emissions-primitives/std",
|
||||
"serai-in-instructions-primitives/std",
|
||||
"serai-signals-primitives/std",
|
||||
]
|
||||
borsh = [
|
||||
"dep:borsh",
|
||||
"serai-primitives/borsh",
|
||||
"serai-coins-primitives/borsh",
|
||||
"serai-validator-sets-primitives/borsh",
|
||||
"serai-genesis-liquidity-primitives/borsh",
|
||||
"serai-in-instructions-primitives/borsh",
|
||||
"serai-signals-primitives/borsh",
|
||||
]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
"serai-primitives/serde",
|
||||
"serai-coins-primitives/serde",
|
||||
"serai-validator-sets-primitives/serde",
|
||||
"serai-genesis-liquidity-primitives/serde",
|
||||
"serai-in-instructions-primitives/serde",
|
||||
"serai-signals-primitives/serde",
|
||||
]
|
||||
substrate = ["serde", "scale", "scale-info", "sp-runtime"]
|
||||
default = ["std"]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Luke Parker
|
||||
Copyright (c) 2023-2025 Luke Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
4
substrate/abi/README.md
Normal file
4
substrate/abi/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# serai-abi
|
||||
|
||||
Serai's ABI, inclusive to the transaction, event, and block types. MIT-licensed to ensure usability
|
||||
in a variety of contexts.
|
|
@ -1,17 +0,0 @@
|
|||
use sp_consensus_babe::EquivocationProof;
|
||||
|
||||
use serai_primitives::{Header, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<Header>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
// We could define a Babe Config here and use the literal pallet_babe::Call
|
||||
// The disadvantage to this would be the complexity and presence of junk fields such as `__Ignore`
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum Call {
|
||||
report_equivocation(ReportEquivocation),
|
||||
report_equivocation_unsigned(ReportEquivocation),
|
||||
}
|
250
substrate/abi/src/block.rs
Normal file
250
substrate/abi/src/block.rs
Normal file
|
@ -0,0 +1,250 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::{primitives::BlockHash, Transaction};
|
||||
|
||||
/// A V1 header for a block.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct HeaderV1 {
|
||||
/// The index of this block on the blockchain.
|
||||
///
|
||||
/// The genesis block has number 0.
|
||||
pub number: u64,
|
||||
/// The block this header builds upon.
|
||||
pub parent_hash: BlockHash,
|
||||
/// The root of a Merkle tree commiting to the transactions within this block.
|
||||
// TODO: Review the format of this defined by Substrate
|
||||
pub transactions_root: [u8; 32],
|
||||
/// A commitment to the consensus data used to justify adding this block to the blockchain.
|
||||
pub consensus_commitment: [u8; 32],
|
||||
}
|
||||
|
||||
/// A header for a block.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Header {
|
||||
/// A version 1 header.
|
||||
V1(HeaderV1),
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Get the hash of the header.
|
||||
pub fn number(&self) -> u64 {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { number, .. }) => *number,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn parent_hash(&self) -> BlockHash {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { parent_hash, .. }) => *parent_hash,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn transactions_root(&self) -> [u8; 32] {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { transactions_root, .. }) => *transactions_root,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn hash(&self) -> BlockHash {
|
||||
BlockHash(sp_core::blake2_256(&borsh::to_vec(self).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A block.
|
||||
///
|
||||
/// This does not guarantee consistency. The header's `transactions_root` may not match the
|
||||
/// contained transactions.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Block {
|
||||
/// The block's header.
|
||||
pub header: Header,
|
||||
/// The block's transactions.
|
||||
pub transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "substrate")]
|
||||
mod substrate {
|
||||
use scale::{Encode, Decode};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
generic::Digest,
|
||||
traits::{Header as HeaderTrait, HeaderProvider, Block as BlockTrait},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// The consensus data for a V1 header.
|
||||
///
|
||||
/// This is not considered part of the protocol proper and may be pruned in the future. It's
|
||||
/// solely considered used for consensus now.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub struct ConsensusV1 {
|
||||
/// The state root.
|
||||
state_root: H256,
|
||||
/// The consensus digests.
|
||||
digest: Digest,
|
||||
}
|
||||
|
||||
/// A V1 header for a block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub struct SubstrateHeaderV1 {
|
||||
number: u64,
|
||||
parent_hash: H256,
|
||||
transactions_root: H256,
|
||||
consensus: ConsensusV1,
|
||||
}
|
||||
|
||||
/// A header for a block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub enum SubstrateHeader {
|
||||
/// A version 1 header.
|
||||
V1(SubstrateHeaderV1),
|
||||
}
|
||||
|
||||
impl From<&SubstrateHeader> for Header {
|
||||
fn from(header: &SubstrateHeader) -> Header {
|
||||
match header {
|
||||
SubstrateHeader::V1(header) => Header::V1(HeaderV1 {
|
||||
number: header.number,
|
||||
parent_hash: BlockHash(header.parent_hash.0),
|
||||
transactions_root: header.transactions_root.0,
|
||||
consensus_commitment: sp_core::blake2_256(&header.consensus.encode()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, sp_runtime::Serialize)]
|
||||
pub struct SubstrateBlock {
|
||||
header: SubstrateHeader,
|
||||
#[serde(skip)] // This makes this unsafe to deserialize, but we don't impl `Deserialize`
|
||||
transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
impl HeaderTrait for SubstrateHeader {
|
||||
type Number = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Digest,
|
||||
) -> Self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 {
|
||||
number,
|
||||
parent_hash,
|
||||
transactions_root: extrinsics_root,
|
||||
consensus: ConsensusV1 { state_root, digest },
|
||||
})
|
||||
}
|
||||
|
||||
fn number(&self) -> &Self::Number {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { number, .. }) => number,
|
||||
}
|
||||
}
|
||||
fn set_number(&mut self, number: Self::Number) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { number: existing, .. }) => {
|
||||
*existing = number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => transactions_root,
|
||||
}
|
||||
}
|
||||
fn set_extrinsics_root(&mut self, extrinsics_root: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => {
|
||||
*transactions_root = extrinsics_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn state_root(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.state_root,
|
||||
}
|
||||
}
|
||||
fn set_state_root(&mut self, state_root: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
|
||||
consensus.state_root = state_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash, .. }) => parent_hash,
|
||||
}
|
||||
}
|
||||
fn set_parent_hash(&mut self, parent_hash: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash: existing, .. }) => {
|
||||
*existing = parent_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn digest(&self) -> &Digest {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.digest,
|
||||
}
|
||||
}
|
||||
fn digest_mut(&mut self) -> &mut Digest {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &mut consensus.digest,
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(&self) -> H256 {
|
||||
H256::from(Header::from(self).hash().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderProvider for SubstrateBlock {
|
||||
type HeaderT = SubstrateHeader;
|
||||
}
|
||||
|
||||
impl BlockTrait for SubstrateBlock {
|
||||
type Extrinsic = Transaction;
|
||||
type Header = SubstrateHeader;
|
||||
type Hash = H256;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.transactions
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.transactions)
|
||||
}
|
||||
fn new(header: Self::Header, transactions: Vec<Self::Extrinsic>) -> Self {
|
||||
Self { header, transactions }
|
||||
}
|
||||
fn encode_from(header: &Self::Header, transactions: &[Self::Extrinsic]) -> Vec<u8> {
|
||||
let header = header.encode();
|
||||
let transactions = transactions.encode();
|
||||
let mut block = header;
|
||||
block.extend(transactions);
|
||||
block
|
||||
}
|
||||
fn hash(&self) -> Self::Hash {
|
||||
self.header.hash()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "substrate")]
|
||||
pub use substrate::*;
|
|
@ -1,25 +1,70 @@
|
|||
use serai_primitives::{Balance, SeraiAddress};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_coins_primitives as primitives;
|
||||
use primitives::OutInstructionWithBalance;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress, balance::Balance, instructions::OutInstructionWithBalance,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to coins.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
burn { balance: Balance },
|
||||
burn_with_instruction { instruction: OutInstructionWithBalance },
|
||||
/// Transfer these coins to the specified address.
|
||||
transfer {
|
||||
/// The address to transfer to.
|
||||
to: SeraiAddress,
|
||||
/// The coins to transfer.
|
||||
coins: Balance,
|
||||
},
|
||||
/// Burn these coins.
|
||||
burn {
|
||||
/// The coins to burn.
|
||||
coins: Balance,
|
||||
},
|
||||
/// Burn these coins with an `OutInstruction` specified.
|
||||
burn_with_instruction {
|
||||
/// The `OutInstruction`, with the coins to burn.
|
||||
instruction: OutInstructionWithBalance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Mint { to: SeraiAddress, balance: Balance },
|
||||
Burn { from: SeraiAddress, balance: Balance },
|
||||
BurnWithInstruction { from: SeraiAddress, instruction: OutInstructionWithBalance },
|
||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::transfer { .. } | Call::burn { .. } | Call::burn_with_instruction { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the system.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// The specified coins were minted.
|
||||
Mint {
|
||||
/// The address minted to.
|
||||
to: SeraiAddress,
|
||||
/// The coins minted.
|
||||
coins: Balance,
|
||||
},
|
||||
/// The specified coins were burnt.
|
||||
Burn {
|
||||
/// The address burnt from.
|
||||
from: SeraiAddress,
|
||||
/// The coins burnt.
|
||||
coins: Balance,
|
||||
},
|
||||
/// The specified coins were burnt with an `OutInstruction` specified.
|
||||
BurnWithInstruction {
|
||||
/// The address burnt from.
|
||||
from: SeraiAddress,
|
||||
/// The `OutInstruction` specified, and the coins burnt.
|
||||
instruction: OutInstructionWithBalance,
|
||||
},
|
||||
/// The specified coins were transferred.
|
||||
Transfer {
|
||||
/// The address transferred from.
|
||||
from: SeraiAddress,
|
||||
/// The address transferred to.
|
||||
to: SeraiAddress,
|
||||
/// The coins transferred.
|
||||
coins: Balance,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,75 +1,121 @@
|
|||
use sp_runtime::BoundedVec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use serai_primitives::*;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
type PoolId = ExternalCoin;
|
||||
type MaxSwapPathLength = sp_core::ConstU32<3>;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress,
|
||||
coin::ExternalCoin,
|
||||
balance::{Amount, ExternalBalance, Balance},
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to the DEX.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
/// Add liquidity.
|
||||
add_liquidity {
|
||||
/// The coin to add liquidity for.
|
||||
coin: ExternalCoin,
|
||||
coin_desired: SubstrateAmount,
|
||||
sri_desired: SubstrateAmount,
|
||||
coin_min: SubstrateAmount,
|
||||
sri_min: SubstrateAmount,
|
||||
mint_to: SeraiAddress,
|
||||
/// The intended amount of SRI to add as liquidity.
|
||||
sri_intended: Amount,
|
||||
/// The intended amount of the coin to add as liquidity.
|
||||
coin_intended: Amount,
|
||||
/// The minimum amount of SRI to add as liquidity.
|
||||
sri_minimum: Amount,
|
||||
/// The minimum amount of the coin to add as liquidity.
|
||||
coin_minimum: Amount,
|
||||
},
|
||||
/// Transfer these liquidity tokens to the specified address.
|
||||
transfer_liquidity {
|
||||
/// The address to transfer to.
|
||||
to: SeraiAddress,
|
||||
/// The liquidity tokens to transfer.
|
||||
liquidity_tokens: ExternalBalance,
|
||||
},
|
||||
/// Remove liquidity.
|
||||
remove_liquidity {
|
||||
coin: ExternalCoin,
|
||||
lp_token_burn: SubstrateAmount,
|
||||
coin_min_receive: SubstrateAmount,
|
||||
sri_min_receive: SubstrateAmount,
|
||||
withdraw_to: SeraiAddress,
|
||||
/// The liquidity tokens to burn, removing the underlying liquidity from the pool.
|
||||
///
|
||||
/// The `coin` within the balance is the coin to remove liquidity for.
|
||||
liquidity_tokens: ExternalBalance,
|
||||
/// The minimum amount of SRI to receive.
|
||||
sri_minimum: Amount,
|
||||
/// The minimum amount of the coin to receive.
|
||||
coin_minimum: Amount,
|
||||
},
|
||||
swap_exact_tokens_for_tokens {
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_in: SubstrateAmount,
|
||||
amount_out_min: SubstrateAmount,
|
||||
send_to: SeraiAddress,
|
||||
/// Swap an exact amount of coins.
|
||||
swap_exact {
|
||||
/// The coins to swap.
|
||||
coins_to_swap: Balance,
|
||||
/// The minimum balance to receive.
|
||||
minimum_to_receive: Balance,
|
||||
},
|
||||
swap_tokens_for_exact_tokens {
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_out: SubstrateAmount,
|
||||
amount_in_max: SubstrateAmount,
|
||||
send_to: SeraiAddress,
|
||||
/// Swap for an exact amount of coins.
|
||||
swap_for_exact {
|
||||
/// The coins to receive.
|
||||
coins_to_receive: Balance,
|
||||
/// The maximum amount to swap.
|
||||
maximum_to_swap: Balance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::add_liquidity { .. } |
|
||||
Call::transfer_liquidity { .. } |
|
||||
Call::remove_liquidity { .. } |
|
||||
Call::swap_exact { .. } |
|
||||
Call::swap_for_exact { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the DEX.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
PoolCreated {
|
||||
pool_id: PoolId,
|
||||
pool_account: SeraiAddress,
|
||||
},
|
||||
|
||||
/// Liquidity was added to a pool.
|
||||
LiquidityAdded {
|
||||
who: SeraiAddress,
|
||||
mint_to: SeraiAddress,
|
||||
pool_id: PoolId,
|
||||
coin_amount: SubstrateAmount,
|
||||
sri_amount: SubstrateAmount,
|
||||
lp_token_minted: SubstrateAmount,
|
||||
/// The account which added the liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The account which received the liquidity tokens.
|
||||
recipient: SeraiAddress,
|
||||
/// The pool liquidity was added to.
|
||||
pool: ExternalCoin,
|
||||
/// The amount of liquidity tokens which were minted.
|
||||
liquidity_tokens_minted: Amount,
|
||||
/// The amount of the coin which was added to the pool's liquidity.
|
||||
coin_amount: Amount,
|
||||
/// The amount of SRI which was added to the pool's liquidity.
|
||||
sri_amount: Amount,
|
||||
},
|
||||
|
||||
/// Liquidity was removed from a pool.
|
||||
LiquidityRemoved {
|
||||
who: SeraiAddress,
|
||||
withdraw_to: SeraiAddress,
|
||||
pool_id: PoolId,
|
||||
coin_amount: SubstrateAmount,
|
||||
sri_amount: SubstrateAmount,
|
||||
lp_token_burned: SubstrateAmount,
|
||||
/// The account which removed the liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The pool liquidity was removed from.
|
||||
pool: ExternalCoin,
|
||||
/// The mount of liquidity tokens which were burnt.
|
||||
liquidity_tokens_burnt: Amount,
|
||||
/// The amount of the coin which was removed from the pool's liquidity.
|
||||
coin_amount: Amount,
|
||||
/// The amount of SRI which was removed from the pool's liquidity.
|
||||
sri_amount: Amount,
|
||||
},
|
||||
|
||||
SwapExecuted {
|
||||
who: SeraiAddress,
|
||||
send_to: SeraiAddress,
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_in: SubstrateAmount,
|
||||
amount_out: SubstrateAmount,
|
||||
/// A swap through the liquidity pools occurred.
|
||||
Swap {
|
||||
/// The account which made the swap.
|
||||
origin: SeraiAddress,
|
||||
/// The recipient for the output of the swap.
|
||||
recipient: SeraiAddress,
|
||||
/// The deltas incurred by the pools.
|
||||
///
|
||||
/// For a swap of sriABC to sriDEF, this would be
|
||||
/// `[Balance { sriABC, 1 }, Balance { SRI, 2 }, Balance { sriDEF, 3 }]`, where
|
||||
/// `Balance { sriABC, 1 }` was added to the `sriABC-SRI` pool, `Balance { SRI, 2 }` was
|
||||
/// removed from the `sriABC-SRI` pool and added to the `sriDEF-SRI` pool, and
|
||||
/// `Balance { sriDEF, 3 }` was removed from the `sriDEF-SRI` pool.
|
||||
deltas: Vec<Balance>,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
use serai_primitives::ExternalNetworkId;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
use serai_primitives::network_id::ExternalNetworkId;
|
||||
|
||||
/// An event from economic security.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
EconomicSecurityReached { network: ExternalNetworkId },
|
||||
/// Economic security was achieved for a network's validator set.
|
||||
EconomicSecurityAchieved {
|
||||
/// The network whose validator set achieved economic security.
|
||||
network: ExternalNetworkId,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
pub use serai_emissions_primitives as primitives;
|
|
@ -1,20 +1,50 @@
|
|||
pub use serai_genesis_liquidity_primitives as primitives;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::*;
|
||||
use primitives::*;
|
||||
use serai_primitives::{
|
||||
crypto::Signature, address::SeraiAddress, balance::ExternalBalance, genesis::GenesisValues,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
/// A call to the genesis liquidity.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
remove_coin_liquidity { balance: ExternalBalance },
|
||||
oraclize_values { values: Values, signature: Signature },
|
||||
/// Oraclize the value of non-Bitcoin external coins relative to Bitcoin.
|
||||
oraclize_values {
|
||||
/// The values of the non-Bitcoin external coins.
|
||||
values: GenesisValues,
|
||||
/// The signature by the genesis validators for these values.
|
||||
signature: Signature,
|
||||
},
|
||||
/// Remove liquidity.
|
||||
remove_liquidity {
|
||||
/// The genesis liquidity to remove.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Event {
|
||||
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::oraclize_values { .. } => false,
|
||||
Call::remove_liquidity { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the genesis liquidity.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// Genesis liquidity added.
|
||||
GenesisLiquidityAdded {
|
||||
/// The recipient of the genesis liquidity.
|
||||
recipient: SeraiAddress,
|
||||
/// The coins added as genesis liquidity.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
/// Genesis liquidity removed.
|
||||
GenesisLiquidityRemoved {
|
||||
/// The account which removed the genesis liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The amount of genesis liquidity removed.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
use sp_consensus_grandpa::EquivocationProof;
|
||||
|
||||
use serai_primitives::{BlockNumber, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<[u8; 32], BlockNumber>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum Call {
|
||||
report_equivocation(ReportEquivocation),
|
||||
report_equivocation_unsigned(ReportEquivocation),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
NewAuthorities { authority_set: alloc::vec::Vec<(SeraiAddress, u64)> },
|
||||
// TODO: Remove these
|
||||
Paused,
|
||||
Resumed,
|
||||
}
|
|
@ -1,30 +1,47 @@
|
|||
use serai_primitives::*;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_in_instructions_primitives as primitives;
|
||||
use primitives::SignedBatch;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
use serai_primitives::{
|
||||
BlockHash, network_id::ExternalNetworkId, validator_sets::Session, instructions::SignedBatch,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to `InInstruction`s.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
execute_batch { batch: SignedBatch },
|
||||
/// Execute a batch of `InInstruction`s.
|
||||
execute_batch {
|
||||
/// The batch to execute.
|
||||
batch: SignedBatch,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::execute_batch { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from `InInstruction`s.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A batch of `InInstruction`s was executed.
|
||||
Batch {
|
||||
/// The network for which a batch was executed.
|
||||
network: ExternalNetworkId,
|
||||
/// The session which published the batch.
|
||||
publishing_session: Session,
|
||||
/// The ID of the batch.
|
||||
id: u32,
|
||||
/// The hash of the block on the external network which caused this batch's creation.
|
||||
external_network_block_hash: BlockHash,
|
||||
/// The hash of the `InInstruction`s within this batch.
|
||||
in_instructions_hash: [u8; 32],
|
||||
/// The results of each `InInstruction` within the batch.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||
)]
|
||||
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
},
|
||||
Halt {
|
||||
network: ExternalNetworkId,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,94 +1,98 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_primitives as primitives;
|
||||
|
||||
/// Call/Event for the system.
|
||||
pub mod system;
|
||||
|
||||
pub mod timestamp;
|
||||
|
||||
/// Call/Event for coins.
|
||||
pub mod coins;
|
||||
pub mod liquidity_tokens;
|
||||
pub mod dex;
|
||||
|
||||
/// Call/Event for validator sets.
|
||||
pub mod validator_sets;
|
||||
|
||||
pub mod genesis_liquidity;
|
||||
pub mod emissions;
|
||||
|
||||
pub mod economic_security;
|
||||
|
||||
pub mod in_instructions;
|
||||
|
||||
/// Call/Event for signals.
|
||||
pub mod signals;
|
||||
|
||||
pub mod babe;
|
||||
pub mod grandpa;
|
||||
/// Call/Event for the DEX.
|
||||
pub mod dex;
|
||||
|
||||
pub mod tx;
|
||||
/// Call/Event for genesis liquidity.
|
||||
pub mod genesis_liquidity;
|
||||
/// Event for economic security.
|
||||
pub mod economic_security;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// Call/Event for `InInstruction`s.
|
||||
pub mod in_instructions;
|
||||
|
||||
mod transaction;
|
||||
pub use transaction::*;
|
||||
|
||||
mod block;
|
||||
pub use block::*;
|
||||
|
||||
/// All calls.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[repr(u8)]
|
||||
pub enum Call {
|
||||
Timestamp(timestamp::Call),
|
||||
Coins(coins::Call),
|
||||
LiquidityTokens(liquidity_tokens::Call),
|
||||
Dex(dex::Call),
|
||||
ValidatorSets(validator_sets::Call),
|
||||
GenesisLiquidity(genesis_liquidity::Call),
|
||||
InInstructions(in_instructions::Call),
|
||||
Signals(signals::Call),
|
||||
Babe(babe::Call),
|
||||
Grandpa(grandpa::Call),
|
||||
// The call for the system.
|
||||
// System(system::Call) = 0,
|
||||
/// The call for coins.
|
||||
Coins(coins::Call) = 1,
|
||||
/// The call for validator sets.
|
||||
ValidatorSets(validator_sets::Call) = 2,
|
||||
/// The call for signals.
|
||||
Signals(signals::Call) = 3,
|
||||
/// The call for the DEX.
|
||||
Dex(dex::Call) = 4,
|
||||
/// The call for genesis liquidity.
|
||||
GenesisLiquidity(genesis_liquidity::Call) = 5,
|
||||
// The call for economic security.
|
||||
// EconomicSecurity = 6,
|
||||
/// The call for `InInstruction`s.
|
||||
InInstructions(in_instructions::Call) = 7,
|
||||
}
|
||||
|
||||
// TODO: Remove this
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum TransactionPaymentEvent {
|
||||
TransactionFeePaid { who: serai_primitives::SeraiAddress, actual_fee: u64, tip: u64 },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::Coins(call) => call.is_signed(),
|
||||
Call::ValidatorSets(call) => call.is_signed(),
|
||||
Call::Signals(call) => call.is_signed(),
|
||||
Call::Dex(call) => call.is_signed(),
|
||||
Call::GenesisLiquidity(call) => call.is_signed(),
|
||||
Call::InInstructions(call) => call.is_signed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// All events.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[repr(u8)]
|
||||
pub enum Event {
|
||||
System(system::Event),
|
||||
Timestamp,
|
||||
TransactionPayment(TransactionPaymentEvent),
|
||||
Coins(coins::Event),
|
||||
LiquidityTokens(liquidity_tokens::Event),
|
||||
Dex(dex::Event),
|
||||
ValidatorSets(validator_sets::Event),
|
||||
GenesisLiquidity(genesis_liquidity::Event),
|
||||
Emissions,
|
||||
EconomicSecurity(economic_security::Event),
|
||||
InInstructions(in_instructions::Event),
|
||||
Signals(signals::Event),
|
||||
Babe,
|
||||
Grandpa(grandpa::Event),
|
||||
/// The event for the system.
|
||||
System(system::Event) = 0,
|
||||
/// The event for coins.
|
||||
Coins(coins::Event) = 1,
|
||||
/// The event for validator sets.
|
||||
ValidatorSets(validator_sets::Event) = 2,
|
||||
/// The event for signals.
|
||||
Signals(signals::Event) = 3,
|
||||
/// The event for the DEX.
|
||||
Dex(dex::Event) = 4,
|
||||
/// The event for genesis liquidity.
|
||||
GenesisLiquidity(genesis_liquidity::Event) = 5,
|
||||
/// The event for economic security.
|
||||
EconomicSecurity(economic_security::Event) = 6,
|
||||
/// The event for `InInstruction`s.
|
||||
InInstructions(in_instructions::Event) = 7,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct Extra {
|
||||
pub era: sp_runtime::generic::Era,
|
||||
#[codec(compact)]
|
||||
pub nonce: u32,
|
||||
#[codec(compact)]
|
||||
pub tip: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct SignedPayloadExtra {
|
||||
pub spec_version: u32,
|
||||
pub tx_version: u32,
|
||||
pub genesis: [u8; 32],
|
||||
pub mortality_checkpoint: [u8; 32],
|
||||
}
|
||||
|
||||
pub type Transaction = tx::Transaction<Call, Extra>;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
use serai_primitives::{Balance, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Call {
|
||||
burn { balance: Balance },
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Mint { to: SeraiAddress, balance: Balance },
|
||||
Burn { from: SeraiAddress, balance: Balance },
|
||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
||||
}
|
|
@ -1,59 +1,132 @@
|
|||
use serai_primitives::{NetworkId, SeraiAddress};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_validator_sets_primitives::ValidatorSet;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress, network_id::NetworkId, validator_sets::ValidatorSet, signals::Signal,
|
||||
};
|
||||
|
||||
pub use serai_signals_primitives as primitives;
|
||||
use primitives::SignalId;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to signals.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
register_retirement_signal { in_favor_of: [u8; 32] },
|
||||
revoke_retirement_signal { retirement_signal_id: [u8; 32] },
|
||||
favor { signal_id: SignalId, for_network: NetworkId },
|
||||
revoke_favor { signal_id: SignalId, for_network: NetworkId },
|
||||
stand_against { signal_id: SignalId, for_network: NetworkId },
|
||||
/// Register a retirement signal.
|
||||
register_retirement_signal {
|
||||
/// The protocol favored over the current protocol.
|
||||
in_favor_of: [u8; 32],
|
||||
},
|
||||
/// Revoke a retirement signal.
|
||||
revoke_retirement_signal {
|
||||
/// The protocol which was favored over the current protocol
|
||||
was_in_favor_of: [u8; 32],
|
||||
},
|
||||
/// Favor a signal.
|
||||
favor {
|
||||
/// The signal to favor.
|
||||
signal: Signal,
|
||||
/// The network this validator is expressing favor with.
|
||||
///
|
||||
/// A validator may be an active validator for multiple networks. The validator must specify
|
||||
/// which network they're expressing favor with in this call.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Revoke favor for a signal.
|
||||
revoke_favor {
|
||||
/// The signal to revoke favor for.
|
||||
signal: Signal,
|
||||
/// The network this validator is revoking favor with.
|
||||
///
|
||||
/// A validator may have expressed favor with multiple networks. The validator must specify
|
||||
/// which network they're revoking favor with in this call.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Stand against a signal.
|
||||
///
|
||||
/// This has no effects other than emitting an event that this signal is stood against. If the
|
||||
/// origin has prior expressed favor, they must still call `revoke_favor` for each network they
|
||||
/// expressed favor with.
|
||||
stand_against {
|
||||
/// The signal to stand against.
|
||||
signal: Signal,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::register_retirement_signal { .. } |
|
||||
Call::revoke_retirement_signal { .. } |
|
||||
Call::favor { .. } |
|
||||
Call::revoke_favor { .. } |
|
||||
Call::stand_against { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from signals.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A retirement signal has been registered.
|
||||
RetirementSignalRegistered {
|
||||
signal_id: [u8; 32],
|
||||
/// The retirement signal's ID.
|
||||
signal: [u8; 32],
|
||||
/// The protocol retirement is proposed in favor of.
|
||||
in_favor_of: [u8; 32],
|
||||
/// The address which registered this signal.
|
||||
registrant: SeraiAddress,
|
||||
},
|
||||
/// A retirement signal was revoked.
|
||||
RetirementSignalRevoked {
|
||||
signal_id: [u8; 32],
|
||||
/// The retirement signal's ID.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// A signal was favored.
|
||||
SignalFavored {
|
||||
signal_id: SignalId,
|
||||
/// The signal favored.
|
||||
signal: Signal,
|
||||
/// The validator the signal was favored by.
|
||||
by: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
},
|
||||
SetInFavor {
|
||||
signal_id: SignalId,
|
||||
set: ValidatorSet,
|
||||
},
|
||||
RetirementSignalLockedIn {
|
||||
signal_id: [u8; 32],
|
||||
},
|
||||
SetNoLongerInFavor {
|
||||
signal_id: SignalId,
|
||||
set: ValidatorSet,
|
||||
/// The network with which the signal was favored.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Favor for a signal was revoked.
|
||||
FavorRevoked {
|
||||
signal_id: SignalId,
|
||||
/// The signal whose favor was revoked.
|
||||
signal: Signal,
|
||||
/// The validator who revoked their favor for the signal.
|
||||
by: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
/// The network with which favor for the signal was revoked.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// A supermajority of a validator set now favor a signal.
|
||||
SetInFavor {
|
||||
/// The signal which now has a supermajority of a validator set favoring it.
|
||||
signal: Signal,
|
||||
/// The validator set which is now considered to favor the signal.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A validator set is no longer considered to favor a signal.
|
||||
SetNoLongerInFavor {
|
||||
/// The signal which no longer has the validator set considered in favor of it.
|
||||
signal: Signal,
|
||||
/// The validator set which is no longer considered to be in favor of the signal.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A retirement signal has been locked in.
|
||||
RetirementSignalLockedIn {
|
||||
/// The signal which has been locked in.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// A validator set's ability to publish batches was halted.
|
||||
///
|
||||
/// This also halts set rotation in effect, as handovers are via new sets starting to publish
|
||||
/// batches.
|
||||
SetHalted {
|
||||
/// The signal which has been locked in.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// An account has stood against a signal.
|
||||
AgainstSignal {
|
||||
signal_id: SignalId,
|
||||
/// The signal stood against.
|
||||
signal: Signal,
|
||||
/// The account which stood against the signal.
|
||||
who: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use frame_support::dispatch::{DispatchInfo, DispatchError};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// An event from the system.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
ExtrinsicSuccess { dispatch_info: DispatchInfo },
|
||||
ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchInfo },
|
||||
CodeUpdated,
|
||||
NewAccount { account: SeraiAddress },
|
||||
KilledAccount { account: SeraiAddress },
|
||||
Remarked { sender: SeraiAddress, hash: [u8; 32] },
|
||||
/// The transaction successfully executed.
|
||||
TransactionSuccess,
|
||||
/// The transaction failed to execute.
|
||||
// TODO: Add an error to this
|
||||
TransactionFailed,
|
||||
}
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
set {
|
||||
#[codec(compact)]
|
||||
now: u64,
|
||||
},
|
||||
}
|
319
substrate/abi/src/transaction.rs
Normal file
319
substrate/abi/src/transaction.rs
Normal file
|
@ -0,0 +1,319 @@
|
|||
use core::num::NonZero;
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{address::SeraiAddress, crypto::Signature};
|
||||
use crate::Call;
|
||||
|
||||
// use frame_support::dispatch::GetDispatchInfo;
|
||||
|
||||
/// An error regarding `SignedCalls`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum SignedCallsError {
|
||||
/// No calls were included.
|
||||
NoCalls,
|
||||
/// An unsigned call was included.
|
||||
IncludedUnsignedCall,
|
||||
}
|
||||
|
||||
/// A `Vec` of signed calls.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct SignedCalls(Vec<Call>);
|
||||
impl TryFrom<Vec<Call>> for SignedCalls {
|
||||
type Error = SignedCallsError;
|
||||
fn try_from(calls: Vec<Call>) -> Result<Self, Self::Error> {
|
||||
if calls.is_empty() {
|
||||
Err(SignedCallsError::NoCalls)?;
|
||||
}
|
||||
for call in &calls {
|
||||
if !call.is_signed() {
|
||||
Err(SignedCallsError::IncludedUnsignedCall)?;
|
||||
}
|
||||
}
|
||||
Ok(SignedCalls(calls))
|
||||
}
|
||||
}
|
||||
|
||||
/// An error regarding `UnsignedCall`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum UnsignedCallError {
|
||||
/// A signed call was specified.
|
||||
SignedCall,
|
||||
}
|
||||
|
||||
/// An unsigned call.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct UnsignedCall(Call);
|
||||
impl TryFrom<Call> for UnsignedCall {
|
||||
type Error = UnsignedCallError;
|
||||
fn try_from(call: Call) -> Result<Self, Self::Error> {
|
||||
if call.is_signed() {
|
||||
Err(UnsignedCallError::SignedCall)?;
|
||||
}
|
||||
Ok(UnsignedCall(call))
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of the context used to sign with, from the protocol.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ImplicitContext {
|
||||
/// The ID of the the protocol.
|
||||
pub protocol_id: [u8; 32],
|
||||
/// The genesis hash of the blockchain.
|
||||
pub genesis: [u8; 32],
|
||||
}
|
||||
|
||||
/// Part of the context used to sign with, specified within the transaction itself.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ExplicitContext {
|
||||
/// The historic block this transaction builds upon.
|
||||
///
|
||||
/// This transaction can not be included in a blockchain which does not include this block.
|
||||
pub historic_block: [u8; 32],
|
||||
|
||||
/// The block this transaction expires at.
|
||||
///
|
||||
/// This transaction can not be included in a block whose number is equal or greater to this
|
||||
/// value.
|
||||
pub expires_at: Option<NonZero<u64>>,
|
||||
|
||||
/// The signer.
|
||||
pub signer: SeraiAddress,
|
||||
|
||||
/// The signer's nonce.
|
||||
pub nonce: u32,
|
||||
|
||||
/// The fee paid to the network for inclusion.
|
||||
///
|
||||
/// This fee is paid regardless of the success of any of the calls.
|
||||
pub fee: u64,
|
||||
}
|
||||
|
||||
/// A signature, with context.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ContextualizedSignature {
|
||||
/// The explicit context.
|
||||
explicit_context: ExplicitContext,
|
||||
/// The signature.
|
||||
signature: Signature,
|
||||
}
|
||||
|
||||
/// The Serai transaction type.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction<RuntimeCall: 'static + From<Call> = Call> {
|
||||
/// The calls, as defined in Serai's ABI.
|
||||
///
|
||||
/// These calls are executed atomically. Either all successfully execute or none do. The
|
||||
/// transaction's fee is paid regardless.
|
||||
// TODO: Bound
|
||||
calls: Vec<Call>,
|
||||
/// The calls, as defined by Substrate.
|
||||
runtime_calls: Vec<RuntimeCall>,
|
||||
/// The signature, if present.
|
||||
contextualized_signature: Option<ContextualizedSignature>,
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> BorshSerialize for Transaction<RuntimeCall> {
|
||||
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
// Write the calls
|
||||
self.calls.serialize(writer)?;
|
||||
// Write the signature, if present. Presence is deterministic to the calls
|
||||
if let Some(contextualized_signature) = self.contextualized_signature.as_ref() {
|
||||
contextualized_signature.serialize(writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> BorshDeserialize for Transaction<RuntimeCall> {
|
||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||
// Read the calls
|
||||
let calls = Vec::<Call>::deserialize_reader(reader)?;
|
||||
// Populate the runtime calls
|
||||
let mut runtime_calls = Vec::with_capacity(calls.len());
|
||||
for call in calls.iter().cloned() {
|
||||
runtime_calls.push(RuntimeCall::from(call));
|
||||
}
|
||||
|
||||
// Determine if this is signed or unsigned
|
||||
let mut signed = None;
|
||||
for call in &calls {
|
||||
let call_is_signed = call.is_signed();
|
||||
if signed.is_none() {
|
||||
signed = Some(call_is_signed)
|
||||
};
|
||||
if signed != Some(call_is_signed) {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "calls were a mixture of signed and unsigned"))?;
|
||||
}
|
||||
}
|
||||
let Some(signed) = signed else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "transaction had no calls"))?
|
||||
};
|
||||
|
||||
// Read the signature, if these calls are signed
|
||||
let contextualized_signature =
|
||||
if signed { Some(<ContextualizedSignature>::deserialize_reader(reader)?) } else { None };
|
||||
|
||||
Ok(Transaction { calls, runtime_calls, contextualized_signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> Transaction<RuntimeCall> {
|
||||
/// The message to sign to produce a signature.
|
||||
pub fn signature_message(
|
||||
calls: &SignedCalls,
|
||||
implicit_context: &ImplicitContext,
|
||||
explicit_context: &ExplicitContext,
|
||||
) -> Vec<u8> {
|
||||
let mut message = Vec::with_capacity(
|
||||
(calls.0.len() * 64) +
|
||||
core::mem::size_of::<ImplicitContext>() +
|
||||
core::mem::size_of::<ExplicitContext>(),
|
||||
);
|
||||
calls.0.serialize(&mut message).unwrap();
|
||||
implicit_context.serialize(&mut message).unwrap();
|
||||
explicit_context.serialize(&mut message).unwrap();
|
||||
message
|
||||
}
|
||||
|
||||
/// A transaction with signed calls.
|
||||
pub fn is_signed(
|
||||
calls: SignedCalls,
|
||||
explicit_context: ExplicitContext,
|
||||
signature: Signature,
|
||||
) -> Self {
|
||||
let calls = calls.0;
|
||||
let mut runtime_calls = Vec::with_capacity(calls.len());
|
||||
for call in calls.iter().cloned() {
|
||||
runtime_calls.push(call.into());
|
||||
}
|
||||
Self {
|
||||
calls,
|
||||
runtime_calls,
|
||||
contextualized_signature: Some(ContextualizedSignature { explicit_context, signature }),
|
||||
}
|
||||
}
|
||||
|
||||
/// A transaction with an unsigned call.
|
||||
pub fn unsigned(call: UnsignedCall) -> Self {
|
||||
let call = call.0;
|
||||
Self {
|
||||
calls: vec![call.clone()],
|
||||
runtime_calls: vec![call.into()],
|
||||
contextualized_signature: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "substrate")]
|
||||
mod substrate {
|
||||
use super::*;
|
||||
|
||||
impl scale::Encode for Transaction {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
borsh::to_vec(self).unwrap()
|
||||
}
|
||||
}
|
||||
impl scale::Decode for Transaction {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
struct ScaleRead<'a, I: scale::Input>(&'a mut I);
|
||||
impl<I: scale::Input> borsh::io::Read for ScaleRead<'_, I> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
|
||||
let remaining_len = self
|
||||
.0
|
||||
.remaining_len()
|
||||
.map_err(|err| borsh::io::Error::new(borsh::io::ErrorKind::Other, err))?;
|
||||
// If we're still calling `read`, we try to read at least one more byte
|
||||
let to_read = buf.len().min(remaining_len.unwrap_or(1));
|
||||
self
|
||||
.0
|
||||
.read(&mut buf[.. to_read])
|
||||
.map_err(|err| borsh::io::Error::new(borsh::io::ErrorKind::Other, err))?;
|
||||
Ok(to_read)
|
||||
}
|
||||
}
|
||||
Self::deserialize_reader(&mut ScaleRead(input)).map_err(|err| err.downcast().unwrap())
|
||||
}
|
||||
}
|
||||
impl<RuntimeCall: 'static + From<Call>> sp_runtime::traits::ExtrinsicLike
|
||||
for Transaction<RuntimeCall>
|
||||
{
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.calls[0].is_signed())
|
||||
}
|
||||
fn is_bare(&self) -> bool {
|
||||
!self.calls[0].is_signed()
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<Call> + TryInto<Call>,
|
||||
> sp_runtime::traits::Extrinsic for Transaction<Call>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
||||
{
|
||||
fn call(&self) -> &Call {
|
||||
&self.mapped_call
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
|
||||
const VERSION: u8 = 0;
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
||||
> GetDispatchInfo for Transaction<Call, Extra>
|
||||
{
|
||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
||||
self.mapped_call.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
||||
{
|
||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
||||
|
||||
fn check(
|
||||
self,
|
||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signer, signature, extra)) => {
|
||||
if !signature.verify(
|
||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
||||
&signer.into(),
|
||||
) {
|
||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
||||
}
|
||||
|
||||
sp_runtime::generic::CheckedExtrinsic {
|
||||
signed: Some((signer.into(), extra)),
|
||||
function: self.mapped_call,
|
||||
}
|
||||
}
|
||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
use scale::Encode;
|
||||
|
||||
use sp_core::sr25519::{Public, Signature};
|
||||
use sp_runtime::traits::Verify;
|
||||
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
|
||||
pub trait TransactionMember:
|
||||
Clone + PartialEq + Eq + core::fmt::Debug + scale::Encode + scale::Decode + scale_info::TypeInfo
|
||||
{
|
||||
}
|
||||
impl<
|
||||
T: Clone
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ core::fmt::Debug
|
||||
+ scale::Encode
|
||||
+ scale::Decode
|
||||
+ scale_info::TypeInfo,
|
||||
> TransactionMember for T
|
||||
{
|
||||
}
|
||||
|
||||
type TransactionEncodeAs<'a, Extra> =
|
||||
(&'a crate::Call, &'a Option<(SeraiAddress, Signature, Extra)>);
|
||||
type TransactionDecodeAs<Extra> = (crate::Call, Option<(SeraiAddress, Signature, Extra)>);
|
||||
|
||||
// We use our own Transaction struct, over UncheckedExtrinsic, for more control, a bit more
|
||||
// simplicity, and in order to be immune to https://github.com/paritytech/polkadot-sdk/issues/2947
|
||||
#[allow(private_bounds)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> {
|
||||
call: crate::Call,
|
||||
mapped_call: Call,
|
||||
signature: Option<(SeraiAddress, Signature, Extra)>,
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Transaction<Call, Extra>
|
||||
{
|
||||
pub fn new(call: crate::Call, signature: Option<(SeraiAddress, Signature, Extra)>) -> Self {
|
||||
Self { call: call.clone(), mapped_call: call.into(), signature }
|
||||
}
|
||||
|
||||
pub fn call(&self) -> &crate::Call {
|
||||
&self.call
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Encode for Transaction<Call, Extra>
|
||||
{
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
let tx: TransactionEncodeAs<Extra> = (&self.call, &self.signature);
|
||||
tx.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Decode for Transaction<Call, Extra>
|
||||
{
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let (call, signature) = TransactionDecodeAs::decode(input)?;
|
||||
let mapped_call = Call::from(call.clone());
|
||||
Ok(Self { call, mapped_call, signature })
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale_info::TypeInfo for Transaction<Call, Extra>
|
||||
{
|
||||
type Identity = TransactionDecodeAs<Extra>;
|
||||
|
||||
// Define the type info as the info of the type equivalent to what we encode as
|
||||
fn type_info() -> scale_info::Type {
|
||||
TransactionDecodeAs::<Extra>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod _serde {
|
||||
use scale::Encode;
|
||||
use serde::ser::*;
|
||||
use super::*;
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Serialize for Transaction<Call, Extra>
|
||||
{
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded = self.encode();
|
||||
serializer.serialize_bytes(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::de::*;
|
||||
#[cfg(feature = "std")]
|
||||
impl<
|
||||
'a,
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> Deserialize<'a> for Transaction<Call, Extra>
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let bytes = sp_core::bytes::deserialize(de)?;
|
||||
<Self as scale::Decode>::decode(&mut &bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid transaction: {e}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> sp_runtime::traits::Extrinsic for Transaction<Call, Extra>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
||||
{
|
||||
fn call(&self) -> &Call {
|
||||
&self.mapped_call
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
|
||||
const VERSION: u8 = 0;
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
||||
Extra: 'static + TransactionMember,
|
||||
> GetDispatchInfo for Transaction<Call, Extra>
|
||||
{
|
||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
||||
self.mapped_call.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
||||
{
|
||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
||||
|
||||
fn check(
|
||||
self,
|
||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signer, signature, extra)) => {
|
||||
if !signature.verify(
|
||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
||||
&signer.into(),
|
||||
) {
|
||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
||||
}
|
||||
|
||||
sp_runtime::generic::CheckedExtrinsic {
|
||||
signed: Some((signer.into(), extra)),
|
||||
function: self.mapped_call,
|
||||
}
|
||||
}
|
||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,79 +1,144 @@
|
|||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
|
||||
pub use serai_validator_sets_primitives as primitives;
|
||||
use serai_primitives::{
|
||||
crypto::{ExternalKey, KeyPair, Signature},
|
||||
address::SeraiAddress,
|
||||
balance::Amount,
|
||||
network_id::*,
|
||||
validator_sets::*,
|
||||
};
|
||||
|
||||
use serai_primitives::*;
|
||||
use serai_validator_sets_primitives::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to the validator sets.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
/// Set the keys for a validator set.
|
||||
set_keys {
|
||||
/// The network whose latest validator set is setting their keys.
|
||||
network: ExternalNetworkId,
|
||||
/// The keys being set.
|
||||
key_pair: KeyPair,
|
||||
/// The participants in the validator set who signed off on these keys.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||
)]
|
||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
/// The signature confirming these keys are valid.
|
||||
signature: Signature,
|
||||
},
|
||||
set_embedded_elliptic_curve_key {
|
||||
embedded_elliptic_curve: EmbeddedEllipticCurve,
|
||||
key: BoundedVec<u8, ConstU32<{ MAX_KEY_LEN }>>,
|
||||
},
|
||||
/// Report a validator set's slashes onto Serai.
|
||||
report_slashes {
|
||||
/// The network whose retiring validator set is setting their keys.
|
||||
network: ExternalNetworkId,
|
||||
/// The slashes they're reporting.
|
||||
slashes: SlashReport,
|
||||
/// The signature confirming the validity of this slash report.
|
||||
signature: Signature,
|
||||
},
|
||||
/// Set a validator's keys on embedded elliptic curves for a specific network.
|
||||
set_embedded_elliptic_curve_keys {
|
||||
/// The network the origin is setting their embedded elliptic curve keys for.
|
||||
network: ExternalNetworkId,
|
||||
/// The keys on the embedded elliptic curves.
|
||||
///
|
||||
/// This may be a single key if the external network uses the same embedded elliptic curve as
|
||||
/// used for the key to oraclize onto Serai.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bounded_vec"
|
||||
)]
|
||||
keys: BoundedVec<u8, ConstU32<{ 2 * ExternalKey::MAX_LEN }>>,
|
||||
},
|
||||
/// Allocate stake to a network.
|
||||
allocate {
|
||||
/// The network to allocate stake to.
|
||||
network: NetworkId,
|
||||
/// The amount of stake to allocate.
|
||||
amount: Amount,
|
||||
},
|
||||
/// Deallocate stake from a network.
|
||||
///
|
||||
/// This deallocation may be immediate or may be delayed depending on if the origin is an
|
||||
/// active, or even recent, validator. If delayed, it will have to be claimed at a later time.
|
||||
deallocate {
|
||||
/// The network to deallocate stake from.
|
||||
network: NetworkId,
|
||||
/// The amount of stake to deallocate.
|
||||
amount: Amount,
|
||||
},
|
||||
/// Claim a now-unlocked deallocation.
|
||||
claim_deallocation {
|
||||
network: NetworkId,
|
||||
session: Session,
|
||||
/// The validator set which claiming the deallocation was delayed until.
|
||||
deallocation: ValidatorSet,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::set_keys { .. } | Call::report_slashes { .. } => false,
|
||||
Call::set_embedded_elliptic_curve_keys { .. } |
|
||||
Call::allocate { .. } |
|
||||
Call::deallocate { .. } |
|
||||
Call::claim_deallocation { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the validator sets.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A new validator set was declared.
|
||||
NewSet {
|
||||
/// The set declared.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
ParticipantRemoved {
|
||||
set: ValidatorSet,
|
||||
removed: SeraiAddress,
|
||||
},
|
||||
KeyGen {
|
||||
/// A validator set has set their keys.
|
||||
SetKeys {
|
||||
/// The set which set their keys.
|
||||
set: ExternalValidatorSet,
|
||||
/// The keys sets.
|
||||
key_pair: KeyPair,
|
||||
},
|
||||
/// A validator set has accepted responsibility from the prior validator set.
|
||||
AcceptedHandover {
|
||||
/// The set which accepted responsibility from the prior set.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A validator set has retired.
|
||||
SetRetired {
|
||||
/// The set retired.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
AllocationIncreased {
|
||||
/// A validator's allocation to a network has increased.
|
||||
Allocation {
|
||||
/// The validator who increased their allocation.
|
||||
validator: SeraiAddress,
|
||||
/// The network the stake was allocated to.
|
||||
network: NetworkId,
|
||||
/// The amount of stake allocated.
|
||||
amount: Amount,
|
||||
},
|
||||
AllocationDecreased {
|
||||
/// A validator's allocation to a network has decreased.
|
||||
Deallocation {
|
||||
/// The validator who decreased their allocation.
|
||||
validator: SeraiAddress,
|
||||
/// The network the stake was deallocated from.
|
||||
network: NetworkId,
|
||||
/// The amount of stake deallocated.
|
||||
amount: Amount,
|
||||
/// The session which claiming the deallocation was delayed until.
|
||||
delayed_until: Option<Session>,
|
||||
},
|
||||
/// A validator's deallocation from a network has been claimed.
|
||||
///
|
||||
/// This is only emited for deallocations which were delayed and has to be explicitly claimed.
|
||||
DeallocationClaimed {
|
||||
/// The validator who claimed their deallocation.
|
||||
validator: SeraiAddress,
|
||||
network: NetworkId,
|
||||
session: Session,
|
||||
/// The validator set the deallocation was delayed until.
|
||||
deallocation: ValidatorSet,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,54 +1 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::{ExternalBalance, SeraiAddress, ExternalAddress, system_address};
|
||||
|
||||
pub const FEE_ACCOUNT: SeraiAddress = system_address(b"Coins-fees");
|
||||
|
||||
// TODO: Replace entirely with just Address
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct OutInstruction {
|
||||
pub address: ExternalAddress,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct OutInstructionWithBalance {
|
||||
pub instruction: OutInstruction,
|
||||
pub balance: ExternalBalance,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Destination {
|
||||
Native(SeraiAddress),
|
||||
External(OutInstruction),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address() {
|
||||
use sp_runtime::traits::TrailingZeroInput;
|
||||
assert_eq!(
|
||||
FEE_ACCOUNT,
|
||||
SeraiAddress::decode(&mut TrailingZeroInput::new(b"Coins-fees")).unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,54 +1 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::*;
|
||||
use validator_sets_primitives::ValidatorSet;
|
||||
|
||||
pub const INITIAL_GENESIS_LP_SHARES: u64 = 10_000;
|
||||
|
||||
// This is the account to hold and manage the genesis liquidity.
|
||||
pub const GENESIS_LIQUIDITY_ACCOUNT: SeraiAddress = system_address(b"GenesisLiquidity-account");
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Values {
|
||||
pub monero: u64,
|
||||
pub ether: u64,
|
||||
pub dai: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LiquidityAmount {
|
||||
pub shares: u64,
|
||||
pub coins: u64,
|
||||
}
|
||||
|
||||
impl LiquidityAmount {
|
||||
pub fn zero() -> Self {
|
||||
LiquidityAmount { shares: 0, coins: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// The message for the oraclize_values signature.
|
||||
pub fn oraclize_values_message(set: &ValidatorSet, values: &Values) -> Vec<u8> {
|
||||
(b"GenesisLiquidity-oraclize_values", set, values).encode()
|
||||
}
|
||||
|
|
|
@ -1,141 +1 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_application_crypto::sr25519::Signature;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::vec::Vec;
|
||||
use sp_runtime::RuntimeDebug;
|
||||
|
||||
#[rustfmt::skip]
|
||||
use serai_primitives::{BlockHash, ExternalNetworkId, NetworkId, ExternalBalance, Balance, SeraiAddress, ExternalAddress, system_address};
|
||||
|
||||
mod shorthand;
|
||||
pub use shorthand::*;
|
||||
|
||||
pub const MAX_BATCH_SIZE: usize = 25_000; // ~25kb
|
||||
|
||||
// This is the account which will be the origin for any dispatched `InInstruction`s.
|
||||
pub const IN_INSTRUCTION_EXECUTOR: SeraiAddress = system_address(b"InInstructions-executor");
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum OutAddress {
|
||||
Serai(SeraiAddress),
|
||||
External(ExternalAddress),
|
||||
}
|
||||
|
||||
impl OutAddress {
|
||||
pub fn is_native(&self) -> bool {
|
||||
matches!(self, Self::Serai(_))
|
||||
}
|
||||
|
||||
pub fn as_native(self) -> Option<SeraiAddress> {
|
||||
match self {
|
||||
Self::Serai(addr) => Some(addr),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_external(self) -> Option<ExternalAddress> {
|
||||
match self {
|
||||
Self::External(addr) => Some(addr),
|
||||
Self::Serai(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum DexCall {
|
||||
// address to send the lp tokens to
|
||||
// TODO: Update this per documentation/Shorthand
|
||||
SwapAndAddLiquidity(SeraiAddress),
|
||||
// minimum out balance and out address
|
||||
Swap(Balance, OutAddress),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum InInstruction {
|
||||
Transfer(SeraiAddress),
|
||||
Dex(DexCall),
|
||||
GenesisLiquidity(SeraiAddress),
|
||||
SwapToStakedSRI(SeraiAddress, NetworkId),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct RefundableInInstruction {
|
||||
pub origin: Option<ExternalAddress>,
|
||||
pub instruction: InInstruction,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct InInstructionWithBalance {
|
||||
pub instruction: InInstruction,
|
||||
pub balance: ExternalBalance,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Batch {
|
||||
pub network: ExternalNetworkId,
|
||||
pub id: u32,
|
||||
pub external_network_block_hash: BlockHash,
|
||||
pub instructions: Vec<InInstructionWithBalance>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct SignedBatch {
|
||||
pub batch: Batch,
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
borsh(
|
||||
serialize_with = "serai_primitives::borsh_serialize_signature",
|
||||
deserialize_with = "serai_primitives::borsh_deserialize_signature"
|
||||
)
|
||||
)]
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Zeroize for SignedBatch {
|
||||
fn zeroize(&mut self) {
|
||||
self.batch.zeroize();
|
||||
self.signature.as_mut().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make this an associated method?
|
||||
/// The message for the batch signature.
|
||||
pub fn batch_message(batch: &Batch) -> Vec<u8> {
|
||||
[b"InInstructions-batch".as_ref(), &batch.encode()].concat()
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::{Amount, ExternalAddress, ExternalCoin, SeraiAddress};
|
||||
|
||||
use coins_primitives::OutInstruction;
|
||||
|
||||
use crate::RefundableInInstruction;
|
||||
#[cfg(feature = "std")]
|
||||
use crate::InInstruction;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Shorthand {
|
||||
Raw(RefundableInInstruction),
|
||||
Swap {
|
||||
origin: Option<ExternalAddress>,
|
||||
coin: ExternalCoin,
|
||||
minimum: Amount,
|
||||
out: OutInstruction,
|
||||
},
|
||||
SwapAndAddLiquidity {
|
||||
origin: Option<ExternalAddress>,
|
||||
minimum: Amount,
|
||||
gas: Amount,
|
||||
address: SeraiAddress,
|
||||
},
|
||||
}
|
||||
|
||||
impl Shorthand {
|
||||
#[cfg(feature = "std")]
|
||||
pub fn transfer(origin: Option<ExternalAddress>, address: SeraiAddress) -> Self {
|
||||
Self::Raw(RefundableInInstruction { origin, instruction: InInstruction::Transfer(address) })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Shorthand> for RefundableInInstruction {
|
||||
type Error = &'static str;
|
||||
fn try_from(shorthand: Shorthand) -> Result<RefundableInInstruction, &'static str> {
|
||||
Ok(match shorthand {
|
||||
Shorthand::Raw(instruction) => instruction,
|
||||
Shorthand::Swap { .. } => todo!(),
|
||||
Shorthand::SwapAndAddLiquidity { .. } => todo!(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -16,29 +16,19 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
zeroize = { version = "^1.5", features = ["derive"], optional = true }
|
||||
zeroize = { version = "^1.5", features = ["derive"] }
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
|
||||
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, optional = true }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||
|
||||
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
||||
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["alloc", "ristretto"] }
|
||||
dkg = { path = "../../crypto/dkg", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
bech32 = { version = "0.11", default-features = false }
|
||||
|
||||
[features]
|
||||
std = ["zeroize", "ciphersuite/std", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "sp-std/std", "frame-support/std"]
|
||||
borsh = ["dep:borsh"]
|
||||
serde = ["dep:serde"]
|
||||
std = ["zeroize/std", "borsh/std", "ciphersuite/std", "dkg/std", "sp-core/std", "bech32/std"]
|
||||
default = ["std"]
|
||||
borsh = [] # TODO
|
||||
serde = [] # TODO
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022-2023 Luke Parker
|
||||
Copyright (c) 2022-2025 Luke Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
4
substrate/primitives/README.md
Normal file
4
substrate/primitives/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Serai Primitives
|
||||
|
||||
`serai-primitives` represents foundational data-types used within Serai's
|
||||
Substrate blockchain.
|
|
@ -1,144 +0,0 @@
|
|||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_core::sr25519::Public;
|
||||
pub use sp_core::sr25519::Signature;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_core::{Pair as PairTrait, sr25519::Pair};
|
||||
|
||||
use sp_runtime::traits::{LookupError, Lookup, StaticLookup};
|
||||
|
||||
pub type PublicKey = Public;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_serialize_public<W: borsh::io::Write>(
|
||||
public: &Public,
|
||||
writer: &mut W,
|
||||
) -> Result<(), borsh::io::Error> {
|
||||
borsh::BorshSerialize::serialize(&public.0, writer)
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_deserialize_public<R: borsh::io::Read>(
|
||||
reader: &mut R,
|
||||
) -> Result<Public, borsh::io::Error> {
|
||||
let public: [u8; 32] = borsh::BorshDeserialize::deserialize_reader(reader)?;
|
||||
Ok(Public(public))
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_serialize_signature<W: borsh::io::Write>(
|
||||
signature: &Signature,
|
||||
writer: &mut W,
|
||||
) -> Result<(), borsh::io::Error> {
|
||||
borsh::BorshSerialize::serialize(&signature.0, writer)
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_deserialize_signature<R: borsh::io::Read>(
|
||||
reader: &mut R,
|
||||
) -> Result<Signature, borsh::io::Error> {
|
||||
let signature: [u8; 64] = borsh::BorshDeserialize::deserialize_reader(reader)?;
|
||||
Ok(Signature(signature))
|
||||
}
|
||||
|
||||
// TODO: Remove this for solely Public?
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct SeraiAddress(pub [u8; 32]);
|
||||
impl SeraiAddress {
|
||||
pub fn new(key: [u8; 32]) -> SeraiAddress {
|
||||
SeraiAddress(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for SeraiAddress {
|
||||
fn from(key: [u8; 32]) -> SeraiAddress {
|
||||
SeraiAddress(key)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for SeraiAddress {
|
||||
fn from(key: PublicKey) -> SeraiAddress {
|
||||
SeraiAddress(key.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SeraiAddress> for PublicKey {
|
||||
fn from(address: SeraiAddress) -> PublicKey {
|
||||
PublicKey::from_raw(address.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::fmt::Display for SeraiAddress {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// TODO: Bech32
|
||||
write!(f, "{:?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Substraate key pair by a name.
|
||||
///
|
||||
/// This should never be considered to have a secure private key. It has effectively no entropy.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn insecure_pair_from_name(name: &str) -> Pair {
|
||||
Pair::from_string(&format!("//{name}"), None).unwrap()
|
||||
}
|
||||
|
||||
/// Create a private key for an arbitrary ciphersuite by a name.
|
||||
///
|
||||
/// This key should never be considered a secure private key. It has effectively no entropy.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn insecure_arbitrary_key_from_name<C: ciphersuite::Ciphersuite>(name: &str) -> C::F {
|
||||
C::hash_to_F(b"insecure arbitrary key", name.as_bytes())
|
||||
}
|
||||
|
||||
pub struct AccountLookup;
|
||||
impl Lookup for AccountLookup {
|
||||
type Source = SeraiAddress;
|
||||
type Target = PublicKey;
|
||||
fn lookup(&self, source: SeraiAddress) -> Result<PublicKey, LookupError> {
|
||||
Ok(PublicKey::from_raw(source.0))
|
||||
}
|
||||
}
|
||||
impl StaticLookup for AccountLookup {
|
||||
type Source = SeraiAddress;
|
||||
type Target = PublicKey;
|
||||
fn lookup(source: SeraiAddress) -> Result<PublicKey, LookupError> {
|
||||
Ok(source.into())
|
||||
}
|
||||
fn unlookup(source: PublicKey) -> SeraiAddress {
|
||||
source.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn system_address(pallet: &'static [u8]) -> SeraiAddress {
|
||||
let mut address = [0; 32];
|
||||
let mut set = false;
|
||||
// Implement a while loop since we can't use a for loop
|
||||
let mut i = 0;
|
||||
while i < pallet.len() {
|
||||
address[i] = pallet[i];
|
||||
if address[i] != 0 {
|
||||
set = true;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
// Make sure this address isn't the identity point
|
||||
// Doesn't do address != [0; 32] since that's not const
|
||||
assert!(set, "address is the identity point");
|
||||
SeraiAddress(address)
|
||||
}
|
143
substrate/primitives/src/address.rs
Normal file
143
substrate/primitives/src/address.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use sp_core::{sr25519::Public, ConstU32, bounded::BoundedVec};
|
||||
|
||||
/*
|
||||
We only use a single HRP across all networks. This follows Serai's general practice. Addresses
|
||||
for external networks are represented in binary, without network information. to minimize
|
||||
bandwidth and reduce potential for malleability.
|
||||
|
||||
This is continued here not solely to be a continuance, yet also with appreciation for the
|
||||
simplicity. This does make it easier for users to make the mistake of using a testnet address
|
||||
where they intended to use a mainnet address (and vice-versa). Since public keys are usable on
|
||||
any network, this should have limited impact and accordingly not be the end of the world.
|
||||
|
||||
There's also precedent for this due to Ethereum (though they do have a somewhat-adopted checksum
|
||||
scheme to encode the network regardless).
|
||||
*/
|
||||
const HUMAN_READABLE_PART: bech32::Hrp = bech32::Hrp::parse_unchecked("sri");
|
||||
|
||||
/// The address for an account on Serai.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct SeraiAddress(pub [u8; 32]);
|
||||
|
||||
impl SeraiAddress {
|
||||
/// Generate an address for use by the system.
|
||||
///
|
||||
/// The returned addresses MAY be valid points. This assumes its infeasible to find the discrete
|
||||
/// logarithm for a point whose representation has a known Blake2b-256 preimage.
|
||||
// The alternative would be to massage this until its not a valid point, which isn't worth the
|
||||
// computational expense as this should be a hard problem for outputs which happen to be points.
|
||||
pub fn system(label: &[u8]) -> Self {
|
||||
Self(sp_core::blake2_256(label))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Public> for SeraiAddress {
|
||||
fn from(key: Public) -> Self {
|
||||
// The encoding of a Ristretto point is the encoding of its address
|
||||
Self(key.0)
|
||||
}
|
||||
}
|
||||
/*
|
||||
A `SeraiAddress` may not be a valid public key. The `sr25519::Public` is not a checked public
|
||||
key, solely bytes alleged to be a public key, which any `SeraiAddress` converted into a `Public`
|
||||
is also alleged to be.
|
||||
*/
|
||||
impl From<SeraiAddress> for Public {
|
||||
fn from(address: SeraiAddress) -> Self {
|
||||
Self::from_raw(address.0)
|
||||
}
|
||||
}
|
||||
|
||||
// We use Bech32m to encode addresses
|
||||
impl core::fmt::Display for SeraiAddress {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match bech32::encode_to_fmt::<bech32::Bech32m, _>(f, HUMAN_READABLE_PART, &self.0) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(bech32::EncodeError::TooLong(_)) => {
|
||||
unreachable!("32 bytes exceeded bech32 length limit?")
|
||||
}
|
||||
Err(bech32::EncodeError::Fmt(e)) => Err(e),
|
||||
// bech32::EncodeError is non-exhaustive
|
||||
Err(_) => Err(core::fmt::Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error from decoding an address.
|
||||
pub enum DecodeError {
|
||||
/// The Bech32m encoding was invalid.
|
||||
InvalidBech32m,
|
||||
/// The Bech32m Human-Readable Part was distinct.
|
||||
DistinctHrp,
|
||||
/// The encoded data's length was wrong.
|
||||
InvalidLength,
|
||||
}
|
||||
|
||||
impl core::str::FromStr for SeraiAddress {
|
||||
type Err = DecodeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
// We drop bech32's error to remain opaque to the implementation
|
||||
let decoded = bech32::primitives::decode::CheckedHrpstring::new::<bech32::Bech32m>(s)
|
||||
.map_err(|_| DecodeError::InvalidBech32m)?;
|
||||
if decoded.hrp() != HUMAN_READABLE_PART {
|
||||
Err(DecodeError::DistinctHrp)?;
|
||||
}
|
||||
|
||||
let mut res = Self([0; 32]);
|
||||
let mut iter = decoded.byte_iter();
|
||||
for i in 0 .. 32 {
|
||||
let Some(byte) = iter.next() else { Err(DecodeError::InvalidLength)? };
|
||||
res.0[i] = byte;
|
||||
}
|
||||
if iter.next().is_some() {
|
||||
Err(DecodeError::InvalidLength)?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// An address for an external network.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
|
||||
pub struct ExternalAddress(
|
||||
#[borsh(
|
||||
serialize_with = "crate::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "crate::borsh_deserialize_bounded_vec"
|
||||
)]
|
||||
BoundedVec<u8, ConstU32<{ Self::MAX_LEN }>>,
|
||||
);
|
||||
|
||||
impl ExternalAddress {
|
||||
/// The maximum length for an `ExternalAddress`.
|
||||
pub const MAX_LEN: u32 = 512;
|
||||
}
|
||||
|
||||
/// An error when converting from a `Vec`.
|
||||
pub enum FromVecError {
|
||||
/// The source `Vec` was too long to be converted.
|
||||
TooLong,
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for ExternalAddress {
|
||||
type Error = FromVecError;
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
vec.try_into().map(ExternalAddress).map_err(|_| FromVecError::TooLong)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalAddress> for Vec<u8> {
|
||||
fn from(ext: ExternalAddress) -> Vec<u8> {
|
||||
ext.0.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl zeroize::Zeroize for ExternalAddress {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.as_mut().zeroize();
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
use core::{
|
||||
ops::{Add, Sub, Mul},
|
||||
fmt::Debug,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
/// The type used for amounts within Substrate.
|
||||
// Distinct from Amount due to Substrate's requirements on this type.
|
||||
// While Amount could have all the necessary traits implemented, not only are they many, it'd make
|
||||
// Amount a large type with a variety of misc functions.
|
||||
// The current type's minimalism sets clear bounds on usage.
|
||||
pub type SubstrateAmount = u64;
|
||||
/// The type used for amounts.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Amount(pub SubstrateAmount);
|
||||
|
||||
// TODO: these impl shouldn't panic and return error to be dealt with.
|
||||
// Otherwise we might have a panic that stops the network.
|
||||
impl Add for Amount {
|
||||
type Output = Amount;
|
||||
fn add(self, other: Amount) -> Amount {
|
||||
// Explicitly use checked_add so even if range checks are disabled, this is still checked
|
||||
Amount(self.0.checked_add(other.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Amount {
|
||||
type Output = Amount;
|
||||
fn sub(self, other: Amount) -> Amount {
|
||||
Amount(self.0.checked_sub(other.0).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Amount {
|
||||
type Output = Amount;
|
||||
fn mul(self, other: Amount) -> Amount {
|
||||
Amount(self.0.checked_mul(other.0).unwrap())
|
||||
}
|
||||
}
|
|
@ -1,38 +1,111 @@
|
|||
use core::ops::{Add, Sub, Mul};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
use crate::coin::{ExternalCoin, Coin};
|
||||
|
||||
use crate::{Amount, Coin, ExternalCoin};
|
||||
/// The type internally used to represent amounts.
|
||||
// https://github.com/rust-lang/rust/issues/8995
|
||||
pub type AmountRepr = u64;
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Balance {
|
||||
pub coin: Coin,
|
||||
/// A wrapper used to represent amounts.
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Debug,
|
||||
Zeroize,
|
||||
BorshSerialize,
|
||||
BorshDeserialize,
|
||||
)]
|
||||
pub struct Amount(pub AmountRepr);
|
||||
|
||||
impl Add for Amount {
|
||||
type Output = Option<Amount>;
|
||||
fn add(self, other: Amount) -> Option<Amount> {
|
||||
self.0.checked_add(other.0).map(Amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Amount {
|
||||
type Output = Option<Amount>;
|
||||
fn sub(self, other: Amount) -> Option<Amount> {
|
||||
self.0.checked_sub(other.0).map(Amount)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for Amount {
|
||||
type Output = Option<Amount>;
|
||||
fn mul(self, other: Amount) -> Option<Amount> {
|
||||
self.0.checked_mul(other.0).map(Amount)
|
||||
}
|
||||
}
|
||||
|
||||
/// An ExternalCoin and an Amount, forming a balance for an external coin.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ExternalBalance {
|
||||
/// The coin this is a balance for.
|
||||
pub coin: ExternalCoin,
|
||||
/// The amount of this balance.
|
||||
pub amount: Amount,
|
||||
}
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalBalance {
|
||||
pub coin: ExternalCoin,
|
||||
impl Add<Amount> for ExternalBalance {
|
||||
type Output = Option<ExternalBalance>;
|
||||
fn add(self, other: Amount) -> Option<ExternalBalance> {
|
||||
(self.amount + other).map(|amount| ExternalBalance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Amount> for ExternalBalance {
|
||||
type Output = Option<ExternalBalance>;
|
||||
fn sub(self, other: Amount) -> Option<ExternalBalance> {
|
||||
(self.amount - other).map(|amount| ExternalBalance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Amount> for ExternalBalance {
|
||||
type Output = Option<ExternalBalance>;
|
||||
fn mul(self, other: Amount) -> Option<ExternalBalance> {
|
||||
(self.amount * other).map(|amount| ExternalBalance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
/// A Coin and an Amount, forming a balance for a coin.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Balance {
|
||||
/// The coin this is a balance for.
|
||||
pub coin: Coin,
|
||||
/// The amount of this balance.
|
||||
pub amount: Amount,
|
||||
}
|
||||
|
||||
impl Add<Amount> for Balance {
|
||||
type Output = Option<Balance>;
|
||||
fn add(self, other: Amount) -> Option<Balance> {
|
||||
(self.amount + other).map(|amount| Balance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Amount> for Balance {
|
||||
type Output = Option<Balance>;
|
||||
fn sub(self, other: Amount) -> Option<Balance> {
|
||||
(self.amount - other).map(|amount| Balance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Amount> for Balance {
|
||||
type Output = Option<Balance>;
|
||||
fn mul(self, other: Amount) -> Option<Balance> {
|
||||
(self.amount * other).map(|amount| Balance { coin: self.coin, amount })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalBalance> for Balance {
|
||||
fn from(balance: ExternalBalance) -> Self {
|
||||
Balance { coin: balance.coin.into(), amount: balance.amount }
|
||||
|
@ -49,25 +122,3 @@ impl TryFrom<Balance> for ExternalBalance {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: these impl either should be removed or return errors in case of overflows
|
||||
impl Add<Amount> for Balance {
|
||||
type Output = Balance;
|
||||
fn add(self, other: Amount) -> Balance {
|
||||
Balance { coin: self.coin, amount: self.amount + other }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Amount> for Balance {
|
||||
type Output = Balance;
|
||||
fn sub(self, other: Amount) -> Balance {
|
||||
Balance { coin: self.coin, amount: self.amount - other }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Amount> for Balance {
|
||||
type Output = Balance;
|
||||
fn mul(self, other: Amount) -> Balance {
|
||||
Balance { coin: self.coin, amount: self.amount * other }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_core::H256;
|
||||
|
||||
/// The type used to identify block numbers.
|
||||
#[derive(
|
||||
Clone, Copy, Default, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct BlockNumber(pub u64);
|
||||
impl From<u64> for BlockNumber {
|
||||
fn from(number: u64) -> BlockNumber {
|
||||
BlockNumber(number)
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify block hashes.
|
||||
// This may not be universally compatible
|
||||
// If a block exists with a hash which isn't 32-bytes, it can be hashed into a value with 32-bytes
|
||||
// This would require the processor to maintain a mapping of 32-byte IDs to actual hashes, which
|
||||
// would be fine
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct BlockHash(pub [u8; 32]);
|
||||
|
||||
impl AsRef<[u8]> for BlockHash {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for BlockHash {
|
||||
fn from(hash: [u8; 32]) -> BlockHash {
|
||||
BlockHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<H256> for BlockHash {
|
||||
fn from(hash: H256) -> BlockHash {
|
||||
BlockHash(hash.into())
|
||||
}
|
||||
}
|
118
substrate/primitives/src/coin.rs
Normal file
118
substrate/primitives/src/coin.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use zeroize::Zeroize;
|
||||
|
||||
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::network_id::{ExternalNetworkId, NetworkId};
|
||||
|
||||
/// The type used to identify coins native to external networks.
|
||||
///
|
||||
/// This type serializes to a subset of `Coin`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[non_exhaustive]
|
||||
pub enum ExternalCoin {
|
||||
/// Bitcoin, from the Bitcoin network.
|
||||
Bitcoin = 1,
|
||||
/// Ether, from the Ethereum network.
|
||||
Ether = 2,
|
||||
/// Dai Stablecoin, from the Ethereum network.
|
||||
Dai = 3,
|
||||
/// Monero, from the Monero network.
|
||||
Monero = 4,
|
||||
}
|
||||
|
||||
impl ExternalCoin {
|
||||
/// All external coins.
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
[ExternalCoin::Bitcoin, ExternalCoin::Ether, ExternalCoin::Dai, ExternalCoin::Monero]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize)]
|
||||
pub enum Coin {
|
||||
/// The Serai coin.
|
||||
Serai,
|
||||
/// An external coin.
|
||||
External(ExternalCoin),
|
||||
}
|
||||
|
||||
impl BorshSerialize for Coin {
|
||||
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Serai => writer.write_all(&[0]),
|
||||
Self::External(external) => external.serialize(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorshDeserialize for Coin {
|
||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let mut kind = [0xff];
|
||||
reader.read_exact(&mut kind)?;
|
||||
match kind[0] {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => ExternalCoin::deserialize_reader(&mut kind.as_slice()).map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
/// All coins.
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
core::iter::once(Coin::Serai).chain(ExternalCoin::all().map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalCoin> for Coin {
|
||||
fn from(coin: ExternalCoin) -> Self {
|
||||
Coin::External(coin)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for ExternalCoin {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(coin: Coin) -> Result<Self, Self::Error> {
|
||||
match coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(ext) => Ok(ext),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCoin {
|
||||
/// The external network this coin is native to.
|
||||
pub fn network(&self) -> ExternalNetworkId {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => ExternalNetworkId::Bitcoin,
|
||||
ExternalCoin::Ether | ExternalCoin::Dai => ExternalNetworkId::Ethereum,
|
||||
ExternalCoin::Monero => ExternalNetworkId::Monero,
|
||||
}
|
||||
}
|
||||
|
||||
/// The decimals used for a single human unit of this coin.
|
||||
///
|
||||
/// This may be less than the decimals used for a single human unit of this coin *by defined
|
||||
/// convention*. If so, that means Serai is *truncating* the decimals. A coin which is defined
|
||||
/// as having 8 decimals, while Serai claims it has 4 decimals, will have `0.00019999`
|
||||
/// interpreted as `0.0001` (in human units, in atomic units, 19999 will be interpreted as 1).
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
ExternalCoin::Bitcoin | ExternalCoin::Ether | ExternalCoin::Dai => 8,
|
||||
ExternalCoin::Monero => 12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
/// The network this coin is native to.
|
||||
pub fn network(&self) -> NetworkId {
|
||||
match self {
|
||||
Coin::Serai => NetworkId::Serai,
|
||||
Coin::External(c) => c.network().into(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +1,13 @@
|
|||
use crate::BlockNumber;
|
||||
use core::time::Duration;
|
||||
|
||||
// 1 MB
|
||||
pub const BLOCK_SIZE: u32 = 1024 * 1024;
|
||||
// 6 seconds
|
||||
// TODO: Use Duration
|
||||
pub const TARGET_BLOCK_TIME: u64 = 6;
|
||||
/// The target block time.
|
||||
pub const TARGET_BLOCK_TIME: Duration = Duration::from_secs(6);
|
||||
|
||||
/// Measured in blocks.
|
||||
pub const MINUTES: BlockNumber = 60 / TARGET_BLOCK_TIME;
|
||||
pub const HOURS: BlockNumber = 60 * MINUTES;
|
||||
pub const DAYS: BlockNumber = 24 * HOURS;
|
||||
pub const WEEKS: BlockNumber = 7 * DAYS;
|
||||
// Defines a month as 30 days, which is slightly inaccurate
|
||||
pub const MONTHS: BlockNumber = 30 * DAYS;
|
||||
// Defines a year as 12 inaccurate months, which is 360 days literally (~1.5% off)
|
||||
pub const YEARS: BlockNumber = 12 * MONTHS;
|
||||
/// The intended duration for a session.
|
||||
// 1 week
|
||||
pub const SESSION_LENGTH: Duration = Duration::from_secs(7 * 24 * 60 * 60);
|
||||
|
||||
/// 6 months of blocks
|
||||
pub const GENESIS_SRI_TRICKLE_FEED: u64 = 6 * MONTHS;
|
||||
|
||||
// 100 Million SRI
|
||||
pub const GENESIS_SRI: u64 = 100_000_000 * 10_u64.pow(8);
|
||||
|
||||
/// This needs to be long enough for arbitrage to occur and make holding any fake price up
|
||||
/// sufficiently unrealistic.
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub const ARBITRAGE_TIME: u16 = (2 * HOURS) as u16;
|
||||
|
||||
/// Since we use the median price, double the window length.
|
||||
///
|
||||
/// We additionally +1 so there is a true median.
|
||||
pub const MEDIAN_PRICE_WINDOW_LENGTH: u16 = (2 * ARBITRAGE_TIME) + 1;
|
||||
|
||||
/// Amount of blocks per epoch in the fast-epoch feature that is used in tests.
|
||||
pub const FAST_EPOCH_DURATION: u64 = 2 * MINUTES;
|
||||
|
||||
/// Amount of blocks for the initial period era of the emissions under the fast-epoch feature.
|
||||
pub const FAST_EPOCH_INITIAL_PERIOD: u64 = 2 * FAST_EPOCH_DURATION;
|
||||
/// The maximum amount of key shares per set.
|
||||
pub const MAX_KEY_SHARES_PER_SET: u16 = 150;
|
||||
/// The maximum amount of key shares per set, as an u32.
|
||||
pub const MAX_KEY_SHARES_PER_SET_U32: u32 = MAX_KEY_SHARES_PER_SET as u32;
|
||||
|
|
66
substrate/primitives/src/crypto.rs
Normal file
66
substrate/primitives/src/crypto.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use zeroize::Zeroize;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
|
||||
/// A Ristretto public key.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Public(pub [u8; 32]);
|
||||
impl From<sp_core::sr25519::Public> for Public {
|
||||
fn from(public: sp_core::sr25519::Public) -> Self {
|
||||
Self(public.0)
|
||||
}
|
||||
}
|
||||
impl From<Public> for sp_core::sr25519::Public {
|
||||
fn from(public: Public) -> Self {
|
||||
Self::from_raw(public.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A sr25519 signature.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Signature(pub [u8; 64]);
|
||||
impl From<sp_core::sr25519::Signature> for Signature {
|
||||
fn from(signature: sp_core::sr25519::Signature) -> Self {
|
||||
Self(signature.0)
|
||||
}
|
||||
}
|
||||
impl From<Signature> for sp_core::sr25519::Signature {
|
||||
fn from(signature: Signature) -> Self {
|
||||
Self::from_raw(signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A key for an external network.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ExternalKey(
|
||||
#[borsh(
|
||||
serialize_with = "crate::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "crate::borsh_deserialize_bounded_vec"
|
||||
)]
|
||||
pub BoundedVec<u8, ConstU32<{ Self::MAX_LEN }>>,
|
||||
);
|
||||
|
||||
impl Zeroize for ExternalKey {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.as_mut().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalKey {
|
||||
/// The maximum length for am external key.
|
||||
/*
|
||||
This support keys up to 96 bytes (such as BLS12-381 G2, which is the largest elliptic-curve
|
||||
group element we might reasonably use as a key). This can always be increased if we need to
|
||||
adopt a different cryptosystem (one where verification keys are multiple group elements, or
|
||||
where group elements do exceed 96 bytes, such as RSA).
|
||||
*/
|
||||
pub const MAX_LEN: u32 = 96;
|
||||
}
|
||||
|
||||
/// The key pair for a validator set.
|
||||
///
|
||||
/// This is their Ristretto key, used for publishing data onto Serai, and their key on the external
|
||||
/// network.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct KeyPair(pub Public, pub ExternalKey);
|
|
@ -1,19 +0,0 @@
|
|||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
|
||||
use crate::{Coin, SubstrateAmount};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct QuotePriceParams {
|
||||
pub coin1: Coin,
|
||||
pub coin2: Coin,
|
||||
pub amount: SubstrateAmount,
|
||||
pub include_fee: bool,
|
||||
pub exact_in: bool,
|
||||
}
|
24
substrate/primitives/src/genesis.rs
Normal file
24
substrate/primitives/src/genesis.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::balance::Amount;
|
||||
|
||||
/// The value of non-Bitcoin externals coins present at genesis, relative to Bitcoin.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct GenesisValues {
|
||||
/// The value of Ether, relative to Bitcoin.
|
||||
pub ether: Amount,
|
||||
/// The value of DAI, relative to Bitcoin.
|
||||
pub dai: Amount,
|
||||
/// The value of Monero, relative to Bitcoin.
|
||||
pub monero: Amount,
|
||||
}
|
||||
|
||||
impl GenesisValues {
|
||||
/// The message for the oraclize_values signature.
|
||||
pub fn oraclize_values_message(&self) -> Vec<u8> {
|
||||
borsh::to_vec(&(b"GenesisLiquidity-oraclize_values", self)).unwrap()
|
||||
}
|
||||
}
|
171
substrate/primitives/src/instructions/in/batch.rs
Normal file
171
substrate/primitives/src/instructions/in/batch.rs
Normal file
|
@ -0,0 +1,171 @@
|
|||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::{
|
||||
BlockHash, crypto::Signature, network_id::ExternalNetworkId,
|
||||
instructions::InInstructionWithBalance,
|
||||
};
|
||||
|
||||
/*
|
||||
`Batch`s have a size limit we enforce upon deserialization.
|
||||
|
||||
The naive solution would be to deserialize, then serialize, and check the serialized length is
|
||||
less than the maximum. This performs a redundant allocation and is computationally non-trivial.
|
||||
|
||||
The next solution would be to wrap the deserialization with a `Cursor` so one can check the
|
||||
amount read, yet `Cursor` isn't available under no-std.
|
||||
|
||||
We solve this by manually implementing a `Cursor`-equivalent (for our purposes) which let us
|
||||
check the total amount read is `<=` the maximum size.
|
||||
|
||||
The issue is we need every call to `BorshDeserialize::deserialize_reader` to use our custom
|
||||
reader, which requires manually implementing it, which means we can't use the derive macro and
|
||||
can't ensure it follows the borsh specification. We solve this by generating two identical
|
||||
structs, one internal with a derived `BorshDeserialize::deserialize_reader`, one public with a
|
||||
manually implemented `BorshDeserialize::deserialize_reader` wrapping the internal struct's. This
|
||||
lets us ensure the correct reader is used and error if the size limit is hit, while still using
|
||||
a derived `BorshDeserialize` which will definitively be compliant.
|
||||
*/
|
||||
macro_rules! batch_struct {
|
||||
(#[$derive: meta] $pub: vis $name: ident) => {
|
||||
/// A batch of `InInstruction`s to publish onto Serai.
|
||||
#[allow(clippy::needless_pub_self)]
|
||||
#[$derive]
|
||||
$pub struct $name {
|
||||
/// The size this will be once encoded.
|
||||
#[allow(dead_code)] // This is unused for the `BatchDeserialize` instance
|
||||
#[borsh(skip)]
|
||||
encoded_size: usize,
|
||||
|
||||
/// The network this batch of instructions is coming from.
|
||||
network: ExternalNetworkId,
|
||||
/// The ID of this `Batch`.
|
||||
id: u32,
|
||||
/// The hash of the external network's block which produced this `Batch`.
|
||||
external_network_block_hash: BlockHash,
|
||||
/// The instructions to execute.
|
||||
instructions: Vec<InInstructionWithBalance>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
batch_struct!(#[derive(BorshDeserialize)] pub(self) BatchDeserialize);
|
||||
batch_struct!(#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize)] pub Batch);
|
||||
|
||||
impl BorshDeserialize for Batch {
|
||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||
// A custom reader which enforces the `Batch`'s max size limit
|
||||
struct SizeLimitReader<'a, R: io::Read> {
|
||||
reader: &'a mut R,
|
||||
read: usize,
|
||||
}
|
||||
impl<R: io::Read> io::Read for SizeLimitReader<'_, R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let read = self.reader.read(buf)?;
|
||||
self.read = self.read.saturating_add(read);
|
||||
if self.read > Batch::MAX_SIZE {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Batch size exceeded maximum"))?;
|
||||
}
|
||||
Ok(read)
|
||||
}
|
||||
}
|
||||
|
||||
let mut size_limit_reader = SizeLimitReader { reader, read: 0 };
|
||||
let BatchDeserialize {
|
||||
encoded_size: _,
|
||||
network,
|
||||
id,
|
||||
external_network_block_hash,
|
||||
instructions,
|
||||
} = <_>::deserialize_reader(&mut size_limit_reader)?;
|
||||
Ok(Batch {
|
||||
encoded_size: size_limit_reader.read,
|
||||
network,
|
||||
id,
|
||||
external_network_block_hash,
|
||||
instructions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An error incurred while pushing an instruction onto a `Batch`.
|
||||
pub enum PushInstructionError {
|
||||
/// The Batch's max size was exceeded.
|
||||
MaxSizeExceeded,
|
||||
}
|
||||
|
||||
impl Batch {
|
||||
/// The maximum size of a valid `Batch`'s encoding.
|
||||
const MAX_SIZE: usize = 32_768;
|
||||
|
||||
/// Create a new Batch.
|
||||
pub fn new(network: ExternalNetworkId, id: u32, external_network_block_hash: BlockHash) -> Self {
|
||||
let mut batch =
|
||||
Batch { encoded_size: 0, network, id, external_network_block_hash, instructions: vec![] };
|
||||
batch.encoded_size = borsh::to_vec(&batch).unwrap().len();
|
||||
batch
|
||||
}
|
||||
|
||||
/// Push an `InInstructionWithBalance` onto this `Batch`.
|
||||
pub fn push_instruction(
|
||||
&mut self,
|
||||
instruction: InInstructionWithBalance,
|
||||
) -> Result<(), PushInstructionError> {
|
||||
if (self.encoded_size.saturating_add(borsh::to_vec(&instruction).unwrap().len())) >
|
||||
Self::MAX_SIZE
|
||||
{
|
||||
Err(PushInstructionError::MaxSizeExceeded)?;
|
||||
}
|
||||
self.instructions.push(instruction);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The message to sign when publishing this Batch.
|
||||
pub fn publish_batch_message(&self) -> Vec<u8> {
|
||||
const DST: &[u8] = b"InInstructions-publish_batch";
|
||||
// We don't estimate the size of this Batch, we just reserve a small constant capacity
|
||||
let mut buf = Vec::with_capacity(1024);
|
||||
(DST, self).serialize(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
/// The network this batch of instructions is coming from.
|
||||
pub fn network(&self) -> ExternalNetworkId {
|
||||
self.network
|
||||
}
|
||||
|
||||
/// The ID of this `Batch`.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// The hash of the external network's block which produced this `Batch`.
|
||||
pub fn external_network_block_hash(&self) -> BlockHash {
|
||||
self.external_network_block_hash
|
||||
}
|
||||
|
||||
/// The instructions within this `Batch`.
|
||||
pub fn instructions(&self) -> &[InInstructionWithBalance] {
|
||||
&self.instructions
|
||||
}
|
||||
}
|
||||
|
||||
/// A signed batch.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct SignedBatch {
|
||||
/// The signed batch.
|
||||
pub batch: Batch,
|
||||
/// The signature.
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Zeroize for SignedBatch {
|
||||
fn zeroize(&mut self) {
|
||||
self.batch.zeroize();
|
||||
self.signature.0.as_mut().zeroize();
|
||||
}
|
||||
}
|
86
substrate/primitives/src/instructions/in/mod.rs
Normal file
86
substrate/primitives/src/instructions/in/mod.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use zeroize::Zeroize;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::{
|
||||
address::{SeraiAddress, ExternalAddress},
|
||||
balance::{Amount, ExternalBalance, Balance},
|
||||
instructions::OutInstruction,
|
||||
};
|
||||
|
||||
mod batch;
|
||||
pub use batch::*;
|
||||
|
||||
/// The destination for coins.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Destination {
|
||||
/// The Serai address to transfer the coins to.
|
||||
Serai(SeraiAddress),
|
||||
/// Burn the coins with the included `OutInstruction`.
|
||||
Burn(OutInstruction),
|
||||
}
|
||||
|
||||
/// An instruction on how to handle coins in.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum InInstruction {
|
||||
/// Add the coins as genesis liquidity.
|
||||
GenesisLiquidity(SeraiAddress),
|
||||
/// Use the coins to swap to staked SRI, pre-economic security.
|
||||
SwapToStakedSri {
|
||||
/// The validator to allocate the stake to.
|
||||
validator: SeraiAddress,
|
||||
/// The minimum amount of staked SRI to swap to.
|
||||
minimum: Amount,
|
||||
},
|
||||
/// Transfer the coins to a Serai address, swapping some for SRI.
|
||||
TransferWithSwap {
|
||||
/// The Serai address to transfer the coins to, after swapping some.
|
||||
to: SeraiAddress,
|
||||
/// The maximum amount of coins to swap for the intended amount of SRI.
|
||||
maximum_swap: Amount,
|
||||
/// The SRI amount to swap some of the coins for.
|
||||
sri: Amount,
|
||||
},
|
||||
/// Transfer the coins to a Serai address.
|
||||
Transfer {
|
||||
/// The Serai address to transfer the coins to.
|
||||
to: SeraiAddress,
|
||||
},
|
||||
/// Swap part of the coins to SRI and add the coins as liquidity.
|
||||
SwapAndAddLiquidity {
|
||||
/// The owner to-be of the added liquidity.
|
||||
owner: SeraiAddress,
|
||||
/// The amount of SRI to add within the liquidity position.
|
||||
sri: Amount,
|
||||
/// The minimum amount of the coin to add as liquidity.
|
||||
minimum_coin: Amount,
|
||||
/// The amount of SRI to swap to and send to the owner to-be to pay for transactions on Serai.
|
||||
sri_for_fees: Amount,
|
||||
},
|
||||
/// Swap the coins.
|
||||
Swap {
|
||||
/// The minimum balance to receive.
|
||||
minimum_out: Balance,
|
||||
/// The destination to transfer the balance to.
|
||||
///
|
||||
/// If `Destination::Burn`, the balance out will be burnt with the included `OutInstruction`.
|
||||
destination: Destination,
|
||||
},
|
||||
}
|
||||
|
||||
/// An instruction on how to handle coins in with the address to return the coins to on error.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct RefundableInInstruction {
|
||||
/// The instruction on how to handle coins in.
|
||||
pub instruction: InInstruction,
|
||||
/// The address to return the coins to on error.
|
||||
pub return_address: Option<ExternalAddress>,
|
||||
}
|
||||
|
||||
/// An instruction on how to handle coins in with the balance to use for the coins in.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct InInstructionWithBalance {
|
||||
/// The instruction on how to handle coins in.
|
||||
pub instruction: OutInstruction,
|
||||
/// The coins in.
|
||||
pub balance: ExternalBalance,
|
||||
}
|
5
substrate/primitives/src/instructions/mod.rs
Normal file
5
substrate/primitives/src/instructions/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
mod r#in;
|
||||
pub use r#in::{InInstruction, InInstructionWithBalance, PushInstructionError, Batch, SignedBatch};
|
||||
|
||||
mod out;
|
||||
pub use out::{OutInstruction, OutInstructionWithBalance};
|
21
substrate/primitives/src/instructions/out.rs
Normal file
21
substrate/primitives/src/instructions/out.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use zeroize::Zeroize;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::{address::ExternalAddress, balance::ExternalBalance};
|
||||
|
||||
/// An instruction on how to transfer coins out.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum OutInstruction {
|
||||
/// Transfer to the specified address.
|
||||
Transfer(ExternalAddress),
|
||||
}
|
||||
|
||||
/// An instruction on how to transfer coins out with the balance to use for the transfer out.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct OutInstructionWithBalance {
|
||||
/// The instruction on how to transfer coins out.
|
||||
pub instruction: OutInstruction,
|
||||
/// The balance to use for the transfer out.
|
||||
pub balance: ExternalBalance,
|
||||
}
|
|
@ -1,168 +1,79 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate alloc;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
use ::borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
/// Wrappers to implement Borsh on non-Borsh-implementing types.
|
||||
#[doc(hidden)]
|
||||
pub mod sp_borsh;
|
||||
pub(crate) use sp_borsh::*;
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
/// Constants within the Serai protocol.
|
||||
pub mod constants;
|
||||
|
||||
#[cfg(test)]
|
||||
use sp_io::TestExternalities;
|
||||
/// Cryptographic types.
|
||||
pub mod crypto;
|
||||
|
||||
#[cfg(test)]
|
||||
use frame_support::{pallet_prelude::*, Identity, traits::StorageInstance};
|
||||
/// Address types.
|
||||
pub mod address;
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
pub use sp_application_crypto as crypto;
|
||||
/// Types for identifying coins.
|
||||
pub mod coin;
|
||||
|
||||
mod amount;
|
||||
pub use amount::*;
|
||||
/// The `Amount`, `ExternalBalance`, and `Balance` types.
|
||||
pub mod balance;
|
||||
|
||||
mod block;
|
||||
pub use block::*;
|
||||
/// Types for genesis.
|
||||
pub mod genesis;
|
||||
|
||||
mod networks;
|
||||
pub use networks::*;
|
||||
/// Types for identifying networks and their properties.
|
||||
pub mod network_id;
|
||||
|
||||
mod balance;
|
||||
pub use balance::*;
|
||||
/// Types for identifying and working with validator sets.
|
||||
pub mod validator_sets;
|
||||
|
||||
mod account;
|
||||
pub use account::*;
|
||||
/// Types for signaling.
|
||||
pub mod signals;
|
||||
|
||||
mod constants;
|
||||
pub use constants::*;
|
||||
/// Instruction types.
|
||||
pub mod instructions;
|
||||
|
||||
mod dex;
|
||||
#[allow(unused_imports)]
|
||||
pub use dex::*;
|
||||
|
||||
pub type BlockNumber = u64;
|
||||
pub type Header = sp_runtime::generic::Header<BlockNumber, sp_runtime::traits::BlakeTwo256>;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_serialize_bounded_vec<W: borsh::io::Write, T: BorshSerialize, const B: u32>(
|
||||
bounded: &BoundedVec<T, ConstU32<B>>,
|
||||
writer: &mut W,
|
||||
) -> Result<(), borsh::io::Error> {
|
||||
borsh::BorshSerialize::serialize(bounded.as_slice(), writer)
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
pub fn borsh_deserialize_bounded_vec<R: borsh::io::Read, T: BorshDeserialize, const B: u32>(
|
||||
reader: &mut R,
|
||||
) -> Result<BoundedVec<T, ConstU32<B>>, borsh::io::Error> {
|
||||
let vec: Vec<T> = borsh::BorshDeserialize::deserialize_reader(reader)?;
|
||||
vec.try_into().map_err(|_| borsh::io::Error::other("bound exceeded"))
|
||||
}
|
||||
|
||||
pub const MAX_ADDRESS_LEN: u32 = 512;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalAddress(
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
borsh(
|
||||
serialize_with = "borsh_serialize_bounded_vec",
|
||||
deserialize_with = "borsh_deserialize_bounded_vec"
|
||||
)
|
||||
)]
|
||||
BoundedVec<u8, ConstU32<{ MAX_ADDRESS_LEN }>>,
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
impl Zeroize for ExternalAddress {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.as_mut().zeroize()
|
||||
/// The type used to identify block numbers.
|
||||
///
|
||||
/// A block's number is its zero-indexed position on the list of blocks which form a blockchain.
|
||||
/// For non-linear structures, this would presumably be the zero-indexed position within some
|
||||
/// topological order.
|
||||
#[derive(
|
||||
Clone, Copy, Default, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub struct BlockNumber(pub u64);
|
||||
impl From<u64> for BlockNumber {
|
||||
fn from(number: u64) -> BlockNumber {
|
||||
BlockNumber(number)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalAddress {
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new(address: Vec<u8>) -> Result<ExternalAddress, &'static str> {
|
||||
Ok(ExternalAddress(address.try_into().map_err(|_| "address length exceeds {MAX_ADDRESS_LEN}")?))
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn consume(self) -> Vec<u8> {
|
||||
self.0.into_inner()
|
||||
/// The type used to identify block hashes.
|
||||
/*
|
||||
Across all networks, block hashes may not be 32 bytes. There may be a network which targets 256
|
||||
bits of security and accordingly has a 64-byte block hash. Serai only targets a 128-bit security
|
||||
level so this is fine for our use-case. If we do ever see a 64-byte block hash, we can simply
|
||||
hash it into a 32-byte hash or truncate it.
|
||||
*/
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct BlockHash(pub [u8; 32]);
|
||||
impl From<[u8; 32]> for BlockHash {
|
||||
fn from(hash: [u8; 32]) -> BlockHash {
|
||||
BlockHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for ExternalAddress {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
impl From<sp_core::H256> for BlockHash {
|
||||
fn from(hash: sp_core::H256) -> BlockHash {
|
||||
BlockHash(hash.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Lexicographically reverses a given byte array.
|
||||
pub fn reverse_lexicographic_order<const N: usize>(bytes: [u8; N]) -> [u8; N] {
|
||||
let mut res = [0u8; N];
|
||||
for (i, byte) in bytes.iter().enumerate() {
|
||||
res[i] = !*byte;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reverse_lexicographic_order() {
|
||||
TestExternalities::default().execute_with(|| {
|
||||
use rand_core::{RngCore, OsRng};
|
||||
|
||||
struct Storage;
|
||||
impl StorageInstance for Storage {
|
||||
fn pallet_prefix() -> &'static str {
|
||||
"LexicographicOrder"
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX: &'static str = "storage";
|
||||
}
|
||||
type Map = StorageMap<Storage, Identity, [u8; 8], (), OptionQuery>;
|
||||
|
||||
struct StorageReverse;
|
||||
impl StorageInstance for StorageReverse {
|
||||
fn pallet_prefix() -> &'static str {
|
||||
"LexicographicOrder"
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX: &'static str = "storagereverse";
|
||||
}
|
||||
type MapReverse = StorageMap<StorageReverse, Identity, [u8; 8], (), OptionQuery>;
|
||||
|
||||
// populate the maps
|
||||
let mut amounts = vec![];
|
||||
for _ in 0 .. 100 {
|
||||
amounts.push(OsRng.next_u64());
|
||||
}
|
||||
|
||||
let mut amounts_sorted = amounts.clone();
|
||||
amounts_sorted.sort();
|
||||
for a in amounts {
|
||||
Map::set(a.to_be_bytes(), Some(()));
|
||||
MapReverse::set(reverse_lexicographic_order(a.to_be_bytes()), Some(()));
|
||||
}
|
||||
|
||||
// retrive back and check whether they are sorted as expected
|
||||
let total_size = amounts_sorted.len();
|
||||
let mut map_iter = Map::iter_keys();
|
||||
let mut reverse_map_iter = MapReverse::iter_keys();
|
||||
for i in 0 .. amounts_sorted.len() {
|
||||
let first = map_iter.next().unwrap();
|
||||
let second = reverse_map_iter.next().unwrap();
|
||||
|
||||
assert_eq!(u64::from_be_bytes(first), amounts_sorted[i]);
|
||||
assert_eq!(
|
||||
u64::from_be_bytes(reverse_lexicographic_order(second)),
|
||||
amounts_sorted[total_size - (i + 1)]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
124
substrate/primitives/src/network_id.rs
Normal file
124
substrate/primitives/src/network_id.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use zeroize::Zeroize;
|
||||
|
||||
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::coin::{ExternalCoin, Coin};
|
||||
|
||||
/// Identifier for an embedded elliptic curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum EmbeddedEllipticCurve {
|
||||
/// The Embedwards25519 curve, defined over (embedded into) Ed25519's/Ristretto's scalar field.
|
||||
Embedwards25519,
|
||||
/// The secq256k1 curve, forming a cycle with secp256k1.
|
||||
Secq256k1,
|
||||
}
|
||||
|
||||
/// The type used to identify external networks.
|
||||
///
|
||||
/// This type serializes to a subset of `NetworkId`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[non_exhaustive]
|
||||
pub enum ExternalNetworkId {
|
||||
/// The Bitcoin network.
|
||||
Bitcoin = 1,
|
||||
/// The Ethereum network.
|
||||
Ethereum = 2,
|
||||
/// The Monero network.
|
||||
Monero = 3,
|
||||
}
|
||||
|
||||
impl ExternalNetworkId {
|
||||
/// All external networks.
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
[ExternalNetworkId::Bitcoin, ExternalNetworkId::Ethereum, ExternalNetworkId::Monero].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalNetworkId {
|
||||
/// The embedded elliptic curves actively used for this network.
|
||||
///
|
||||
/// This is guaranteed to return `[Embedwards25519]` or
|
||||
/// `[Embedwards25519, *network specific curve*]`.
|
||||
pub fn embedded_elliptic_curves(&self) -> &'static [EmbeddedEllipticCurve] {
|
||||
match self {
|
||||
// We need to generate a Ristretto key for oraclizing and a Secp256k1 key for the network
|
||||
Self::Bitcoin | Self::Ethereum => {
|
||||
&[EmbeddedEllipticCurve::Embedwards25519, EmbeddedEllipticCurve::Secq256k1]
|
||||
}
|
||||
// Since the oraclizing key curve is the same as the network's curve, we only need it
|
||||
Self::Monero => &[EmbeddedEllipticCurve::Embedwards25519],
|
||||
}
|
||||
}
|
||||
|
||||
/// The coins native to this network.
|
||||
pub fn coins(&self) -> &'static [ExternalCoin] {
|
||||
match self {
|
||||
Self::Bitcoin => &[ExternalCoin::Bitcoin],
|
||||
Self::Ethereum => &[ExternalCoin::Ether, ExternalCoin::Dai],
|
||||
Self::Monero => &[ExternalCoin::Monero],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize)]
|
||||
pub enum NetworkId {
|
||||
/// The Serai network.
|
||||
Serai,
|
||||
/// An external network.
|
||||
External(ExternalNetworkId),
|
||||
}
|
||||
|
||||
impl BorshSerialize for NetworkId {
|
||||
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Self::Serai => writer.write_all(&[0]),
|
||||
Self::External(external) => external.serialize(writer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorshDeserialize for NetworkId {
|
||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||
let mut kind = [0xff];
|
||||
reader.read_exact(&mut kind)?;
|
||||
match kind[0] {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => ExternalNetworkId::deserialize_reader(&mut kind.as_slice()).map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkId {
|
||||
/// All networks.
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
core::iter::once(NetworkId::Serai).chain(ExternalNetworkId::all().map(Into::into))
|
||||
}
|
||||
|
||||
/// The coins native to this network.
|
||||
pub fn coins(self) -> impl Iterator<Item = Coin> {
|
||||
let (coins, external_coins): (&[Coin], &[ExternalCoin]) = match self {
|
||||
NetworkId::Serai => (&[Coin::Serai], &[]),
|
||||
NetworkId::External(ext) => (&[], ext.coins()),
|
||||
};
|
||||
coins.iter().copied().chain(external_coins.iter().copied().map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalNetworkId> for NetworkId {
|
||||
fn from(network: ExternalNetworkId) -> Self {
|
||||
NetworkId::External(network)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<NetworkId> for ExternalNetworkId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(network: NetworkId) -> Result<Self, Self::Error> {
|
||||
match network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(ext) => Ok(ext),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,479 +0,0 @@
|
|||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use scale::{Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
use sp_std::{vec, vec::Vec};
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use crate::{borsh_serialize_bounded_vec, borsh_deserialize_bounded_vec};
|
||||
|
||||
/// Identifier for an embedded elliptic curve.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum EmbeddedEllipticCurve {
|
||||
Embedwards25519,
|
||||
Secq256k1,
|
||||
}
|
||||
|
||||
/// The type used to identify external networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ExternalNetworkId {
|
||||
Bitcoin,
|
||||
Ethereum,
|
||||
Monero,
|
||||
}
|
||||
|
||||
impl Encode for ExternalNetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
ExternalNetworkId::Bitcoin => vec![1],
|
||||
ExternalNetworkId::Ethereum => vec![2],
|
||||
ExternalNetworkId::Monero => vec![3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for ExternalNetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
1 => Ok(Self::Bitcoin),
|
||||
2 => Ok(Self::Ethereum),
|
||||
3 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for ExternalNetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for ExternalNetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalNetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalNetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalNetworkId::decode(&mut kind.as_slice())
|
||||
.map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum NetworkId {
|
||||
Serai,
|
||||
External(ExternalNetworkId),
|
||||
}
|
||||
|
||||
impl Encode for NetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
NetworkId::Serai => vec![0],
|
||||
NetworkId::External(network) => network.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for NetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalNetworkId::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for NetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for NetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for NetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for NetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
NetworkId::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalNetworkId {
|
||||
/// The embedded elliptic curve actively used for this network.
|
||||
///
|
||||
/// This is guaranteed to return `[]`, `[Embedwards25519]`, or
|
||||
/// `[Embedwards25519, *network specific curve*]`.
|
||||
pub fn embedded_elliptic_curves(&self) -> &'static [EmbeddedEllipticCurve] {
|
||||
match self {
|
||||
// We need to generate a Ristretto key for oraclizing and a Secp256k1 key for the network
|
||||
Self::Bitcoin | Self::Ethereum => {
|
||||
&[EmbeddedEllipticCurve::Embedwards25519, EmbeddedEllipticCurve::Secq256k1]
|
||||
}
|
||||
// Since the oraclizing key curve is the same as the network's curve, we only need it
|
||||
Self::Monero => &[EmbeddedEllipticCurve::Embedwards25519],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coins(&self) -> Vec<ExternalCoin> {
|
||||
match self {
|
||||
Self::Bitcoin => vec![ExternalCoin::Bitcoin],
|
||||
Self::Ethereum => vec![ExternalCoin::Ether, ExternalCoin::Dai],
|
||||
Self::Monero => vec![ExternalCoin::Monero],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkId {
|
||||
/// The embedded elliptic curve actively used for this network.
|
||||
///
|
||||
/// This is guaranteed to return `[]`, `[Embedwards25519]`, or
|
||||
/// `[Embedwards25519, *network specific curve*]`.
|
||||
pub fn embedded_elliptic_curves(&self) -> &'static [EmbeddedEllipticCurve] {
|
||||
match self {
|
||||
Self::Serai => &[],
|
||||
Self::External(network) => network.embedded_elliptic_curves(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coins(&self) -> Vec<Coin> {
|
||||
match self {
|
||||
Self::Serai => vec![Coin::Serai],
|
||||
Self::External(network) => {
|
||||
network.coins().into_iter().map(core::convert::Into::into).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalNetworkId> for NetworkId {
|
||||
fn from(network: ExternalNetworkId) -> Self {
|
||||
NetworkId::External(network)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<NetworkId> for ExternalNetworkId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(network: NetworkId) -> Result<Self, Self::Error> {
|
||||
match network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(n) => Ok(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const EXTERNAL_NETWORKS: [ExternalNetworkId; 3] =
|
||||
[ExternalNetworkId::Bitcoin, ExternalNetworkId::Ethereum, ExternalNetworkId::Monero];
|
||||
|
||||
pub const NETWORKS: [NetworkId; 4] = [
|
||||
NetworkId::Serai,
|
||||
NetworkId::External(ExternalNetworkId::Bitcoin),
|
||||
NetworkId::External(ExternalNetworkId::Ethereum),
|
||||
NetworkId::External(ExternalNetworkId::Monero),
|
||||
];
|
||||
|
||||
pub const EXTERNAL_COINS: [ExternalCoin; 4] =
|
||||
[ExternalCoin::Bitcoin, ExternalCoin::Ether, ExternalCoin::Dai, ExternalCoin::Monero];
|
||||
|
||||
pub const COINS: [Coin; 5] = [
|
||||
Coin::Serai,
|
||||
Coin::External(ExternalCoin::Bitcoin),
|
||||
Coin::External(ExternalCoin::Ether),
|
||||
Coin::External(ExternalCoin::Dai),
|
||||
Coin::External(ExternalCoin::Monero),
|
||||
];
|
||||
|
||||
/// The type used to identify external coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ExternalCoin {
|
||||
Bitcoin,
|
||||
Ether,
|
||||
Dai,
|
||||
Monero,
|
||||
}
|
||||
|
||||
impl Encode for ExternalCoin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => vec![4],
|
||||
ExternalCoin::Ether => vec![5],
|
||||
ExternalCoin::Dai => vec![6],
|
||||
ExternalCoin::Monero => vec![7],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for ExternalCoin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
4 => Ok(Self::Bitcoin),
|
||||
5 => Ok(Self::Ether),
|
||||
6 => Ok(Self::Dai),
|
||||
7 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MaxEncodedLen for ExternalCoin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for ExternalCoin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalCoin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalCoin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalCoin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Coin {
|
||||
Serai,
|
||||
External(ExternalCoin),
|
||||
}
|
||||
|
||||
impl Encode for Coin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Coin::Serai => vec![0],
|
||||
Coin::External(ec) => ec.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for Coin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalCoin::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for Coin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for Coin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for Coin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for Coin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
Coin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalCoin> for Coin {
|
||||
fn from(coin: ExternalCoin) -> Self {
|
||||
Coin::External(coin)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for ExternalCoin {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(coin: Coin) -> Result<Self, Self::Error> {
|
||||
match coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(c) => Ok(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCoin {
|
||||
pub fn network(&self) -> ExternalNetworkId {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => ExternalNetworkId::Bitcoin,
|
||||
ExternalCoin::Ether | ExternalCoin::Dai => ExternalNetworkId::Ethereum,
|
||||
ExternalCoin::Monero => ExternalNetworkId::Monero,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "Bitcoin",
|
||||
ExternalCoin::Ether => "Ether",
|
||||
ExternalCoin::Dai => "Dai Stablecoin",
|
||||
ExternalCoin::Monero => "Monero",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "BTC",
|
||||
ExternalCoin::Ether => "ETH",
|
||||
ExternalCoin::Dai => "DAI",
|
||||
ExternalCoin::Monero => "XMR",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
ExternalCoin::Bitcoin | ExternalCoin::Ether | ExternalCoin::Dai => 8,
|
||||
ExternalCoin::Monero => 12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
pub fn native() -> Coin {
|
||||
Coin::Serai
|
||||
}
|
||||
|
||||
pub fn network(&self) -> NetworkId {
|
||||
match self {
|
||||
Coin::Serai => NetworkId::Serai,
|
||||
Coin::External(c) => c.network().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "Serai",
|
||||
Coin::External(c) => c.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "SRI",
|
||||
Coin::External(c) => c.symbol(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
Coin::Serai => 8,
|
||||
Coin::External(c) => c.decimals(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_native(&self) -> bool {
|
||||
matches!(self, Coin::Serai)
|
||||
}
|
||||
}
|
||||
|
||||
// Max of 8 coins per network
|
||||
// Since Serai isn't interested in listing tokens, as on-chain DEXs will almost certainly have
|
||||
// more liquidity, the only reason we'd have so many coins from a network is if there's no DEX
|
||||
// on-chain
|
||||
// There's probably no chain with so many *worthwhile* coins and no on-chain DEX
|
||||
// This could probably be just 4, yet 8 is a hedge for the unforeseen
|
||||
// If necessary, this can be increased with a fork
|
||||
pub const MAX_COINS_PER_NETWORK: u32 = 8;
|
||||
|
||||
/// Network definition.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Network {
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
borsh(
|
||||
serialize_with = "borsh_serialize_bounded_vec",
|
||||
deserialize_with = "borsh_deserialize_bounded_vec"
|
||||
)
|
||||
)]
|
||||
coins: BoundedVec<Coin, ConstU32<{ MAX_COINS_PER_NETWORK }>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Zeroize for Network {
|
||||
fn zeroize(&mut self) {
|
||||
for coin in self.coins.as_mut() {
|
||||
coin.zeroize();
|
||||
}
|
||||
self.coins.truncate(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Network {
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new(coins: Vec<Coin>) -> Result<Network, &'static str> {
|
||||
if coins.is_empty() {
|
||||
Err("no coins provided")?;
|
||||
}
|
||||
|
||||
let network = coins[0].network();
|
||||
for coin in coins.iter().skip(1) {
|
||||
if coin.network() != network {
|
||||
Err("coins have different networks")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Network {
|
||||
coins: coins.try_into().map_err(|_| "coins length exceeds {MAX_COINS_PER_NETWORK}")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn coins(&self) -> &[Coin] {
|
||||
&self.coins
|
||||
}
|
||||
}
|
16
substrate/primitives/src/signals.rs
Normal file
16
substrate/primitives/src/signals.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use zeroize::Zeroize;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::network_id::ExternalNetworkId;
|
||||
|
||||
/// A signal.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Signal {
|
||||
/// A signal to retire the current protocol.
|
||||
Retire {
|
||||
/// The protocol to retire in favor of.
|
||||
in_favor_of: [u8; 32],
|
||||
},
|
||||
/// A signal to halt an external network.
|
||||
Halt(ExternalNetworkId),
|
||||
}
|
37
substrate/primitives/src/sp_borsh.rs
Normal file
37
substrate/primitives/src/sp_borsh.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use borsh::{io::*, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
|
||||
// TODO: Don't serialize this as a Vec<u8>. Shorten the length-prefix, technically encoding as an
|
||||
// enum.
|
||||
pub fn borsh_serialize_bitvec<W: Write>(
|
||||
bitvec: &bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
writer: &mut W,
|
||||
) -> Result<()> {
|
||||
let vec: &[u8] = bitvec.as_raw_slice();
|
||||
BorshSerialize::serialize(vec, writer)
|
||||
}
|
||||
|
||||
pub fn borsh_deserialize_bitvec<R: Read>(
|
||||
reader: &mut R,
|
||||
) -> Result<bitvec::vec::BitVec<u8, bitvec::order::Lsb0>> {
|
||||
let bitvec: alloc::vec::Vec<u8> = BorshDeserialize::deserialize_reader(reader)?;
|
||||
Ok(bitvec::vec::BitVec::from_vec(bitvec))
|
||||
}
|
||||
|
||||
type SerializeBoundedVecAs<T> = alloc::vec::Vec<T>;
|
||||
|
||||
pub fn borsh_serialize_bounded_vec<W: Write, T: BorshSerialize, const B: u32>(
|
||||
bounded: &BoundedVec<T, ConstU32<B>>,
|
||||
writer: &mut W,
|
||||
) -> Result<()> {
|
||||
let vec: &SerializeBoundedVecAs<T> = bounded.as_ref();
|
||||
BorshSerialize::serialize(vec, writer)
|
||||
}
|
||||
|
||||
pub fn borsh_deserialize_bounded_vec<R: Read, T: BorshDeserialize, const B: u32>(
|
||||
reader: &mut R,
|
||||
) -> Result<BoundedVec<T, ConstU32<B>>> {
|
||||
let vec: SerializeBoundedVecAs<T> = BorshDeserialize::deserialize_reader(reader)?;
|
||||
vec.try_into().map_err(|_| Error::new(ErrorKind::Other, "bound exceeded"))
|
||||
}
|
76
substrate/primitives/src/validator_sets/mod.rs
Normal file
76
substrate/primitives/src/validator_sets/mod.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
use zeroize::Zeroize;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
|
||||
use crate::{
|
||||
crypto::{Public, KeyPair},
|
||||
network_id::{ExternalNetworkId, NetworkId},
|
||||
};
|
||||
|
||||
mod slashes;
|
||||
pub use slashes::*;
|
||||
|
||||
/// The type used to identify a specific session of validators.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Session(pub u32);
|
||||
|
||||
/// The type used to identify a specific set of validators for an external network.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ExternalValidatorSet {
|
||||
/// The network this set of validators are for.
|
||||
pub network: ExternalNetworkId,
|
||||
/// Which session this set of validators is occuring during.
|
||||
pub session: Session,
|
||||
}
|
||||
|
||||
/// The type used to identify a specific set of validators.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ValidatorSet {
|
||||
/// The network this set of validators are for.
|
||||
pub network: NetworkId,
|
||||
/// Which session this set of validators is occuring during.
|
||||
pub session: Session,
|
||||
}
|
||||
|
||||
impl From<ExternalValidatorSet> for ValidatorSet {
|
||||
fn from(set: ExternalValidatorSet) -> Self {
|
||||
ValidatorSet { network: set.network.into(), session: set.session }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ValidatorSet> for ExternalValidatorSet {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(set: ValidatorSet) -> Result<Self, Self::Error> {
|
||||
set.network.try_into().map(|network| ExternalValidatorSet { network, session: set.session })
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalValidatorSet {
|
||||
/// The MuSig context for this validator set.
|
||||
pub fn musig_context(&self) -> Vec<u8> {
|
||||
borsh::to_vec(&(b"ValidatorSets-musig_key".as_ref(), self)).unwrap()
|
||||
}
|
||||
|
||||
/// The MuSig public key for a validator set.
|
||||
///
|
||||
/// This function panics on invalid input, per the definition of `dkg::musig::musig_key`.
|
||||
pub fn musig_key(&self, set_keys: &[Public]) -> Public {
|
||||
let mut keys = Vec::new();
|
||||
for key in set_keys {
|
||||
keys.push(
|
||||
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut key.0.as_ref())
|
||||
.expect("invalid participant"),
|
||||
);
|
||||
}
|
||||
Public(dkg::musig::musig_key::<Ristretto>(&self.musig_context(), &keys).unwrap().to_bytes())
|
||||
}
|
||||
|
||||
/// The message for the `set_keys` signature.
|
||||
pub fn set_keys_message(&self, key_pair: &KeyPair) -> Vec<u8> {
|
||||
borsh::to_vec(&(b"ValidatorSets-set_keys", self, key_pair)).unwrap()
|
||||
}
|
||||
}
|
|
@ -1,35 +1,25 @@
|
|||
use core::{num::NonZero, time::Duration};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use serai_primitives::{TARGET_BLOCK_TIME, Amount};
|
||||
|
||||
use crate::{SESSION_LENGTH, MAX_KEY_SHARES_PER_SET_U32};
|
||||
use crate::{
|
||||
constants::{TARGET_BLOCK_TIME, SESSION_LENGTH, MAX_KEY_SHARES_PER_SET_U32},
|
||||
balance::Amount,
|
||||
};
|
||||
|
||||
/// Each slash point is equivalent to the downtime implied by missing a block proposal.
|
||||
// Takes a NonZero<u16> so that the result is never 0.
|
||||
// Takes a NonZero<u16> so that the result is never 0, making this safe to divide by.
|
||||
fn downtime_per_slash_point(validators: NonZero<u16>) -> Duration {
|
||||
Duration::from_secs(TARGET_BLOCK_TIME) * u32::from(u16::from(validators))
|
||||
TARGET_BLOCK_TIME * u32::from(u16::from(validators))
|
||||
}
|
||||
|
||||
/// A slash for a validator.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Slash {
|
||||
/// The slash points accumulated by this validator.
|
||||
///
|
||||
|
@ -46,7 +36,7 @@ pub enum Slash {
|
|||
impl Slash {
|
||||
/// Calculate the penalty which should be applied to the validator.
|
||||
///
|
||||
/// Does not panic, even due to overflows, if `allocated_stake + session_rewards <= u64::MAX`.
|
||||
/// Does not panic, even when compiled with checked arithmetic.
|
||||
pub fn penalty(
|
||||
self,
|
||||
validators: NonZero<u16>,
|
||||
|
@ -206,31 +196,34 @@ impl Slash {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct SlashReport(pub BoundedVec<Slash, ConstU32<{ MAX_KEY_SHARES_PER_SET_U32 }>>);
|
||||
/// A report of all slashes incurred for a `ValidatorSet`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct SlashReport(
|
||||
#[borsh(
|
||||
serialize_with = "crate::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "crate::borsh_deserialize_bounded_vec"
|
||||
)]
|
||||
pub BoundedVec<Slash, ConstU32<{ MAX_KEY_SHARES_PER_SET_U32 }>>,
|
||||
);
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for SlashReport {
|
||||
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
|
||||
BorshSerialize::serialize(self.0.as_slice(), writer)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for SlashReport {
|
||||
fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
|
||||
let slashes = Vec::<Slash>::deserialize_reader(reader)?;
|
||||
slashes
|
||||
.try_into()
|
||||
.map(Self)
|
||||
.map_err(|_| borsh::io::Error::other("length of slash report exceeds max validators"))
|
||||
}
|
||||
/// An error when converting from a `Vec`.
|
||||
pub enum FromVecError {
|
||||
/// The source `Vec` was too long to be converted.
|
||||
TooLong,
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<Slash>> for SlashReport {
|
||||
type Error = &'static str;
|
||||
fn try_from(slashes: Vec<Slash>) -> Result<SlashReport, &'static str> {
|
||||
slashes.try_into().map(Self).map_err(|_| "length of slash report exceeds max validators")
|
||||
type Error = FromVecError;
|
||||
fn try_from(slashes: Vec<Slash>) -> Result<SlashReport, FromVecError> {
|
||||
slashes.try_into().map(Self).map_err(|_| FromVecError::TooLong)
|
||||
}
|
||||
}
|
||||
|
||||
impl zeroize::Zeroize for SlashReport {
|
||||
fn zeroize(&mut self) {
|
||||
for slash in self.0.as_mut() {
|
||||
slash.zeroize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,13 +231,18 @@ impl SlashReport {
|
|||
/// The message to sign when publishing this SlashReport.
|
||||
// This is assumed binding to the ValidatorSet via the key signed with
|
||||
pub fn report_slashes_message(&self) -> Vec<u8> {
|
||||
(b"ValidatorSets-report_slashes", &self.0).encode()
|
||||
const DST: &[u8] = b"ValidatorSets-report_slashes";
|
||||
let mut buf = Vec::with_capacity(
|
||||
DST.len() + core::mem::size_of::<u32>() + (self.0.len() * core::mem::size_of::<Slash>()),
|
||||
);
|
||||
(DST, self).serialize(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_penalty() {
|
||||
for validators in [1, 50, 100, crate::MAX_KEY_SHARES_PER_SET] {
|
||||
for validators in [1, 50, 100, crate::constants::MAX_KEY_SHARES_PER_SET] {
|
||||
let validators = NonZero::new(validators).unwrap();
|
||||
// 12 hours of slash points should only decrease the rewards proportionately
|
||||
let twelve_hours_of_slash_points =
|
||||
|
@ -316,11 +314,13 @@ fn test_penalty() {
|
|||
|
||||
#[test]
|
||||
fn no_overflow() {
|
||||
// Test with u16::MAX for validators, maximizing the downtime each slash point represents
|
||||
Slash::Points(u32::MAX).penalty(
|
||||
NonZero::new(u16::MAX).unwrap(),
|
||||
Amount(u64::MAX),
|
||||
Amount(u64::MAX),
|
||||
);
|
||||
|
||||
// Test with 1 for validators, in case validators is inversely correlated
|
||||
Slash::Points(u32::MAX).penalty(NonZero::new(1).unwrap(), Amount(u64::MAX), Amount(u64::MAX));
|
||||
}
|
|
@ -49,7 +49,7 @@ frame-executive = { git = "https://github.com/serai-dex/polkadot-sdk", branch =
|
|||
frame-benchmarking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, optional = true }
|
||||
|
||||
serai-primitives = { path = "../primitives", default-features = false }
|
||||
serai-abi = { path = "../abi", default-features = false, features = ["serde"] }
|
||||
serai-abi = { path = "../abi", default-features = false }
|
||||
|
||||
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
pallet-authorship = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
@ -108,7 +108,6 @@ std = [
|
|||
|
||||
"serai-primitives/std",
|
||||
"serai-abi/std",
|
||||
"serai-abi/serde",
|
||||
|
||||
"pallet-timestamp/std",
|
||||
"pallet-authorship/std",
|
||||
|
|
|
@ -1,17 +1 @@
|
|||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(zeroize::Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum SignalId {
|
||||
Retirement([u8; 32]),
|
||||
Halt(ExternalNetworkId),
|
||||
}
|
||||
|
|
|
@ -1,171 +1 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec, sr25519::Public};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use serai_primitives::{ExternalNetworkId, NetworkId};
|
||||
|
||||
mod slash_points;
|
||||
pub use slash_points::*;
|
||||
|
||||
/// The expected duration for a session.
|
||||
// 1 week
|
||||
pub const SESSION_LENGTH: Duration = Duration::from_secs(7 * 24 * 60 * 60);
|
||||
|
||||
/// The maximum length for a key.
|
||||
// Support keys up to 96 bytes (BLS12-381 G2).
|
||||
pub const MAX_KEY_LEN: u32 = 96;
|
||||
|
||||
/// The maximum amount of key shares per set.
|
||||
pub const MAX_KEY_SHARES_PER_SET: u16 = 150;
|
||||
pub const MAX_KEY_SHARES_PER_SET_U32: u32 = MAX_KEY_SHARES_PER_SET as u32;
|
||||
|
||||
/// The type used to identify a specific session of validators.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
|
||||
)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Session(pub u32);
|
||||
|
||||
/// The type used to identify a specific validator set during a specific session.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
|
||||
)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ValidatorSet {
|
||||
pub session: Session,
|
||||
pub network: NetworkId,
|
||||
}
|
||||
|
||||
/// The type used to identify a specific validator set during a specific session.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalValidatorSet {
|
||||
pub session: Session,
|
||||
pub network: ExternalNetworkId,
|
||||
}
|
||||
|
||||
impl From<ExternalValidatorSet> for ValidatorSet {
|
||||
fn from(set: ExternalValidatorSet) -> Self {
|
||||
ValidatorSet { session: set.session, network: set.network.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ValidatorSet> for ExternalValidatorSet {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(set: ValidatorSet) -> Result<Self, Self::Error> {
|
||||
match set.network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(network) => Ok(ExternalValidatorSet { session: set.session, network }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type representing a Key from an external network.
|
||||
pub type ExternalKey = BoundedVec<u8, ConstU32<MAX_KEY_LEN>>;
|
||||
|
||||
/// The key pair for a validator set.
|
||||
///
|
||||
/// This is their Ristretto key, used for publishing data onto Serai, and their key on the external
|
||||
/// network.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct KeyPair(
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
borsh(
|
||||
serialize_with = "serai_primitives::borsh_serialize_public",
|
||||
deserialize_with = "serai_primitives::borsh_deserialize_public"
|
||||
)
|
||||
)]
|
||||
pub Public,
|
||||
#[cfg_attr(
|
||||
feature = "borsh",
|
||||
borsh(
|
||||
serialize_with = "serai_primitives::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "serai_primitives::borsh_deserialize_bounded_vec"
|
||||
)
|
||||
)]
|
||||
pub ExternalKey,
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
impl Zeroize for KeyPair {
|
||||
fn zeroize(&mut self) {
|
||||
self.0 .0.zeroize();
|
||||
self.1.as_mut().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
/// The MuSig context for a validator set.
|
||||
pub fn musig_context(set: ValidatorSet) -> Vec<u8> {
|
||||
(b"ValidatorSets-musig_key".as_ref(), set).encode()
|
||||
}
|
||||
|
||||
/// The MuSig public key for a validator set.
|
||||
///
|
||||
/// This function panics on invalid input, per the definition of `dkg::musig::musig_key`.
|
||||
pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
|
||||
let mut keys = Vec::new();
|
||||
for key in set_keys {
|
||||
keys.push(
|
||||
<Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut key.0.as_ref())
|
||||
.expect("invalid participant"),
|
||||
);
|
||||
}
|
||||
Public(dkg::musig::musig_key::<Ristretto>(&musig_context(set), &keys).unwrap().to_bytes())
|
||||
}
|
||||
|
||||
/// The message for the `set_keys` signature.
|
||||
pub fn set_keys_message(set: &ExternalValidatorSet, key_pair: &KeyPair) -> Vec<u8> {
|
||||
(b"ValidatorSets-set_keys", set, key_pair).encode()
|
||||
}
|
||||
|
||||
/// For a set of validators whose key shares may exceed the maximum, reduce until they equal the
|
||||
/// maximum.
|
||||
///
|
||||
/// Reduction occurs by reducing each validator in a reverse round-robin.
|
||||
pub fn amortize_excess_key_shares(validators: &mut [(Public, u64)]) {
|
||||
let total_key_shares = validators.iter().map(|(_, shares)| shares).sum::<u64>();
|
||||
for i in 0 .. usize::try_from(total_key_shares.saturating_sub(u64::from(MAX_KEY_SHARES_PER_SET)))
|
||||
.unwrap()
|
||||
{
|
||||
validators[validators.len() - ((i % validators.len()) + 1)].1 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the post-amortization key shares for the top validator.
|
||||
///
|
||||
/// Panics when `validators == 0`.
|
||||
pub fn post_amortization_key_shares_for_top_validator(
|
||||
validators: usize,
|
||||
top: u64,
|
||||
key_shares: u64,
|
||||
) -> u64 {
|
||||
top -
|
||||
(key_shares.saturating_sub(MAX_KEY_SHARES_PER_SET.into()) /
|
||||
u64::try_from(validators).unwrap())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue