mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Initial In Instructions pallet and Serai client lib (#233)
* Initial work on an In Inherents pallet * Add an event for when a batch is executed * Add a dummy provider for InInstructions * Add in-instructions to the node * Add the Serai runtime API to the processor * Move processor tests around * Build a subxt Client around Serai * Successfully get Batch events from Serai Renamed processor/substrate to processor/serai. * Much more robust InInstruction pallet * Implement the workaround from https://github.com/paritytech/subxt/issues/602 * Initial prototype of processor generated InInstructions * Correct PendingCoins data flow for InInstructions * Minor lint to in-instructions * Remove the global Serai connection for a partial re-impl * Correct ID handling of the processor test * Workaround the delay in the subscription * Make an unwrap an if let Some, remove old comments * Lint the processor toml * Rebase and update * Move substrate/in-instructions to substrate/in-instructions/pallet * Start an in-instructions primitives lib * Properly update processor to subxt 0.24 Also corrects failures from the rebase. * in-instructions cargo update * Implement IsFatalError * is_inherent -> true * Rename in-instructions crates and misc cleanup * Update documentation * cargo update * Misc update fixes * Replace height with block_number * Update processor src to latest subxt * Correct pipeline for InInstructions testing * Remove runtime::AccountId for serai_primitives::NativeAddress * Rewrite the in-instructions pallet Complete with respect to the currently written docs. Drops the custom serializer for just using SCALE. Makes slight tweaks as relevant. * Move instructions' InherentDataProvider to a client crate * Correct doc gen * Add serde to in-instructions-primitives * Add in-instructions-primitives to pallet * Heights -> BlockNumbers * Get batch pub test loop working * Update in instructions pallet terminology Removes the ambiguous Coin for Update. Removes pending/artificial latency for furture client work. Also moves to using serai_primitives::Coin. * Add a BlockNumber primitive * Belated cargo fmt * Further document why DifferentBatch isn't fatal * Correct processor sleeps * Remove metadata at compile time, add test framework for Serai nodes * Remove manual RPC client * Simplify update test * Improve re-exporting behavior of serai-runtime It now re-exports all pallets underneath it. * Add a function to get storage values to the Serai RPC * Update substrate/ to latest substrate * Create a dedicated crate for the Serai RPC * Remove unused dependencies in substrate/ * Remove unused dependencies in coins/ Out of scope for this branch, just minor and path of least resistance. * Use substrate/serai/client for the Serai RPC lib It's a bit out of place, since these client folders are intended for the node to access pallets and so on. This is for end-users to access Serai as a whole. In that sense, it made more sense as a top level folder, yet that also felt out of place. * Move InInstructions test to serai-client for now * Final cleanup * Update deny.toml * Cargo.lock update from merging develop * Update nightly Attempt to work around the current CI failure, which is a Rust ICE. We previously didn't upgrade due to clippy 10134, yet that's been reverted. * clippy * clippy * fmt * NativeAddress -> SeraiAddress * Sec fix on non-provided updates and doc fixes * Add Serai as a Coin Necessary in order to swap to Serai. * Add a BlockHash type, used for batch IDs * Remove origin from InInstruction Makes InInstructionTarget. Adds RefundableInInstruction with origin. * Document storage items in in-instructions * Rename serai/client/tests/serai.rs to updates.rs It only tested publishing updates and their successful acceptance.
This commit is contained in:
15
.github/actions/test-dependencies/action.yml
vendored
15
.github/actions/test-dependencies/action.yml
vendored
@@ -12,6 +12,11 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: v0.18.0.0
|
default: v0.18.0.0
|
||||||
|
|
||||||
|
serai:
|
||||||
|
description: "Run a Serai development node in the background"
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
@@ -32,3 +37,13 @@ runs:
|
|||||||
|
|
||||||
- name: Run a Monero Wallet-RPC
|
- name: Run a Monero Wallet-RPC
|
||||||
uses: ./.github/actions/monero-wallet-rpc
|
uses: ./.github/actions/monero-wallet-rpc
|
||||||
|
|
||||||
|
- name: Run a Serai Development Node
|
||||||
|
if: ${{ inputs.serai }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd substrate/node
|
||||||
|
cargo build
|
||||||
|
cd ../..
|
||||||
|
|
||||||
|
./target/debug/serai-node --dev &
|
||||||
|
|||||||
2
.github/nightly-version
vendored
2
.github/nightly-version
vendored
@@ -1 +1 @@
|
|||||||
nightly-2022-12-01
|
nightly-2023-01-16
|
||||||
|
|||||||
5
.github/workflows/tests.yml
vendored
5
.github/workflows/tests.yml
vendored
@@ -58,6 +58,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build node
|
||||||
|
run: |
|
||||||
|
cd substrate/node
|
||||||
|
cargo build
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: cargo test --all-features
|
run: cargo test --all-features
|
||||||
|
|
||||||
|
|||||||
254
Cargo.lock
generated
254
Cargo.lock
generated
@@ -1698,6 +1698,17 @@ dependencies = [
|
|||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derivative"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_builder"
|
name = "derive_builder"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@@ -2170,7 +2181,6 @@ dependencies = [
|
|||||||
"ethers-solc",
|
"ethers-solc",
|
||||||
"eyre",
|
"eyre",
|
||||||
"group",
|
"group",
|
||||||
"hex-literal",
|
|
||||||
"k256",
|
"k256",
|
||||||
"modular-frost",
|
"modular-frost",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
@@ -3414,6 +3424,7 @@ dependencies = [
|
|||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3547,6 +3558,46 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "in-instructions-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"in-instructions-pallet",
|
||||||
|
"jsonrpsee-core",
|
||||||
|
"jsonrpsee-http-client",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"sp-inherents",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "in-instructions-pallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"frame-support",
|
||||||
|
"frame-system",
|
||||||
|
"in-instructions-primitives",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"serai-primitives",
|
||||||
|
"serde",
|
||||||
|
"sp-inherents",
|
||||||
|
"sp-runtime",
|
||||||
|
"sp-std",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "in-instructions-primitives"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"serai-primitives",
|
||||||
|
"serde",
|
||||||
|
"sp-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indenter"
|
name = "indenter"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -3731,13 +3782,36 @@ version = "0.16.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e"
|
checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"jsonrpsee-client-transport",
|
||||||
"jsonrpsee-core",
|
"jsonrpsee-core",
|
||||||
|
"jsonrpsee-http-client",
|
||||||
"jsonrpsee-proc-macros",
|
"jsonrpsee-proc-macros",
|
||||||
"jsonrpsee-server",
|
"jsonrpsee-server",
|
||||||
"jsonrpsee-types",
|
"jsonrpsee-types",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonrpsee-client-transport"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "965de52763f2004bc91ac5bcec504192440f0b568a5d621c59d9dbd6f886c3fb"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"jsonrpsee-core",
|
||||||
|
"jsonrpsee-types",
|
||||||
|
"pin-project",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"soketto",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpsee-core"
|
name = "jsonrpsee-core"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
@@ -3746,9 +3820,11 @@ checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrayvec 0.7.2",
|
"arrayvec 0.7.2",
|
||||||
|
"async-lock",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"beef",
|
"beef",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
"futures-timer",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"globset",
|
"globset",
|
||||||
"hyper",
|
"hyper",
|
||||||
@@ -3764,6 +3840,25 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonrpsee-http-client"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc345b0a43c6bc49b947ebeb936e886a419ee3d894421790c969cc56040542ad"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
|
"jsonrpsee-core",
|
||||||
|
"jsonrpsee-types",
|
||||||
|
"rustc-hash",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsonrpsee-proc-macros"
|
name = "jsonrpsee-proc-macros"
|
||||||
version = "0.16.2"
|
version = "0.16.2"
|
||||||
@@ -4832,7 +4927,6 @@ name = "monero-serai"
|
|||||||
version = "0.1.2-alpha"
|
version = "0.1.2-alpha"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base58-monero",
|
"base58-monero",
|
||||||
"blake2",
|
|
||||||
"curve25519-dalek 3.2.0",
|
"curve25519-dalek 3.2.0",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"digest_auth",
|
"digest_auth",
|
||||||
@@ -7477,12 +7571,10 @@ dependencies = [
|
|||||||
"sc-block-builder",
|
"sc-block-builder",
|
||||||
"sc-client-api",
|
"sc-client-api",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
"sc-executor",
|
|
||||||
"sc-network",
|
"sc-network",
|
||||||
"sc-network-common",
|
"sc-network-common",
|
||||||
"sc-network-gossip",
|
"sc-network-gossip",
|
||||||
"sc-service",
|
"sc-service",
|
||||||
"sc-transaction-pool",
|
|
||||||
"sp-api",
|
"sp-api",
|
||||||
"sp-application-crypto",
|
"sp-application-crypto",
|
||||||
"sp-blockchain",
|
"sp-blockchain",
|
||||||
@@ -7594,6 +7686,29 @@ dependencies = [
|
|||||||
"prometheus",
|
"prometheus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scale-bits"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf"
|
||||||
|
dependencies = [
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scale-decode"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233"
|
||||||
|
dependencies = [
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-bits",
|
||||||
|
"scale-info",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scale-info"
|
name = "scale-info"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@@ -7620,6 +7735,23 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scale-value"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"frame-metadata",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-bits",
|
||||||
|
"scale-decode",
|
||||||
|
"scale-info",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"yap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
@@ -7823,6 +7955,23 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
|
checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serai-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"in-instructions-primitives",
|
||||||
|
"jsonrpsee-server",
|
||||||
|
"lazy_static",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-value",
|
||||||
|
"serai-primitives",
|
||||||
|
"serai-runtime",
|
||||||
|
"serde",
|
||||||
|
"subxt",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serai-node"
|
name = "serai-node"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -7831,12 +7980,8 @@ dependencies = [
|
|||||||
"clap 4.1.1",
|
"clap 4.1.1",
|
||||||
"frame-benchmarking",
|
"frame-benchmarking",
|
||||||
"frame-benchmarking-cli",
|
"frame-benchmarking-cli",
|
||||||
"frame-system",
|
"in-instructions-client",
|
||||||
"futures",
|
|
||||||
"jsonrpsee",
|
"jsonrpsee",
|
||||||
"log",
|
|
||||||
"pallet-tendermint",
|
|
||||||
"pallet-transaction-payment",
|
|
||||||
"pallet-transaction-payment-rpc",
|
"pallet-transaction-payment-rpc",
|
||||||
"sc-basic-authorship",
|
"sc-basic-authorship",
|
||||||
"sc-cli",
|
"sc-cli",
|
||||||
@@ -7844,31 +7989,24 @@ dependencies = [
|
|||||||
"sc-client-db",
|
"sc-client-db",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
"sc-executor",
|
"sc-executor",
|
||||||
"sc-keystore",
|
|
||||||
"sc-network",
|
"sc-network",
|
||||||
"sc-rpc",
|
|
||||||
"sc-rpc-api",
|
"sc-rpc-api",
|
||||||
"sc-service",
|
"sc-service",
|
||||||
"sc-telemetry",
|
"sc-telemetry",
|
||||||
"sc-tendermint",
|
"sc-tendermint",
|
||||||
"sc-transaction-pool",
|
"sc-transaction-pool",
|
||||||
"sc-transaction-pool-api",
|
"sc-transaction-pool-api",
|
||||||
"serai-primitives",
|
|
||||||
"serai-runtime",
|
"serai-runtime",
|
||||||
"sp-api",
|
"sp-api",
|
||||||
"sp-application-crypto",
|
|
||||||
"sp-block-builder",
|
"sp-block-builder",
|
||||||
"sp-blockchain",
|
"sp-blockchain",
|
||||||
"sp-consensus",
|
"sp-consensus",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-inherents",
|
"sp-inherents",
|
||||||
"sp-keyring",
|
"sp-keyring",
|
||||||
"sp-keystore",
|
|
||||||
"sp-runtime",
|
"sp-runtime",
|
||||||
"sp-tendermint",
|
|
||||||
"substrate-build-script-utils",
|
"substrate-build-script-utils",
|
||||||
"substrate-frame-rpc-system",
|
"substrate-frame-rpc-system",
|
||||||
"validator-sets-pallet",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7879,7 +8017,6 @@ dependencies = [
|
|||||||
"scale-info",
|
"scale-info",
|
||||||
"serde",
|
"serde",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-std",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7913,6 +8050,7 @@ dependencies = [
|
|||||||
"frame-system",
|
"frame-system",
|
||||||
"frame-system-rpc-runtime-api",
|
"frame-system-rpc-runtime-api",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
"in-instructions-pallet",
|
||||||
"pallet-assets",
|
"pallet-assets",
|
||||||
"pallet-balances",
|
"pallet-balances",
|
||||||
"pallet-session",
|
"pallet-session",
|
||||||
@@ -7923,7 +8061,6 @@ dependencies = [
|
|||||||
"scale-info",
|
"scale-info",
|
||||||
"serai-primitives",
|
"serai-primitives",
|
||||||
"sp-api",
|
"sp-api",
|
||||||
"sp-application-crypto",
|
|
||||||
"sp-block-builder",
|
"sp-block-builder",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-inherents",
|
"sp-inherents",
|
||||||
@@ -9016,6 +9153,79 @@ version = "2.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subxt"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3cbc78fd36035a24883eada29e0205b9b1416172530a7d00a60c07d0337db0c"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec 1.0.1",
|
||||||
|
"derivative",
|
||||||
|
"frame-metadata",
|
||||||
|
"futures",
|
||||||
|
"getrandom 0.2.8",
|
||||||
|
"hex",
|
||||||
|
"jsonrpsee",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"scale-decode",
|
||||||
|
"scale-info",
|
||||||
|
"scale-value",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sp-core",
|
||||||
|
"sp-runtime",
|
||||||
|
"subxt-macro",
|
||||||
|
"subxt-metadata",
|
||||||
|
"thiserror",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subxt-codegen"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7722c31febf55eb300c73d977da5d65cfd6fb443419b1185b9abcdd9925fd7be"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"frame-metadata",
|
||||||
|
"heck 0.4.0",
|
||||||
|
"hex",
|
||||||
|
"jsonrpsee",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"scale-info",
|
||||||
|
"subxt-metadata",
|
||||||
|
"syn",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subxt-macro"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f64826f2c4ba20e3b2a86ec81a6ae8655ca6b6a4c2a6ccc888b6615efc2df14"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro-error",
|
||||||
|
"subxt-codegen",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subxt-metadata"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "869af75e23513538ad0af046af4a97b8d684e8d202e35ff4127ee061c1110813"
|
||||||
|
dependencies = [
|
||||||
|
"frame-metadata",
|
||||||
|
"parity-scale-codec",
|
||||||
|
"scale-info",
|
||||||
|
"sp-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "svm-rs"
|
name = "svm-rs"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -9798,8 +10008,6 @@ dependencies = [
|
|||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"scale-info",
|
"scale-info",
|
||||||
"serde",
|
"serde",
|
||||||
"sp-core",
|
|
||||||
"sp-std",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -10789,6 +10997,12 @@ version = "0.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yap"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yasna"
|
name = "yasna"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|||||||
12
Cargo.toml
12
Cargo.toml
@@ -23,6 +23,11 @@ members = [
|
|||||||
"processor",
|
"processor",
|
||||||
|
|
||||||
"substrate/serai/primitives",
|
"substrate/serai/primitives",
|
||||||
|
"substrate/serai/client",
|
||||||
|
|
||||||
|
"substrate/in-instructions/primitives",
|
||||||
|
"substrate/in-instructions/pallet",
|
||||||
|
"substrate/in-instructions/client",
|
||||||
|
|
||||||
"substrate/validator-sets/primitives",
|
"substrate/validator-sets/primitives",
|
||||||
"substrate/validator-sets/pallet",
|
"substrate/validator-sets/pallet",
|
||||||
@@ -37,7 +42,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Always compile Monero (and a variety of dependencies) with optimizations due
|
# Always compile Monero (and a variety of dependencies) with optimizations due
|
||||||
# to the unoptimized performance of Bulletproofs
|
# to the extensive operations required for Bulletproofs
|
||||||
[profile.dev.package]
|
[profile.dev.package]
|
||||||
subtle = { opt-level = 3 }
|
subtle = { opt-level = 3 }
|
||||||
curve25519-dalek = { opt-level = 3 }
|
curve25519-dalek = { opt-level = 3 }
|
||||||
@@ -55,3 +60,8 @@ monero-serai = { opt-level = 3 }
|
|||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "unwind"
|
panic = "unwind"
|
||||||
|
|
||||||
|
# Required for subxt
|
||||||
|
[patch.crates-io]
|
||||||
|
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex-literal = "0.3"
|
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,10 @@ zeroize = { version = "1.5", features = ["zeroize_derive"] }
|
|||||||
subtle = "2.4"
|
subtle = "2.4"
|
||||||
|
|
||||||
sha3 = "0.10"
|
sha3 = "0.10"
|
||||||
blake2 = { version = "0.10", optional = true }
|
|
||||||
|
|
||||||
curve25519-dalek = { version = "3", features = ["std"] }
|
curve25519-dalek = { version = "3", features = ["std"] }
|
||||||
|
|
||||||
group = { version = "0.12" }
|
group = "0.12"
|
||||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.1" }
|
dalek-ff-group = { path = "../../crypto/dalek-ff-group", version = "0.1" }
|
||||||
multiexp = { path = "../../crypto/multiexp", version = "0.2", features = ["batch"] }
|
multiexp = { path = "../../crypto/multiexp", version = "0.2", features = ["batch"] }
|
||||||
|
|
||||||
@@ -60,4 +59,4 @@ monero-rpc = "0.3"
|
|||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.5", features = ["ed25519", "tests"] }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.5", features = ["ed25519", "tests"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
multisig = ["rand_chacha", "blake2", "transcript", "frost", "dleq"]
|
multisig = ["rand_chacha", "transcript", "frost", "dleq"]
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ fn clsag() {
|
|||||||
Commitment::new(secrets.1, AMOUNT),
|
Commitment::new(secrets.1, AMOUNT),
|
||||||
Decoys {
|
Decoys {
|
||||||
i: u8::try_from(real).unwrap(),
|
i: u8::try_from(real).unwrap(),
|
||||||
offsets: (1 ..= RING_LEN).into_iter().collect(),
|
offsets: (1 ..= RING_LEN).collect(),
|
||||||
ring: ring.clone(),
|
ring: ring.clone(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -107,11 +107,7 @@ fn clsag_multisig() {
|
|||||||
Arc::new(RwLock::new(Some(ClsagDetails::new(
|
Arc::new(RwLock::new(Some(ClsagDetails::new(
|
||||||
ClsagInput::new(
|
ClsagInput::new(
|
||||||
Commitment::new(randomness, AMOUNT),
|
Commitment::new(randomness, AMOUNT),
|
||||||
Decoys {
|
Decoys { i: RING_INDEX, offsets: (1 ..= RING_LEN).collect(), ring: ring.clone() },
|
||||||
i: RING_INDEX,
|
|
||||||
offsets: (1 ..= RING_LEN).into_iter().collect(),
|
|
||||||
ring: ring.clone(),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
mask_sum,
|
mask_sum,
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||||||
// Find out who's included
|
// Find out who's included
|
||||||
// This may not be a valid set of signers yet the algorithm machine will error if it's not
|
// This may not be a valid set of signers yet the algorithm machine will error if it's not
|
||||||
commitments.remove(&self.i); // Remove, if it was included for some reason
|
commitments.remove(&self.i); // Remove, if it was included for some reason
|
||||||
let mut included = commitments.keys().into_iter().cloned().collect::<Vec<_>>();
|
let mut included = commitments.keys().cloned().collect::<Vec<_>>();
|
||||||
included.push(self.i);
|
included.push(self.i);
|
||||||
included.sort_unstable();
|
included.sort_unstable();
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ pub async fn mine_until_unlocked(rpc: &Rpc, addr: &str, tx_hash: [u8; 32]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mines 60 blocks and returns an unlocked miner TX output.
|
// Mines 60 blocks and returns an unlocked miner TX output.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub async fn get_miner_tx_output(rpc: &Rpc, view: &ViewPair) -> SpendableOutput {
|
pub async fn get_miner_tx_output(rpc: &Rpc, view: &ViewPair) -> SpendableOutput {
|
||||||
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
|
let mut scanner = Scanner::from_view(view.clone(), Some(HashSet::new()));
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ exceptions = [
|
|||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-processor" },
|
{ allow = ["AGPL-3.0"], name = "serai-processor" },
|
||||||
|
|
||||||
|
{ allow = ["AGPL-3.0"], name = "in-instructions-pallet" },
|
||||||
|
{ allow = ["AGPL-3.0"], name = "in-instructions-client" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "validator-sets-pallet" },
|
{ allow = ["AGPL-3.0"], name = "validator-sets-pallet" },
|
||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "sp-tendermint" },
|
{ allow = ["AGPL-3.0"], name = "sp-tendermint" },
|
||||||
@@ -56,6 +59,8 @@ exceptions = [
|
|||||||
|
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-runtime" },
|
{ allow = ["AGPL-3.0"], name = "serai-runtime" },
|
||||||
{ allow = ["AGPL-3.0"], name = "serai-node" },
|
{ allow = ["AGPL-3.0"], name = "serai-node" },
|
||||||
|
|
||||||
|
{ allow = ["AGPL-3.0"], name = "serai-client" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[licenses.clarify]]
|
[[licenses.clarify]]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Serai
|
# Serai
|
||||||
|
|
||||||
Serai is a decentralized execution layer whose validators form multisig wallets
|
Serai is a decentralized execution layer whose validators form multisig wallets
|
||||||
for various connected networks, offering secure decentralized custody of foreign
|
for various connected networks, offering secure decentralized control of foreign
|
||||||
assets to applications built on it.
|
coins to applications built on it.
|
||||||
|
|
||||||
Serai is exemplified by Serai DEX, an automated-market-maker (AMM) decentralized
|
Serai is exemplified by Serai DEX, an automated-market-maker (AMM) decentralized
|
||||||
exchange, allowing swapping Bitcoin, Ether, DAI, and Monero. It is the premier
|
exchange, allowing swapping Bitcoin, Ether, DAI, and Monero. It is the premier
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ Ethereum addresses are 20-byte hashes.
|
|||||||
Ethereum In Instructions are present via being appended to the calldata
|
Ethereum In Instructions are present via being appended to the calldata
|
||||||
transferring funds to Serai. `origin` is automatically set to the party from
|
transferring funds to Serai. `origin` is automatically set to the party from
|
||||||
which funds are being transferred. For an ERC20, this is `from`. For ETH, this
|
which funds are being transferred. For an ERC20, this is `from`. For ETH, this
|
||||||
is the caller. `data` is limited to 255 bytes.
|
is the caller.
|
||||||
|
|
||||||
### Out Instructions
|
### Out Instructions
|
||||||
|
|
||||||
`data` is limited to 255 bytes.
|
`data` is limited to 512 bytes.
|
||||||
|
|
||||||
If `data` is provided, the Ethereum Router will call a contract-calling child
|
If `data` is provided, the Ethereum Router will call a contract-calling child
|
||||||
contract in order to sandbox it. The first byte of `data` designates which child
|
contract in order to sandbox it. The first byte of `data` designates which child
|
||||||
|
|||||||
@@ -3,90 +3,94 @@
|
|||||||
Instructions are used to communicate with networks connected to Serai, and they
|
Instructions are used to communicate with networks connected to Serai, and they
|
||||||
come in two forms:
|
come in two forms:
|
||||||
|
|
||||||
- In Instructions are [Application Calls](../Serai.md#application-calls),
|
- In Instructions are programmable specifications paired with incoming coins,
|
||||||
paired with incoming funds. Encoded in transactions on connected networks,
|
encoded into transactions on connected networks. Serai will parse included
|
||||||
Serai will parse out instructions when it receives funds, executing the included
|
instructions when it receives coins, executing the included specs.
|
||||||
calls.
|
|
||||||
|
|
||||||
- Out Instructions detail how to transfer assets, either to a Serai address or
|
- Out Instructions detail how to transfer coins, either to a Serai address or
|
||||||
an address native to the asset in question.
|
an address native to the coin in question.
|
||||||
|
|
||||||
A transaction containing an In Instruction and an Out Instruction (to a native
|
A transaction containing an In Instruction and an Out Instruction (to a native
|
||||||
address) will receive funds to Serai and send funds from Serai, without
|
address) will receive coins to Serai and send coins from Serai, without
|
||||||
requiring directly performing any transactions on Serai itself.
|
requiring directly performing any transactions on Serai itself.
|
||||||
|
|
||||||
All instructions are encoded under [Shorthand](#shorthand). Shorthand provides
|
All instructions are encoded under [Shorthand](#shorthand). Shorthand provides
|
||||||
frequent use cases to create minimal data representations on connected networks.
|
frequent use cases to create minimal data representations on connected networks.
|
||||||
|
|
||||||
Instructions are interpreted according to their non-Serai network. Addresses
|
Instructions are interpreted according to their non-Serai network. Addresses
|
||||||
have no validation performed, beyond being a valid enum entry (when applicable)
|
have no validation performed unless otherwise noted. If the processor is
|
||||||
of the correct length, unless otherwise noted. If the processor is instructed to
|
instructed to act on invalid data, it will drop the entire instruction.
|
||||||
act on invalid data, or send to itself, it will drop the entire instruction.
|
|
||||||
|
|
||||||
### Serialization
|
### Serialization
|
||||||
|
|
||||||
- Numbers are exclusively unsigned and encoded as compact integers under
|
Instructions are SCALE encoded.
|
||||||
SCALE.
|
|
||||||
- Enums are prefixed by an ordinal byte of their type, followed by their
|
|
||||||
actual values.
|
|
||||||
- Vectors are prefixed by their length.
|
|
||||||
- In Instruction fields are numbered and sequentially encoded, allowing
|
|
||||||
omission, each prefixed by an ordinal byte. This is due to its fields being more
|
|
||||||
frequently omitted than not, making their presence what's notable.
|
|
||||||
- All other types have their fields sequentially encoded with no markers.
|
|
||||||
|
|
||||||
Certain fields may be omitted depending on the network in question.
|
### Application Call
|
||||||
|
|
||||||
### In Instructions
|
- `application` (u16): The application of Serai to call. Currently, only 0,
|
||||||
|
Serai DEX is valid.
|
||||||
|
- `data` (Data): The data to call the application with.
|
||||||
|
|
||||||
- `origin` (Address): Address from the network of origin which sent funds in.
|
### In Instruction
|
||||||
- `target` (Address): The ink! contract to transfer the incoming funds to.
|
|
||||||
- `data` (Vec\<u8>): The data to call `target` with.
|
InInstruction is an enum of SeraiAddress and ApplicationCall.
|
||||||
|
|
||||||
|
The specified target will be minted an appropriate amount of the respective
|
||||||
|
Serai token. If an Application Call, the encoded call will be executed.
|
||||||
|
|
||||||
|
### Refundable In Instruction
|
||||||
|
|
||||||
|
- `origin` (Option\<ExternalAddress>): Address, from the network of origin,
|
||||||
|
which sent coins in.
|
||||||
|
- `instruction` (InInstruction): The action to perform with the incoming
|
||||||
|
coins.
|
||||||
|
|
||||||
Networks may automatically provide `origin`. If they do, the instruction may
|
Networks may automatically provide `origin`. If they do, the instruction may
|
||||||
still provide `origin`, overriding the automatically provided value. If no
|
still provide `origin`, overriding the automatically provided value.
|
||||||
`origin` is provided, the instruction is dropped.
|
|
||||||
|
|
||||||
Upon receiving funds, the respective Serai Asset contract is called, minting the
|
If the instruction fails, coins are scheduled to be returned to `origin`,
|
||||||
appropriate amount of coins, and transferring them to `target`, calling it with
|
if provided.
|
||||||
the attached data.
|
|
||||||
|
|
||||||
If the instruction fails, funds are scheduled to be returned to `origin`.
|
### Destination
|
||||||
|
|
||||||
### Out Instructions
|
Destination is an enum of SeraiAddress and ExternalAddress.
|
||||||
|
|
||||||
- `destination` (Enum { Native(Address), Serai(Address) }): Address to receive
|
### Out Instruction
|
||||||
funds to.
|
|
||||||
- `data` (Option\<Vec\<u8>>): The data to call
|
|
||||||
the target with.
|
|
||||||
|
|
||||||
Transfer the funds included with this instruction to the specified address with
|
- `destination` (Destination): Address to receive coins to.
|
||||||
the specified data. Asset contracts perform no validation on native
|
- `data` (Option\<Data>): The data to call the destination with.
|
||||||
addresses/data.
|
|
||||||
|
Transfer the coins included with this instruction to the specified address with
|
||||||
|
the specified data. No validation of external addresses/data is performed
|
||||||
|
on-chain. If data is specified for a chain not supporting data, it is silently
|
||||||
|
dropped.
|
||||||
|
|
||||||
### Shorthand
|
### Shorthand
|
||||||
|
|
||||||
Shorthand is an enum which expands to an In Instruction.
|
Shorthand is an enum which expands to an Refundable In Instruction.
|
||||||
|
|
||||||
##### Raw
|
##### Raw
|
||||||
|
|
||||||
Raw Shorthand encodes a raw In Instruction with no further processing. This is
|
Raw Shorthand encodes a raw Refundable In Instruction in a Data, with no further
|
||||||
a verbose fallback option for infrequent use cases not covered by Shorthand.
|
processing. This is a verbose fallback option for infrequent use cases not
|
||||||
|
covered by Shorthand.
|
||||||
|
|
||||||
##### Swap
|
##### Swap
|
||||||
|
|
||||||
- `origin` (Option\<Address>): In Instruction's `origin`.
|
- `origin` (Option\<ExternalAddress>): Refundable In Instruction's `origin`.
|
||||||
- `coin` (Coin): Coin to swap funds for.
|
- `coin` (Coin): Coin to swap funds for.
|
||||||
- `minimum` (Amount): Minimum amount of `coin` to receive.
|
- `minimum` (Amount): Minimum amount of `coin` to receive.
|
||||||
- `out` (Out Instruction): Final destination for funds.
|
- `out` (Out Instruction): Final destination for funds.
|
||||||
|
|
||||||
which expands to:
|
which expands to:
|
||||||
|
|
||||||
```
|
```
|
||||||
In Instruction {
|
RefundableInInstruction {
|
||||||
origin,
|
origin,
|
||||||
target: Router,
|
instruction: ApplicationCall {
|
||||||
data: swap(Incoming Asset, out, minimum)
|
application: DEX,
|
||||||
|
data: swap(Incoming Asset, coin, minimum, out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,19 +103,23 @@ where `swap` is a function which:
|
|||||||
|
|
||||||
##### Add Liquidity
|
##### Add Liquidity
|
||||||
|
|
||||||
- `origin` (Option\<Address>): In Instruction's `origin`.
|
- `origin` (Option\<ExternalAddress>): Refundable In Instruction's `origin`.
|
||||||
- `minimum` (Amount): Minimum amount of SRI to receive.
|
- `minimum` (Amount): Minimum amount of SRI tokens to swap
|
||||||
- `gas` (Amount): Amount of SRI to send to `address` to cover
|
half for.
|
||||||
gas in the future.
|
- `gas` (Amount): Amount of SRI to send to `address` to
|
||||||
- `address` (Address): Account to send the created liquidity tokens.
|
cover gas in the future.
|
||||||
|
- `address` (Address): Account to send the created liquidity
|
||||||
|
tokens.
|
||||||
|
|
||||||
which expands to:
|
which expands to:
|
||||||
|
|
||||||
```
|
```
|
||||||
In Instruction {
|
RefundableInInstruction {
|
||||||
origin,
|
origin,
|
||||||
target: Router,
|
instruction: ApplicationCall {
|
||||||
data: swap_and_add_liquidity(Incoming Asset, address, minimum, gas)
|
application: DEX,
|
||||||
|
data: swap_and_add_liquidity(Incoming Asset, minimum, gas, address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -120,5 +128,5 @@ where `swap_and_add_liquidity` is a function which:
|
|||||||
1) Swaps half of the incoming funds for SRI.
|
1) Swaps half of the incoming funds for SRI.
|
||||||
2) Checks the amount of SRI received is greater than `minimum`.
|
2) Checks the amount of SRI received is greater than `minimum`.
|
||||||
3) Calls `swap_and_add_liquidity` with the amount of SRI received - `gas`, and
|
3) Calls `swap_and_add_liquidity` with the amount of SRI received - `gas`, and
|
||||||
a matching amount of the incoming asset.
|
a matching amount of the incoming coin.
|
||||||
4) Transfers any leftover funds to `address`.
|
4) Transfers any leftover funds to `address`.
|
||||||
|
|||||||
@@ -5,14 +5,17 @@
|
|||||||
These are the list of types used to represent various properties within the
|
These are the list of types used to represent various properties within the
|
||||||
protocol.
|
protocol.
|
||||||
|
|
||||||
| Alias | Type |
|
| Alias | Type |
|
||||||
|------------------------|--------------------------------|
|
|------------------------|----------------------------------------------|
|
||||||
| Amount | u64 |
|
| SeraiAddress | sr25519::Public (unchecked [u8; 32] wrapper) |
|
||||||
| Coin | u32 |
|
| Amount | u64 |
|
||||||
| Session | u32 |
|
| Coin | u32 |
|
||||||
| Validator Set Index | u16 |
|
| Session | u32 |
|
||||||
| Validator Set Instance | (Session, Validator Set Index) |
|
| Validator Set Index | u16 |
|
||||||
| Key | Vec\<u8> |
|
| Validator Set Instance | (Session, Validator Set Index) |
|
||||||
|
| Key | BoundedVec\<u8, 96> |
|
||||||
|
| ExternalAddress | BoundedVec\<u8, 74> |
|
||||||
|
| Data | BoundedVec\<u8, 512> |
|
||||||
|
|
||||||
### Networks
|
### Networks
|
||||||
|
|
||||||
@@ -36,7 +39,8 @@ Coins exist over a network and have a distinct integer ID.
|
|||||||
|
|
||||||
| Coin | Network | ID |
|
| Coin | Network | ID |
|
||||||
|----------|----------|----|
|
|----------|----------|----|
|
||||||
| Bitcoin | Bitcoin | 0 |
|
| Serai | Serai | 0 |
|
||||||
| Ether | Ethereum | 1 |
|
| Bitcoin | Bitcoin | 1 |
|
||||||
| DAI | Ethereum | 2 |
|
| Ether | Ethereum | 2 |
|
||||||
| Monero | Monero | 3 |
|
| DAI | Ethereum | 3 |
|
||||||
|
| Monero | Monero | 4 |
|
||||||
|
|||||||
@@ -14,26 +14,28 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# Macros
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
zeroize = "1.5"
|
zeroize = "1.5"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
|
|
||||||
|
# Cryptography
|
||||||
group = "0.12"
|
group = "0.12"
|
||||||
|
|
||||||
curve25519-dalek = { version = "3", features = ["std"] }
|
curve25519-dalek = { version = "3", features = ["std"] }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../crypto/transcript", features = ["recommended"] }
|
|
||||||
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
|
dalek-ff-group = { path = "../crypto/dalek-ff-group" }
|
||||||
|
|
||||||
|
transcript = { package = "flexible-transcript", path = "../crypto/transcript" }
|
||||||
frost = { package = "modular-frost", path = "../crypto/frost", features = ["ed25519"] }
|
frost = { package = "modular-frost", path = "../crypto/frost", features = ["ed25519"] }
|
||||||
|
|
||||||
|
# Monero
|
||||||
monero-serai = { path = "../coins/monero", features = ["multisig"] }
|
monero-serai = { path = "../coins/monero", features = ["multisig"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
|
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|||||||
@@ -1,113 +1,4 @@
|
|||||||
use std::{
|
mod send;
|
||||||
sync::{Arc, RwLock},
|
pub(crate) use send::test_send;
|
||||||
collections::HashMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
mod monero;
|
||||||
|
|
||||||
use rand_core::OsRng;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
NetworkError, Network,
|
|
||||||
coin::{Coin, Monero},
|
|
||||||
wallet::{WalletKeys, MemCoinDb, Wallet},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct LocalNetwork {
|
|
||||||
i: u16,
|
|
||||||
size: u16,
|
|
||||||
round: usize,
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
rounds: Arc<RwLock<Vec<HashMap<u16, Vec<u8>>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LocalNetwork {
|
|
||||||
fn new(size: u16) -> Vec<LocalNetwork> {
|
|
||||||
let rounds = Arc::new(RwLock::new(vec![]));
|
|
||||||
let mut res = vec![];
|
|
||||||
for i in 1 ..= size {
|
|
||||||
res.push(LocalNetwork { i, size, round: 0, rounds: rounds.clone() });
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Network for LocalNetwork {
|
|
||||||
async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<u16, Vec<u8>>, NetworkError> {
|
|
||||||
{
|
|
||||||
let mut rounds = self.rounds.write().unwrap();
|
|
||||||
if rounds.len() == self.round {
|
|
||||||
rounds.push(HashMap::new());
|
|
||||||
}
|
|
||||||
rounds[self.round].insert(self.i, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
while {
|
|
||||||
let read = self.rounds.try_read().unwrap();
|
|
||||||
read[self.round].len() != usize::from(self.size)
|
|
||||||
} {
|
|
||||||
tokio::task::yield_now().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut res = self.rounds.try_read().unwrap()[self.round].clone();
|
|
||||||
res.remove(&self.i);
|
|
||||||
self.round += 1;
|
|
||||||
Ok(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test_send<C: Coin + Clone>(coin: C, fee: C::Fee) {
|
|
||||||
// Mine blocks so there's a confirmed block
|
|
||||||
coin.mine_block().await;
|
|
||||||
let latest = coin.get_latest_block_number().await.unwrap();
|
|
||||||
|
|
||||||
let mut keys = frost::tests::key_gen::<_, C::Curve>(&mut OsRng);
|
|
||||||
let threshold = keys[&1].params().t();
|
|
||||||
let mut networks = LocalNetwork::new(threshold);
|
|
||||||
|
|
||||||
let mut wallets = vec![];
|
|
||||||
for i in 1 ..= threshold {
|
|
||||||
let mut wallet = Wallet::new(MemCoinDb::new(), coin.clone());
|
|
||||||
wallet.acknowledge_block(0, latest);
|
|
||||||
wallet.add_keys(&WalletKeys::new(keys.remove(&i).unwrap(), 0));
|
|
||||||
wallets.push(wallet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the chain to a length where blocks have sufficient confirmations
|
|
||||||
while (latest + (C::CONFIRMATIONS - 1)) > coin.get_latest_block_number().await.unwrap() {
|
|
||||||
coin.mine_block().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
for wallet in wallets.iter_mut() {
|
|
||||||
// Poll to activate the keys
|
|
||||||
wallet.poll().await.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
coin.test_send(wallets[0].address()).await;
|
|
||||||
|
|
||||||
let mut futures = vec![];
|
|
||||||
for (network, wallet) in networks.iter_mut().zip(wallets.iter_mut()) {
|
|
||||||
wallet.poll().await.unwrap();
|
|
||||||
|
|
||||||
let latest = coin.get_latest_block_number().await.unwrap();
|
|
||||||
wallet.acknowledge_block(1, latest - (C::CONFIRMATIONS - 1));
|
|
||||||
let signable = wallet
|
|
||||||
.prepare_sends(1, vec![(wallet.address(), 10000000000)], fee)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.1
|
|
||||||
.swap_remove(0);
|
|
||||||
futures.push(wallet.attempt_send(network, signable));
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{:?}", hex::encode(futures::future::join_all(futures).await.swap_remove(0).unwrap().0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn monero() {
|
|
||||||
let monero = Monero::new("http://127.0.0.1:18081".to_string()).await;
|
|
||||||
let fee = monero.get_fee().await;
|
|
||||||
test_send(monero, fee).await;
|
|
||||||
}
|
|
||||||
|
|||||||
11
processor/src/tests/monero.rs
Normal file
11
processor/src/tests/monero.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use crate::{
|
||||||
|
coin::{Coin, Monero},
|
||||||
|
tests::test_send,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn monero() {
|
||||||
|
let monero = Monero::new("http://127.0.0.1:18081".to_string()).await;
|
||||||
|
let fee = monero.get_fee().await;
|
||||||
|
test_send(monero, fee).await;
|
||||||
|
}
|
||||||
106
processor/src/tests/send.rs
Normal file
106
processor/src/tests/send.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use rand_core::OsRng;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
NetworkError, Network,
|
||||||
|
coin::Coin,
|
||||||
|
wallet::{WalletKeys, MemCoinDb, Wallet},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LocalNetwork {
|
||||||
|
i: u16,
|
||||||
|
size: u16,
|
||||||
|
round: usize,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
rounds: Arc<RwLock<Vec<HashMap<u16, Vec<u8>>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalNetwork {
|
||||||
|
fn new(size: u16) -> Vec<LocalNetwork> {
|
||||||
|
let rounds = Arc::new(RwLock::new(vec![]));
|
||||||
|
let mut res = vec![];
|
||||||
|
for i in 1 ..= size {
|
||||||
|
res.push(LocalNetwork { i, size, round: 0, rounds: rounds.clone() });
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Network for LocalNetwork {
|
||||||
|
async fn round(&mut self, data: Vec<u8>) -> Result<HashMap<u16, Vec<u8>>, NetworkError> {
|
||||||
|
{
|
||||||
|
let mut rounds = self.rounds.write().unwrap();
|
||||||
|
if rounds.len() == self.round {
|
||||||
|
rounds.push(HashMap::new());
|
||||||
|
}
|
||||||
|
rounds[self.round].insert(self.i, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
while {
|
||||||
|
let read = self.rounds.try_read().unwrap();
|
||||||
|
read[self.round].len() != usize::from(self.size)
|
||||||
|
} {
|
||||||
|
tokio::task::yield_now().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut res = self.rounds.try_read().unwrap()[self.round].clone();
|
||||||
|
res.remove(&self.i);
|
||||||
|
self.round += 1;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test_send<C: Coin + Clone>(coin: C, fee: C::Fee) {
|
||||||
|
// Mine blocks so there's a confirmed block
|
||||||
|
coin.mine_block().await;
|
||||||
|
let latest = coin.get_latest_block_number().await.unwrap();
|
||||||
|
|
||||||
|
let mut keys = frost::tests::key_gen::<_, C::Curve>(&mut OsRng);
|
||||||
|
let threshold = keys[&1].params().t();
|
||||||
|
let mut networks = LocalNetwork::new(threshold);
|
||||||
|
|
||||||
|
let mut wallets = vec![];
|
||||||
|
for i in 1 ..= threshold {
|
||||||
|
let mut wallet = Wallet::new(MemCoinDb::new(), coin.clone());
|
||||||
|
wallet.acknowledge_block(0, latest);
|
||||||
|
wallet.add_keys(&WalletKeys::new(keys.remove(&i).unwrap(), 0));
|
||||||
|
wallets.push(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the chain to a length where blocks have sufficient confirmations
|
||||||
|
while (latest + (C::CONFIRMATIONS - 1)) > coin.get_latest_block_number().await.unwrap() {
|
||||||
|
coin.mine_block().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
for wallet in wallets.iter_mut() {
|
||||||
|
// Poll to activate the keys
|
||||||
|
wallet.poll().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
coin.test_send(wallets[0].address()).await;
|
||||||
|
|
||||||
|
let mut futures = vec![];
|
||||||
|
for (network, wallet) in networks.iter_mut().zip(wallets.iter_mut()) {
|
||||||
|
wallet.poll().await.unwrap();
|
||||||
|
|
||||||
|
let latest = coin.get_latest_block_number().await.unwrap();
|
||||||
|
wallet.acknowledge_block(1, latest - (C::CONFIRMATIONS - 1));
|
||||||
|
let signable = wallet
|
||||||
|
.prepare_sends(1, vec![(wallet.address(), 10000000000)], fee)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.swap_remove(0);
|
||||||
|
futures.push(wallet.attempt_send(network, signable));
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{:?}", hex::encode(futures::future::join_all(futures).await.swap_remove(0).unwrap().0));
|
||||||
|
}
|
||||||
24
substrate/in-instructions/client/Cargo.toml
Normal file
24
substrate/in-instructions/client/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "in-instructions-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Package In Instructions into inherent transactions"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-trait = "0.1"
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", features = ["derive", "max-encoded-len"] }
|
||||||
|
|
||||||
|
jsonrpsee-core = "0.16"
|
||||||
|
jsonrpsee-http-client = "0.16"
|
||||||
|
|
||||||
|
sp-inherents = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
|
in-instructions-pallet = { path = "../pallet" }
|
||||||
15
substrate/in-instructions/client/LICENSE
Normal file
15
substrate/in-instructions/client/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
AGPL-3.0-only license
|
||||||
|
|
||||||
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
47
substrate/in-instructions/client/src/lib.rs
Normal file
47
substrate/in-instructions/client/src/lib.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
|
use scale::Decode;
|
||||||
|
|
||||||
|
use jsonrpsee_core::client::ClientT;
|
||||||
|
use jsonrpsee_http_client::HttpClientBuilder;
|
||||||
|
|
||||||
|
use sp_inherents::{Error, InherentData, InherentIdentifier};
|
||||||
|
|
||||||
|
use in_instructions_pallet::{INHERENT_IDENTIFIER, Updates, InherentError};
|
||||||
|
|
||||||
|
pub struct InherentDataProvider;
|
||||||
|
impl InherentDataProvider {
|
||||||
|
#[allow(clippy::new_without_default)] // This isn't planned to forever have empty arguments
|
||||||
|
pub fn new() -> InherentDataProvider {
|
||||||
|
InherentDataProvider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl sp_inherents::InherentDataProvider for InherentDataProvider {
|
||||||
|
async fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), Error> {
|
||||||
|
let updates: Updates = (|| async {
|
||||||
|
let client = HttpClientBuilder::default().build("http://127.0.0.1:5134")?;
|
||||||
|
client.request("processor_coinUpdates", Vec::<u8>::new()).await
|
||||||
|
})()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
Error::Application(Box::from(format!("couldn't communicate with processor: {e}")))
|
||||||
|
})?;
|
||||||
|
inherent_data.put_data(INHERENT_IDENTIFIER, &updates)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn try_handle_error(
|
||||||
|
&self,
|
||||||
|
identifier: &InherentIdentifier,
|
||||||
|
mut error: &[u8],
|
||||||
|
) -> Option<Result<(), Error>> {
|
||||||
|
if *identifier != INHERENT_IDENTIFIER {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Err(Error::Application(Box::from(<InherentError as Decode>::decode(&mut error).ok()?))))
|
||||||
|
}
|
||||||
|
}
|
||||||
51
substrate/in-instructions/pallet/Cargo.toml
Normal file
51
substrate/in-instructions/pallet/Cargo.toml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
[package]
|
||||||
|
name = "in-instructions-pallet"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Execute calls via In Instructions from inherent transactions"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = { version = "1", optional = true }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
|
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
sp-inherents = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
|
||||||
|
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../../serai/primitives", default-features = false }
|
||||||
|
in-instructions-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [
|
||||||
|
"thiserror",
|
||||||
|
|
||||||
|
"scale/std",
|
||||||
|
"scale-info/std",
|
||||||
|
|
||||||
|
"serde",
|
||||||
|
|
||||||
|
"sp-std/std",
|
||||||
|
"sp-inherents/std",
|
||||||
|
"sp-runtime/std",
|
||||||
|
|
||||||
|
"frame-system/std",
|
||||||
|
"frame-support/std",
|
||||||
|
|
||||||
|
"serai-primitives/std",
|
||||||
|
"in-instructions-primitives/std",
|
||||||
|
]
|
||||||
|
default = ["std"]
|
||||||
15
substrate/in-instructions/pallet/LICENSE
Normal file
15
substrate/in-instructions/pallet/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
AGPL-3.0-only license
|
||||||
|
|
||||||
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
240
substrate/in-instructions/pallet/src/lib.rs
Normal file
240
substrate/in-instructions/pallet/src/lib.rs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use scale::{Encode, Decode};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_std::vec::Vec;
|
||||||
|
use sp_inherents::{InherentIdentifier, IsFatalError};
|
||||||
|
|
||||||
|
use sp_runtime::RuntimeDebug;
|
||||||
|
|
||||||
|
use serai_primitives::{BlockNumber, BlockHash, Coin};
|
||||||
|
|
||||||
|
pub use in_instructions_primitives as primitives;
|
||||||
|
use primitives::InInstruction;
|
||||||
|
|
||||||
|
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"ininstrs";
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct Batch {
|
||||||
|
pub id: BlockHash,
|
||||||
|
pub instructions: Vec<InInstruction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct Update {
|
||||||
|
// Coin's latest block number
|
||||||
|
pub block_number: BlockNumber,
|
||||||
|
pub batches: Vec<Batch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// None if the current block producer isn't operating over this coin or otherwise failed to get
|
||||||
|
// data
|
||||||
|
pub type Updates = Vec<Option<Update>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Encode, RuntimeDebug)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Decode, thiserror::Error))]
|
||||||
|
pub enum InherentError {
|
||||||
|
#[cfg_attr(feature = "std", error("invalid call"))]
|
||||||
|
InvalidCall,
|
||||||
|
#[cfg_attr(feature = "std", error("inherent has {0} updates despite us having {1} coins"))]
|
||||||
|
InvalidUpdateQuantity(u32, u32),
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "std",
|
||||||
|
error("inherent for coin {0:?} has block number {1:?} despite us having {2:?}")
|
||||||
|
)]
|
||||||
|
UnrecognizedBlockNumber(Coin, BlockNumber, BlockNumber),
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "std",
|
||||||
|
error("inherent for coin {0:?} has block number {1:?} which doesn't succeed {2:?}")
|
||||||
|
)]
|
||||||
|
InvalidBlockNumber(Coin, BlockNumber, BlockNumber),
|
||||||
|
#[cfg_attr(feature = "std", error("coin {0:?} has {1} more batches than we do"))]
|
||||||
|
UnrecognizedBatches(Coin, u32),
|
||||||
|
#[cfg_attr(feature = "std", error("coin {0:?} has a different batch (ID {1:?})"))]
|
||||||
|
DifferentBatch(Coin, BlockHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsFatalError for InherentError {
|
||||||
|
fn is_fatal_error(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
InherentError::InvalidCall | InherentError::InvalidUpdateQuantity(..) => true,
|
||||||
|
InherentError::UnrecognizedBlockNumber(..) => false,
|
||||||
|
InherentError::InvalidBlockNumber(..) => true,
|
||||||
|
InherentError::UnrecognizedBatches(..) => false,
|
||||||
|
// One of our nodes is definitively wrong. If it's ours (signified by it passing consensus),
|
||||||
|
// we should panic. If it's theirs, they should be slashed
|
||||||
|
// Unfortunately, we can't return fatal here to trigger a slash as fatal should only be used
|
||||||
|
// for undeniable, technical invalidity
|
||||||
|
// TODO: Code a way in which this still triggers a slash vote
|
||||||
|
InherentError::DifferentBatch(..) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coin_from_index(index: usize) -> Coin {
|
||||||
|
// Offset by 1 since Serai is the first coin, yet Serai doesn't have updates
|
||||||
|
Coin::from(1 + u32::try_from(index).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[frame_support::pallet]
|
||||||
|
pub mod pallet {
|
||||||
|
use frame_support::pallet_prelude::*;
|
||||||
|
use frame_system::pallet_prelude::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[pallet::config]
|
||||||
|
pub trait Config: frame_system::Config<BlockNumber = u32> {
|
||||||
|
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::event]
|
||||||
|
#[pallet::generate_deposit(fn deposit_event)]
|
||||||
|
pub enum Event<T: Config> {
|
||||||
|
Batch { coin: Coin, id: BlockHash },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::pallet]
|
||||||
|
#[pallet::generate_store(pub(crate) trait Store)]
|
||||||
|
pub struct Pallet<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
// Used to only allow one set of updates per block, preventing double updating
|
||||||
|
#[pallet::storage]
|
||||||
|
pub(crate) type Once<T: Config> = StorageValue<_, bool, ValueQuery>;
|
||||||
|
// Latest block number agreed upon for a coin
|
||||||
|
#[pallet::storage]
|
||||||
|
#[pallet::getter(fn block_number)]
|
||||||
|
pub(crate) type BlockNumbers<T: Config> =
|
||||||
|
StorageMap<_, Blake2_256, Coin, BlockNumber, ValueQuery>;
|
||||||
|
|
||||||
|
#[pallet::hooks]
|
||||||
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
fn on_finalize(_: BlockNumberFor<T>) {
|
||||||
|
Once::<T>::take();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::call]
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
#[pallet::call_index(0)]
|
||||||
|
#[pallet::weight((0, DispatchClass::Mandatory))] // TODO
|
||||||
|
pub fn execute(origin: OriginFor<T>, updates: Updates) -> DispatchResult {
|
||||||
|
ensure_none(origin)?;
|
||||||
|
assert!(!Once::<T>::exists());
|
||||||
|
Once::<T>::put(true);
|
||||||
|
|
||||||
|
for (coin, update) in updates.iter().enumerate() {
|
||||||
|
if let Some(update) = update {
|
||||||
|
let coin = coin_from_index(coin);
|
||||||
|
BlockNumbers::<T>::insert(coin, update.block_number);
|
||||||
|
|
||||||
|
for batch in &update.batches {
|
||||||
|
// TODO: EXECUTE
|
||||||
|
Self::deposit_event(Event::Batch { coin, id: batch.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pallet::inherent]
|
||||||
|
impl<T: Config> ProvideInherent for Pallet<T> {
|
||||||
|
type Call = Call<T>;
|
||||||
|
type Error = InherentError;
|
||||||
|
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
|
||||||
|
|
||||||
|
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
|
||||||
|
data
|
||||||
|
.get_data::<Updates>(&INHERENT_IDENTIFIER)
|
||||||
|
.unwrap()
|
||||||
|
.map(|updates| Call::execute { updates })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes that only not yet handled batches are provided as inherent data
|
||||||
|
fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
|
||||||
|
// First unwrap is for the Result of fetching/decoding the Updates
|
||||||
|
// Second unwrap is for the Option of if they exist
|
||||||
|
let expected = data.get_data::<Updates>(&INHERENT_IDENTIFIER).unwrap().unwrap();
|
||||||
|
// Match to be exhaustive
|
||||||
|
let updates = match call {
|
||||||
|
Call::execute { ref updates } => updates,
|
||||||
|
_ => Err(InherentError::InvalidCall)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The block producer should've provided one update per coin
|
||||||
|
// We, an honest node, did provide one update per coin
|
||||||
|
// Accordingly, we should have the same amount of updates
|
||||||
|
if updates.len() != expected.len() {
|
||||||
|
Err(InherentError::InvalidUpdateQuantity(
|
||||||
|
updates.len().try_into().unwrap(),
|
||||||
|
expected.len().try_into().unwrap(),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This zip is safe since we verified they're equally sized
|
||||||
|
// This should be written as coins.zip(updates.iter().zip(&expected)), where coins is the
|
||||||
|
// validator set's coins
|
||||||
|
// That'd require having context on the validator set right now which isn't worth pulling in
|
||||||
|
// right now, when we only have one validator set
|
||||||
|
for (coin, both) in updates.iter().zip(&expected).enumerate() {
|
||||||
|
let coin = coin_from_index(coin);
|
||||||
|
match both {
|
||||||
|
// Block producer claims there's an update for this coin, as do we
|
||||||
|
(Some(update), Some(expected)) => {
|
||||||
|
if update.block_number.0 > expected.block_number.0 {
|
||||||
|
Err(InherentError::UnrecognizedBlockNumber(
|
||||||
|
coin,
|
||||||
|
update.block_number,
|
||||||
|
expected.block_number,
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prev = BlockNumbers::<T>::get(coin);
|
||||||
|
if update.block_number.0 <= prev.0 {
|
||||||
|
Err(InherentError::InvalidBlockNumber(coin, update.block_number, prev))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if update.batches.len() > expected.batches.len() {
|
||||||
|
Err(InherentError::UnrecognizedBatches(
|
||||||
|
coin,
|
||||||
|
(update.batches.len() - expected.batches.len()).try_into().unwrap(),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (batch, expected) in update.batches.iter().zip(&expected.batches) {
|
||||||
|
if batch != expected {
|
||||||
|
Err(InherentError::DifferentBatch(coin, batch.id))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block producer claims there's an update for this coin, yet we don't
|
||||||
|
(Some(update), None) => {
|
||||||
|
Err(InherentError::UnrecognizedBatches(coin, update.batches.len().try_into().unwrap()))?
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block producer didn't include update for this coin
|
||||||
|
(None, _) => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_inherent(_: &Self::Call) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use pallet::*;
|
||||||
25
substrate/in-instructions/primitives/Cargo.toml
Normal file
25
substrate/in-instructions/primitives/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "in-instructions-primitives"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Serai instructions library, enabling encoding and decoding"
|
||||||
|
license = "MIT"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
|
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
|
||||||
|
serai-primitives = { path = "../../serai/primitives", default-features = false }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = ["scale/std", "scale-info/std", "serde", "sp-core/std", "serai-primitives/std"]
|
||||||
|
default = ["std"]
|
||||||
21
substrate/in-instructions/primitives/LICENSE
Normal file
21
substrate/in-instructions/primitives/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022-2023 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
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
38
substrate/in-instructions/primitives/src/incoming.rs
Normal file
38
substrate/in-instructions/primitives/src/incoming.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use scale::{Encode, Decode, MaxEncodedLen};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
|
||||||
|
use serai_primitives::SeraiAddress;
|
||||||
|
|
||||||
|
use crate::{MAX_DATA_LEN, ExternalAddress};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub enum Application {
|
||||||
|
DEX,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct ApplicationCall {
|
||||||
|
application: Application,
|
||||||
|
data: BoundedVec<u8, ConstU32<{ MAX_DATA_LEN }>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub enum InInstruction {
|
||||||
|
Transfer(SeraiAddress),
|
||||||
|
Call(ApplicationCall),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct RefundableInInstruction {
|
||||||
|
pub origin: Option<ExternalAddress>,
|
||||||
|
pub instruction: InInstruction,
|
||||||
|
}
|
||||||
47
substrate/in-instructions/primitives/src/lib.rs
Normal file
47
substrate/in-instructions/primitives/src/lib.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#![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;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
|
||||||
|
// Monero, our current longest address candidate, has a longest address of featured with payment ID
|
||||||
|
// 1 (enum) + 1 (flags) + 64 (two keys) + 8 (payment ID) = 74
|
||||||
|
pub const MAX_ADDRESS_LEN: u32 = 74;
|
||||||
|
// Should be enough for a Uniswap v3 call
|
||||||
|
pub const MAX_DATA_LEN: u32 = 512;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct ExternalAddress(BoundedVec<u8, ConstU32<{ MAX_ADDRESS_LEN }>>);
|
||||||
|
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}")?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> &[u8] {
|
||||||
|
self.0.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn consume(self) -> Vec<u8> {
|
||||||
|
self.0.into_inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not "in" as "in" is a keyword
|
||||||
|
mod incoming;
|
||||||
|
pub use incoming::*;
|
||||||
|
|
||||||
|
// Not "out" to match in
|
||||||
|
mod outgoing;
|
||||||
|
pub use outgoing::*;
|
||||||
|
|
||||||
|
mod shorthand;
|
||||||
|
pub use shorthand::*;
|
||||||
25
substrate/in-instructions/primitives/src/outgoing.rs
Normal file
25
substrate/in-instructions/primitives/src/outgoing.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use scale::{Encode, Decode, MaxEncodedLen};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
|
||||||
|
use serai_primitives::SeraiAddress;
|
||||||
|
|
||||||
|
use crate::{MAX_DATA_LEN, ExternalAddress};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub enum Destination {
|
||||||
|
Native(SeraiAddress),
|
||||||
|
External(ExternalAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct OutInstruction {
|
||||||
|
destination: Destination,
|
||||||
|
data: Option<BoundedVec<u8, ConstU32<{ MAX_DATA_LEN }>>>,
|
||||||
|
}
|
||||||
54
substrate/in-instructions/primitives/src/shorthand.rs
Normal file
54
substrate/in-instructions/primitives/src/shorthand.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use scale::{Encode, Decode, MaxEncodedLen};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||||
|
|
||||||
|
use serai_primitives::{SeraiAddress, Coin, Amount};
|
||||||
|
|
||||||
|
use crate::{MAX_DATA_LEN, ExternalAddress, RefundableInInstruction, InInstruction, OutInstruction};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub enum Shorthand {
|
||||||
|
Raw(BoundedVec<u8, ConstU32<{ MAX_DATA_LEN }>>),
|
||||||
|
Swap {
|
||||||
|
origin: Option<ExternalAddress>,
|
||||||
|
coin: Coin,
|
||||||
|
minimum: Amount,
|
||||||
|
out: OutInstruction,
|
||||||
|
},
|
||||||
|
AddLiquidity {
|
||||||
|
origin: Option<ExternalAddress>,
|
||||||
|
minimum: Amount,
|
||||||
|
gas: Amount,
|
||||||
|
address: SeraiAddress,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shorthand {
|
||||||
|
pub fn transfer(origin: Option<ExternalAddress>, address: SeraiAddress) -> Option<Self> {
|
||||||
|
Some(Self::Raw(
|
||||||
|
BoundedVec::try_from(
|
||||||
|
(RefundableInInstruction { origin, instruction: InInstruction::Transfer(address) })
|
||||||
|
.encode(),
|
||||||
|
)
|
||||||
|
.ok()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Shorthand> for RefundableInInstruction {
|
||||||
|
type Error = &'static str;
|
||||||
|
fn try_from(shorthand: Shorthand) -> Result<RefundableInInstruction, &'static str> {
|
||||||
|
Ok(match shorthand {
|
||||||
|
Shorthand::Raw(raw) => {
|
||||||
|
RefundableInInstruction::decode(&mut raw.as_ref()).map_err(|_| "invalid raw instruction")?
|
||||||
|
}
|
||||||
|
Shorthand::Swap { .. } => todo!(),
|
||||||
|
Shorthand::AddLiquidity { .. } => todo!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,17 +14,11 @@ name = "serai-node"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
|
||||||
log = "0.4"
|
|
||||||
|
|
||||||
futures = { version = "0.3" }
|
|
||||||
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|
||||||
jsonrpsee = { version = "0.16", features = ["server"] }
|
jsonrpsee = { version = "0.16", features = ["server"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
sp-keystore = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
sp-keyring = { git = "https://github.com/serai-dex/substrate" }
|
sp-keyring = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sp-inherents = { git = "https://github.com/serai-dex/substrate" }
|
sp-inherents = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
|
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
|
||||||
@@ -33,7 +27,11 @@ sp-api = { git = "https://github.com/serai-dex/substrate" }
|
|||||||
sp-block-builder = { git = "https://github.com/serai-dex/substrate" }
|
sp-block-builder = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sp-consensus = { git = "https://github.com/serai-dex/substrate" }
|
sp-consensus = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
sc-keystore = { git = "https://github.com/serai-dex/substrate" }
|
frame-benchmarking = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
frame-benchmarking-cli = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
|
serai-runtime = { path = "../runtime" }
|
||||||
|
|
||||||
sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" }
|
sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-transaction-pool-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-transaction-pool-api = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-basic-authorship = { git = "https://github.com/serai-dex/substrate" }
|
sc-basic-authorship = { git = "https://github.com/serai-dex/substrate" }
|
||||||
@@ -47,24 +45,13 @@ sc-consensus = { git = "https://github.com/serai-dex/substrate" }
|
|||||||
sc-telemetry = { git = "https://github.com/serai-dex/substrate" }
|
sc-telemetry = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-cli = { git = "https://github.com/serai-dex/substrate" }
|
sc-cli = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
frame-benchmarking-cli = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
sc-rpc = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
sc-rpc-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-rpc-api = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" }
|
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" }
|
||||||
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" }
|
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|
||||||
serai-primitives = { path = "../serai/primitives" }
|
in-instructions-client = { path = "../in-instructions/client" }
|
||||||
|
|
||||||
validator-sets-pallet = { path = "../validator-sets/pallet" }
|
|
||||||
|
|
||||||
sp-tendermint = { path = "../tendermint/primitives" }
|
|
||||||
pallet-tendermint = { path = "../tendermint/pallet", default-features = false }
|
|
||||||
serai-runtime = { path = "../runtime" }
|
|
||||||
sc-tendermint = { path = "../tendermint/client" }
|
sc-tendermint = { path = "../tendermint/client" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ use sp_runtime::traits::TrailingZeroInput;
|
|||||||
|
|
||||||
use sc_service::ChainType;
|
use sc_service::ChainType;
|
||||||
|
|
||||||
use serai_primitives::*;
|
|
||||||
use pallet_tendermint::crypto::Public;
|
|
||||||
|
|
||||||
use serai_runtime::{
|
use serai_runtime::{
|
||||||
WASM_BINARY, AccountId, opaque::SessionKeys, GenesisConfig, SystemConfig, BalancesConfig,
|
primitives::*, tendermint::crypto::Public, WASM_BINARY, opaque::SessionKeys, GenesisConfig,
|
||||||
AssetsConfig, ValidatorSetsConfig, SessionConfig,
|
SystemConfig, BalancesConfig, AssetsConfig, ValidatorSetsConfig, SessionConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
|
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
|
||||||
@@ -17,22 +14,22 @@ fn insecure_pair_from_name(name: &'static str) -> Pair {
|
|||||||
Pair::from_string(&format!("//{name}"), None).unwrap()
|
Pair::from_string(&format!("//{name}"), None).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn account_id_from_name(name: &'static str) -> AccountId {
|
fn address_from_name(name: &'static str) -> SeraiAddress {
|
||||||
insecure_pair_from_name(name).public()
|
insecure_pair_from_name(name).public()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testnet_genesis(
|
fn testnet_genesis(
|
||||||
wasm_binary: &[u8],
|
wasm_binary: &[u8],
|
||||||
validators: &[&'static str],
|
validators: &[&'static str],
|
||||||
endowed_accounts: Vec<AccountId>,
|
endowed_accounts: Vec<SeraiAddress>,
|
||||||
) -> GenesisConfig {
|
) -> GenesisConfig {
|
||||||
let session_key = |name| {
|
let session_key = |name| {
|
||||||
let key = account_id_from_name(name);
|
let key = address_from_name(name);
|
||||||
(key, key, SessionKeys { tendermint: Public::from(key) })
|
(key, key, SessionKeys { tendermint: Public::from(key) })
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Replace with a call to the pallet to ask for its account
|
// TODO: Replace with a call to the pallet to ask for its account
|
||||||
let owner = AccountId::decode(&mut TrailingZeroInput::new(b"tokens")).unwrap();
|
let owner = SeraiAddress::decode(&mut TrailingZeroInput::new(b"tokens")).unwrap();
|
||||||
|
|
||||||
GenesisConfig {
|
GenesisConfig {
|
||||||
system: SystemConfig { code: wasm_binary.to_vec() },
|
system: SystemConfig { code: wasm_binary.to_vec() },
|
||||||
@@ -54,8 +51,8 @@ fn testnet_genesis(
|
|||||||
|
|
||||||
validator_sets: ValidatorSetsConfig {
|
validator_sets: ValidatorSetsConfig {
|
||||||
bond: Amount(1_000_000) * COIN,
|
bond: Amount(1_000_000) * COIN,
|
||||||
coins: Coin(4),
|
coins: vec![BITCOIN, ETHER, DAI, MONERO],
|
||||||
participants: validators.iter().map(|name| account_id_from_name(name)).collect(),
|
participants: validators.iter().map(|name| address_from_name(name)).collect(),
|
||||||
},
|
},
|
||||||
session: SessionConfig { keys: validators.iter().map(|name| session_key(*name)).collect() },
|
session: SessionConfig { keys: validators.iter().map(|name| session_key(*name)).collect() },
|
||||||
}
|
}
|
||||||
@@ -75,18 +72,18 @@ pub fn development_config() -> Result<ChainSpec, &'static str> {
|
|||||||
wasm_binary,
|
wasm_binary,
|
||||||
&["Alice"],
|
&["Alice"],
|
||||||
vec![
|
vec![
|
||||||
account_id_from_name("Alice"),
|
address_from_name("Alice"),
|
||||||
account_id_from_name("Bob"),
|
address_from_name("Bob"),
|
||||||
account_id_from_name("Charlie"),
|
address_from_name("Charlie"),
|
||||||
account_id_from_name("Dave"),
|
address_from_name("Dave"),
|
||||||
account_id_from_name("Eve"),
|
address_from_name("Eve"),
|
||||||
account_id_from_name("Ferdie"),
|
address_from_name("Ferdie"),
|
||||||
account_id_from_name("Alice//stash"),
|
address_from_name("Alice//stash"),
|
||||||
account_id_from_name("Bob//stash"),
|
address_from_name("Bob//stash"),
|
||||||
account_id_from_name("Charlie//stash"),
|
address_from_name("Charlie//stash"),
|
||||||
account_id_from_name("Dave//stash"),
|
address_from_name("Dave//stash"),
|
||||||
account_id_from_name("Eve//stash"),
|
address_from_name("Eve//stash"),
|
||||||
account_id_from_name("Ferdie//stash"),
|
address_from_name("Ferdie//stash"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -119,18 +116,18 @@ pub fn testnet_config() -> Result<ChainSpec, &'static str> {
|
|||||||
wasm_binary,
|
wasm_binary,
|
||||||
&["Alice", "Bob", "Charlie"],
|
&["Alice", "Bob", "Charlie"],
|
||||||
vec![
|
vec![
|
||||||
account_id_from_name("Alice"),
|
address_from_name("Alice"),
|
||||||
account_id_from_name("Bob"),
|
address_from_name("Bob"),
|
||||||
account_id_from_name("Charlie"),
|
address_from_name("Charlie"),
|
||||||
account_id_from_name("Dave"),
|
address_from_name("Dave"),
|
||||||
account_id_from_name("Eve"),
|
address_from_name("Eve"),
|
||||||
account_id_from_name("Ferdie"),
|
address_from_name("Ferdie"),
|
||||||
account_id_from_name("Alice//stash"),
|
address_from_name("Alice//stash"),
|
||||||
account_id_from_name("Bob//stash"),
|
address_from_name("Bob//stash"),
|
||||||
account_id_from_name("Charlie//stash"),
|
address_from_name("Charlie//stash"),
|
||||||
account_id_from_name("Dave//stash"),
|
address_from_name("Dave//stash"),
|
||||||
account_id_from_name("Eve//stash"),
|
address_from_name("Eve//stash"),
|
||||||
account_id_from_name("Ferdie//stash"),
|
address_from_name("Ferdie//stash"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use sc_service::{PruningMode, PartialComponents};
|
|
||||||
use frame_benchmarking_cli::{ExtrinsicFactory, BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
|
|
||||||
use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli};
|
|
||||||
|
|
||||||
use serai_runtime::Block;
|
use serai_runtime::Block;
|
||||||
|
|
||||||
|
use sc_service::{PruningMode, PartialComponents};
|
||||||
|
|
||||||
|
use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli};
|
||||||
|
use frame_benchmarking_cli::{ExtrinsicFactory, BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chain_spec,
|
chain_spec,
|
||||||
cli::{Cli, Subcommand},
|
cli::{Cli, Subcommand},
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ use sp_runtime::OpaqueExtrinsic;
|
|||||||
use sc_cli::Result;
|
use sc_cli::Result;
|
||||||
use sc_client_api::BlockBackend;
|
use sc_client_api::BlockBackend;
|
||||||
|
|
||||||
use serai_runtime as runtime;
|
use serai_runtime::{
|
||||||
use runtime::SystemCall;
|
VERSION, BlockHashCount,
|
||||||
|
system::{self, Call as SystemCall},
|
||||||
|
transaction_payment, RuntimeCall, UncheckedExtrinsic, SignedPayload, Runtime,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::service::FullClient;
|
use crate::service::FullClient;
|
||||||
|
|
||||||
@@ -45,35 +48,33 @@ impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder {
|
|||||||
pub fn create_benchmark_extrinsic(
|
pub fn create_benchmark_extrinsic(
|
||||||
client: &FullClient,
|
client: &FullClient,
|
||||||
sender: sp_core::sr25519::Pair,
|
sender: sp_core::sr25519::Pair,
|
||||||
call: runtime::RuntimeCall,
|
call: RuntimeCall,
|
||||||
nonce: u32,
|
nonce: u32,
|
||||||
) -> runtime::UncheckedExtrinsic {
|
) -> UncheckedExtrinsic {
|
||||||
let extra = (
|
let extra = (
|
||||||
frame_system::CheckNonZeroSender::<runtime::Runtime>::new(),
|
system::CheckNonZeroSender::<Runtime>::new(),
|
||||||
frame_system::CheckSpecVersion::<runtime::Runtime>::new(),
|
system::CheckSpecVersion::<Runtime>::new(),
|
||||||
frame_system::CheckTxVersion::<runtime::Runtime>::new(),
|
system::CheckTxVersion::<Runtime>::new(),
|
||||||
frame_system::CheckGenesis::<runtime::Runtime>::new(),
|
system::CheckGenesis::<Runtime>::new(),
|
||||||
frame_system::CheckEra::<runtime::Runtime>::from(sp_runtime::generic::Era::mortal(
|
system::CheckEra::<Runtime>::from(sp_runtime::generic::Era::mortal(
|
||||||
u64::from(
|
u64::from(BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2)),
|
||||||
runtime::BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2),
|
|
||||||
),
|
|
||||||
client.chain_info().best_number.into(),
|
client.chain_info().best_number.into(),
|
||||||
)),
|
)),
|
||||||
frame_system::CheckNonce::<runtime::Runtime>::from(nonce),
|
system::CheckNonce::<Runtime>::from(nonce),
|
||||||
frame_system::CheckWeight::<runtime::Runtime>::new(),
|
system::CheckWeight::<Runtime>::new(),
|
||||||
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
|
transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
runtime::UncheckedExtrinsic::new_signed(
|
UncheckedExtrinsic::new_signed(
|
||||||
call.clone(),
|
call.clone(),
|
||||||
sender.public(),
|
sender.public(),
|
||||||
runtime::SignedPayload::from_raw(
|
SignedPayload::from_raw(
|
||||||
call,
|
call,
|
||||||
extra.clone(),
|
extra.clone(),
|
||||||
(
|
(
|
||||||
(),
|
(),
|
||||||
runtime::VERSION.spec_version,
|
VERSION.spec_version,
|
||||||
runtime::VERSION.transaction_version,
|
VERSION.transaction_version,
|
||||||
client.block_hash(0).ok().flatten().unwrap(),
|
client.block_hash(0).ok().flatten().unwrap(),
|
||||||
client.chain_info().best_hash,
|
client.chain_info().best_hash,
|
||||||
(),
|
(),
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use std::sync::Arc;
|
|||||||
use jsonrpsee::RpcModule;
|
use jsonrpsee::RpcModule;
|
||||||
|
|
||||||
use sp_blockchain::{Error as BlockchainError, HeaderBackend, HeaderMetadata};
|
use sp_blockchain::{Error as BlockchainError, HeaderBackend, HeaderMetadata};
|
||||||
use sc_transaction_pool_api::TransactionPool;
|
|
||||||
use sp_block_builder::BlockBuilder;
|
use sp_block_builder::BlockBuilder;
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
|
|
||||||
pub use sc_rpc_api::DenyUnsafe;
|
use serai_runtime::{primitives::SeraiAddress, opaque::Block, Balance, Index};
|
||||||
|
|
||||||
use serai_runtime::{opaque::Block, AccountId, Balance, Index};
|
pub use sc_rpc_api::DenyUnsafe;
|
||||||
|
use sc_transaction_pool_api::TransactionPool;
|
||||||
|
|
||||||
pub struct FullDeps<C, P> {
|
pub struct FullDeps<C, P> {
|
||||||
pub client: Arc<C>,
|
pub client: Arc<C>,
|
||||||
@@ -29,7 +29,7 @@ pub fn create_full<
|
|||||||
deps: FullDeps<C, P>,
|
deps: FullDeps<C, P>,
|
||||||
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
|
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
where
|
where
|
||||||
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>
|
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, SeraiAddress, Index>
|
||||||
+ pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
|
+ pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
|
||||||
+ BlockBuilder<Block>,
|
+ BlockBuilder<Block>,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use sp_inherents::CreateInherentDataProviders;
|
|||||||
use sp_consensus::DisableProofRecording;
|
use sp_consensus::DisableProofRecording;
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
|
|
||||||
|
use in_instructions_client::InherentDataProvider as InstructionsProvider;
|
||||||
|
|
||||||
use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor};
|
use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor};
|
||||||
use sc_transaction_pool::FullPool;
|
use sc_transaction_pool::FullPool;
|
||||||
use sc_network::NetworkService;
|
use sc_network::NetworkService;
|
||||||
@@ -24,7 +26,7 @@ pub(crate) use sc_tendermint::{
|
|||||||
TendermintClientMinimal, TendermintValidator, TendermintImport, TendermintAuthority,
|
TendermintClientMinimal, TendermintValidator, TendermintImport, TendermintAuthority,
|
||||||
TendermintSelectChain, import_queue,
|
TendermintSelectChain, import_queue,
|
||||||
};
|
};
|
||||||
use serai_runtime::{self, BLOCK_SIZE, TARGET_BLOCK_TIME, opaque::Block, RuntimeApi};
|
use serai_runtime::{self as runtime, BLOCK_SIZE, TARGET_BLOCK_TIME, opaque::Block, RuntimeApi};
|
||||||
|
|
||||||
type FullBackend = sc_service::TFullBackend<Block>;
|
type FullBackend = sc_service::TFullBackend<Block>;
|
||||||
pub type FullClient = TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<ExecutorDispatch>>;
|
pub type FullClient = TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<ExecutorDispatch>>;
|
||||||
@@ -46,7 +48,7 @@ impl NativeExecutionDispatch for ExecutorDispatch {
|
|||||||
type ExtendHostFunctions = ();
|
type ExtendHostFunctions = ();
|
||||||
|
|
||||||
fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
|
fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
|
||||||
serai_runtime::api::dispatch(method, data)
|
runtime::api::dispatch(method, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn native_version() -> NativeVersion {
|
fn native_version() -> NativeVersion {
|
||||||
@@ -57,13 +59,13 @@ impl NativeExecutionDispatch for ExecutorDispatch {
|
|||||||
pub struct Cidp;
|
pub struct Cidp;
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl CreateInherentDataProviders<Block, ()> for Cidp {
|
impl CreateInherentDataProviders<Block, ()> for Cidp {
|
||||||
type InherentDataProviders = ();
|
type InherentDataProviders = (InstructionsProvider,);
|
||||||
async fn create_inherent_data_providers(
|
async fn create_inherent_data_providers(
|
||||||
&self,
|
&self,
|
||||||
_: <Block as BlockTrait>::Hash,
|
_: <Block as BlockTrait>::Hash,
|
||||||
_: (),
|
_: (),
|
||||||
) -> Result<Self::InherentDataProviders, Box<dyn Send + Sync + Error>> {
|
) -> Result<Self::InherentDataProviders, Box<dyn Send + Sync + Error>> {
|
||||||
Ok(())
|
Ok((InstructionsProvider::new(),))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,12 @@ all-features = true
|
|||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex-literal = { version = "0.3.4", optional = true }
|
hex-literal = { version = "0.3", optional = true }
|
||||||
|
|
||||||
codec = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
codec = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
sp-version = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-version = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
sp-inherents = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-inherents = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
@@ -42,6 +41,8 @@ pallet-balances = { git = "https://github.com/serai-dex/substrate", default-feat
|
|||||||
pallet-assets = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-assets = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
|
|
||||||
|
in-instructions-pallet = { path = "../in-instructions/pallet", default-features = false }
|
||||||
|
|
||||||
validator-sets-pallet = { path = "../validator-sets/pallet", default-features = false }
|
validator-sets-pallet = { path = "../validator-sets/pallet", default-features = false }
|
||||||
pallet-session = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-session = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
pallet-tendermint = { path = "../tendermint/pallet", default-features = false }
|
pallet-tendermint = { path = "../tendermint/pallet", default-features = false }
|
||||||
@@ -58,7 +59,6 @@ std = [
|
|||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
|
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-application-crypto/std",
|
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
"sp-version/std",
|
"sp-version/std",
|
||||||
"sp-inherents/std",
|
"sp-inherents/std",
|
||||||
@@ -81,6 +81,8 @@ std = [
|
|||||||
"pallet-assets/std",
|
"pallet-assets/std",
|
||||||
"pallet-transaction-payment/std",
|
"pallet-transaction-payment/std",
|
||||||
|
|
||||||
|
"in-instructions-pallet/std",
|
||||||
|
|
||||||
"validator-sets-pallet/std",
|
"validator-sets-pallet/std",
|
||||||
"pallet-session/std",
|
"pallet-session/std",
|
||||||
"pallet-tendermint/std",
|
"pallet-tendermint/std",
|
||||||
|
|||||||
@@ -1,23 +1,46 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||||
|
|
||||||
|
// Re-export all components
|
||||||
|
pub use serai_primitives as primitives;
|
||||||
|
|
||||||
|
pub use frame_system as system;
|
||||||
|
pub use frame_support as support;
|
||||||
|
|
||||||
|
pub use pallet_balances as balances;
|
||||||
|
pub use pallet_transaction_payment as transaction_payment;
|
||||||
|
|
||||||
|
pub use pallet_assets as assets;
|
||||||
|
pub use in_instructions_pallet as in_instructions;
|
||||||
|
|
||||||
|
pub use validator_sets_pallet as validator_sets;
|
||||||
|
|
||||||
|
pub use pallet_session as session;
|
||||||
|
pub use pallet_tendermint as tendermint;
|
||||||
|
|
||||||
|
// Actually used by the runtime
|
||||||
use sp_core::OpaqueMetadata;
|
use sp_core::OpaqueMetadata;
|
||||||
pub use sp_core::sr25519::{Public, Signature};
|
use sp_std::prelude::*;
|
||||||
|
|
||||||
|
use sp_version::RuntimeVersion;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use sp_version::NativeVersion;
|
||||||
|
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
create_runtime_str, generic, impl_opaque_keys, KeyTypeId,
|
create_runtime_str, generic, impl_opaque_keys, KeyTypeId,
|
||||||
traits::{Convert, OpaqueKeys, IdentityLookup, BlakeTwo256, Block as BlockT},
|
traits::{Convert, OpaqueKeys, IdentityLookup, BlakeTwo256, Block as BlockT},
|
||||||
transaction_validity::{TransactionSource, TransactionValidity},
|
transaction_validity::{TransactionSource, TransactionValidity},
|
||||||
ApplyExtrinsicResult, Perbill,
|
ApplyExtrinsicResult, Perbill,
|
||||||
};
|
};
|
||||||
use sp_std::prelude::*;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use sp_version::NativeVersion;
|
|
||||||
use sp_version::RuntimeVersion;
|
|
||||||
|
|
||||||
use frame_support::{
|
use primitives::{PublicKey, Signature, SeraiAddress, Coin};
|
||||||
|
|
||||||
|
use support::{
|
||||||
traits::{ConstU8, ConstU32, ConstU64},
|
traits::{ConstU8, ConstU32, ConstU64},
|
||||||
weights::{
|
weights::{
|
||||||
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
|
constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND},
|
||||||
@@ -25,22 +48,14 @@ use frame_support::{
|
|||||||
},
|
},
|
||||||
parameter_types, construct_runtime,
|
parameter_types, construct_runtime,
|
||||||
};
|
};
|
||||||
pub use frame_system::Call as SystemCall;
|
|
||||||
|
|
||||||
use serai_primitives::Coin;
|
use transaction_payment::CurrencyAdapter;
|
||||||
|
|
||||||
pub use pallet_balances::Call as BalancesCall;
|
use session::PeriodicSessions;
|
||||||
pub use pallet_assets::Call as AssetsCall;
|
|
||||||
use pallet_transaction_payment::CurrencyAdapter;
|
|
||||||
|
|
||||||
use pallet_session::PeriodicSessions;
|
|
||||||
|
|
||||||
/// An index to a block.
|
/// An index to a block.
|
||||||
pub type BlockNumber = u32;
|
pub type BlockNumber = u32;
|
||||||
|
|
||||||
/// Account ID type, equivalent to a public key
|
|
||||||
pub type AccountId = Public;
|
|
||||||
|
|
||||||
/// Balance of an account.
|
/// Balance of an account.
|
||||||
// Distinct from serai-primitives Amount due to Substrate's requirements on this type.
|
// Distinct from serai-primitives Amount due to Substrate's requirements on this type.
|
||||||
// If Amount could be dropped in here, it would be.
|
// If Amount could be dropped in here, it would be.
|
||||||
@@ -58,7 +73,7 @@ pub type Hash = sp_core::H256;
|
|||||||
pub mod opaque {
|
pub mod opaque {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
|
use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
|
||||||
|
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
@@ -110,22 +125,22 @@ parameter_types! {
|
|||||||
pub const SS58Prefix: u8 = 42; // TODO: Remove for Bech32m
|
pub const SS58Prefix: u8 = 42; // TODO: Remove for Bech32m
|
||||||
|
|
||||||
// 1 MB block size limit
|
// 1 MB block size limit
|
||||||
pub BlockLength: frame_system::limits::BlockLength =
|
pub BlockLength: system::limits::BlockLength =
|
||||||
frame_system::limits::BlockLength::max_with_normal_ratio(BLOCK_SIZE, NORMAL_DISPATCH_RATIO);
|
system::limits::BlockLength::max_with_normal_ratio(BLOCK_SIZE, NORMAL_DISPATCH_RATIO);
|
||||||
pub BlockWeights: frame_system::limits::BlockWeights =
|
pub BlockWeights: system::limits::BlockWeights =
|
||||||
frame_system::limits::BlockWeights::with_sensible_defaults(
|
system::limits::BlockWeights::with_sensible_defaults(
|
||||||
Weight::from_ref_time(2u64 * WEIGHT_REF_TIME_PER_SECOND).set_proof_size(u64::MAX),
|
Weight::from_ref_time(2u64 * WEIGHT_REF_TIME_PER_SECOND).set_proof_size(u64::MAX),
|
||||||
NORMAL_DISPATCH_RATIO,
|
NORMAL_DISPATCH_RATIO,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl frame_system::Config for Runtime {
|
impl system::Config for Runtime {
|
||||||
type BaseCallFilter = frame_support::traits::Everything;
|
type BaseCallFilter = support::traits::Everything;
|
||||||
type BlockWeights = BlockWeights;
|
type BlockWeights = BlockWeights;
|
||||||
type BlockLength = BlockLength;
|
type BlockLength = BlockLength;
|
||||||
type AccountId = AccountId;
|
type AccountId = SeraiAddress;
|
||||||
type RuntimeCall = RuntimeCall;
|
type RuntimeCall = RuntimeCall;
|
||||||
type Lookup = IdentityLookup<AccountId>;
|
type Lookup = IdentityLookup<SeraiAddress>;
|
||||||
type Index = Index;
|
type Index = Index;
|
||||||
type BlockNumber = BlockNumber;
|
type BlockNumber = BlockNumber;
|
||||||
type Hash = Hash;
|
type Hash = Hash;
|
||||||
@@ -142,14 +157,14 @@ impl frame_system::Config for Runtime {
|
|||||||
type OnKilledAccount = ();
|
type OnKilledAccount = ();
|
||||||
type OnSetCode = ();
|
type OnSetCode = ();
|
||||||
|
|
||||||
type AccountData = pallet_balances::AccountData<Balance>;
|
type AccountData = balances::AccountData<Balance>;
|
||||||
type SystemWeightInfo = ();
|
type SystemWeightInfo = ();
|
||||||
type SS58Prefix = SS58Prefix; // TODO: Remove for Bech32m
|
type SS58Prefix = SS58Prefix; // TODO: Remove for Bech32m
|
||||||
|
|
||||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
type MaxConsumers = support::traits::ConstU32<16>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_balances::Config for Runtime {
|
impl balances::Config for Runtime {
|
||||||
type MaxLocks = ConstU32<50>;
|
type MaxLocks = ConstU32<50>;
|
||||||
type MaxReserves = ();
|
type MaxReserves = ();
|
||||||
type ReserveIdentifier = [u8; 8];
|
type ReserveIdentifier = [u8; 8];
|
||||||
@@ -158,10 +173,10 @@ impl pallet_balances::Config for Runtime {
|
|||||||
type DustRemoval = ();
|
type DustRemoval = ();
|
||||||
type ExistentialDeposit = ConstU64<500>;
|
type ExistentialDeposit = ConstU64<500>;
|
||||||
type AccountStore = System;
|
type AccountStore = System;
|
||||||
type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
|
type WeightInfo = balances::weights::SubstrateWeight<Runtime>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_assets::Config for Runtime {
|
impl assets::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
type Balance = Balance;
|
type Balance = Balance;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
@@ -171,9 +186,8 @@ impl pallet_assets::Config for Runtime {
|
|||||||
type StringLimit = ConstU32<32>;
|
type StringLimit = ConstU32<32>;
|
||||||
|
|
||||||
// Don't allow anyone to create assets
|
// Don't allow anyone to create assets
|
||||||
type CreateOrigin =
|
type CreateOrigin = support::traits::AsEnsureOriginWithArg<system::EnsureNever<SeraiAddress>>;
|
||||||
frame_support::traits::AsEnsureOriginWithArg<frame_system::EnsureNever<AccountId>>;
|
type ForceOrigin = system::EnsureRoot<SeraiAddress>;
|
||||||
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
|
|
||||||
|
|
||||||
// Don't charge fees nor kill accounts
|
// Don't charge fees nor kill accounts
|
||||||
type RemoveItemsLimit = ConstU32<0>;
|
type RemoveItemsLimit = ConstU32<0>;
|
||||||
@@ -188,12 +202,12 @@ impl pallet_assets::Config for Runtime {
|
|||||||
type Freezer = ();
|
type Freezer = ();
|
||||||
type Extra = ();
|
type Extra = ();
|
||||||
|
|
||||||
type WeightInfo = pallet_assets::weights::SubstrateWeight<Runtime>;
|
type WeightInfo = assets::weights::SubstrateWeight<Runtime>;
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
type BenchmarkHelper = ();
|
type BenchmarkHelper = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_transaction_payment::Config for Runtime {
|
impl transaction_payment::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
|
type OnChargeTransaction = CurrencyAdapter<Balances, ()>;
|
||||||
type OperationalFeeMultiplier = ConstU8<5>;
|
type OperationalFeeMultiplier = ConstU8<5>;
|
||||||
@@ -202,54 +216,57 @@ impl pallet_transaction_payment::Config for Runtime {
|
|||||||
type FeeMultiplierUpdate = ();
|
type FeeMultiplierUpdate = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl in_instructions::Config for Runtime {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
}
|
||||||
|
|
||||||
const SESSION_LENGTH: BlockNumber = 5 * DAYS;
|
const SESSION_LENGTH: BlockNumber = 5 * DAYS;
|
||||||
type Sessions = PeriodicSessions<ConstU32<{ SESSION_LENGTH }>, ConstU32<{ SESSION_LENGTH }>>;
|
type Sessions = PeriodicSessions<ConstU32<{ SESSION_LENGTH }>, ConstU32<{ SESSION_LENGTH }>>;
|
||||||
|
|
||||||
pub struct IdentityValidatorIdOf;
|
pub struct IdentityValidatorIdOf;
|
||||||
impl Convert<Public, Option<Public>> for IdentityValidatorIdOf {
|
impl Convert<PublicKey, Option<PublicKey>> for IdentityValidatorIdOf {
|
||||||
fn convert(key: Public) -> Option<Public> {
|
fn convert(key: PublicKey) -> Option<PublicKey> {
|
||||||
Some(key)
|
Some(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl validator_sets_pallet::Config for Runtime {
|
impl validator_sets::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_session::Config for Runtime {
|
impl session::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
type ValidatorId = AccountId;
|
type ValidatorId = SeraiAddress;
|
||||||
type ValidatorIdOf = IdentityValidatorIdOf;
|
type ValidatorIdOf = IdentityValidatorIdOf;
|
||||||
type ShouldEndSession = Sessions;
|
type ShouldEndSession = Sessions;
|
||||||
type NextSessionRotation = Sessions;
|
type NextSessionRotation = Sessions;
|
||||||
type SessionManager = ();
|
type SessionManager = ();
|
||||||
type SessionHandler = <SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
|
type SessionHandler = <SessionKeys as OpaqueKeys>::KeyTypeIdProviders;
|
||||||
type Keys = SessionKeys;
|
type Keys = SessionKeys;
|
||||||
type WeightInfo = pallet_session::weights::SubstrateWeight<Runtime>;
|
type WeightInfo = session::weights::SubstrateWeight<Runtime>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pallet_tendermint::Config for Runtime {}
|
impl tendermint::Config for Runtime {}
|
||||||
|
|
||||||
pub type Address = AccountId;
|
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
pub type SignedExtra = (
|
pub type SignedExtra = (
|
||||||
frame_system::CheckNonZeroSender<Runtime>,
|
system::CheckNonZeroSender<Runtime>,
|
||||||
frame_system::CheckSpecVersion<Runtime>,
|
system::CheckSpecVersion<Runtime>,
|
||||||
frame_system::CheckTxVersion<Runtime>,
|
system::CheckTxVersion<Runtime>,
|
||||||
frame_system::CheckGenesis<Runtime>,
|
system::CheckGenesis<Runtime>,
|
||||||
frame_system::CheckEra<Runtime>,
|
system::CheckEra<Runtime>,
|
||||||
frame_system::CheckNonce<Runtime>,
|
system::CheckNonce<Runtime>,
|
||||||
frame_system::CheckWeight<Runtime>,
|
system::CheckWeight<Runtime>,
|
||||||
pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
|
transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||||
);
|
);
|
||||||
pub type UncheckedExtrinsic =
|
pub type UncheckedExtrinsic =
|
||||||
generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
|
generic::UncheckedExtrinsic<SeraiAddress, RuntimeCall, Signature, SignedExtra>;
|
||||||
pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;
|
pub type SignedPayload = generic::SignedPayload<RuntimeCall, SignedExtra>;
|
||||||
pub type Executive = frame_executive::Executive<
|
pub type Executive = frame_executive::Executive<
|
||||||
Runtime,
|
Runtime,
|
||||||
Block,
|
Block,
|
||||||
frame_system::ChainContext<Runtime>,
|
system::ChainContext<Runtime>,
|
||||||
Runtime,
|
Runtime,
|
||||||
AllPalletsWithSystem,
|
AllPalletsWithSystem,
|
||||||
>;
|
>;
|
||||||
@@ -260,14 +277,18 @@ construct_runtime!(
|
|||||||
NodeBlock = Block,
|
NodeBlock = Block,
|
||||||
UncheckedExtrinsic = UncheckedExtrinsic
|
UncheckedExtrinsic = UncheckedExtrinsic
|
||||||
{
|
{
|
||||||
System: frame_system,
|
System: system,
|
||||||
Balances: pallet_balances,
|
|
||||||
Assets: pallet_assets,
|
|
||||||
TransactionPayment: pallet_transaction_payment,
|
|
||||||
|
|
||||||
ValidatorSets: validator_sets_pallet,
|
Balances: balances,
|
||||||
Session: pallet_session,
|
TransactionPayment: transaction_payment,
|
||||||
Tendermint: pallet_tendermint,
|
|
||||||
|
Assets: assets,
|
||||||
|
InInstructions: in_instructions,
|
||||||
|
|
||||||
|
ValidatorSets: validator_sets,
|
||||||
|
|
||||||
|
Session: session,
|
||||||
|
Tendermint: tendermint,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -279,8 +300,8 @@ extern crate frame_benchmarking;
|
|||||||
mod benches {
|
mod benches {
|
||||||
define_benchmarks!(
|
define_benchmarks!(
|
||||||
[frame_benchmarking, BaselineBench::<Runtime>]
|
[frame_benchmarking, BaselineBench::<Runtime>]
|
||||||
[frame_system, SystemBench::<Runtime>]
|
[system, SystemBench::<Runtime>]
|
||||||
[pallet_balances, Balances]
|
[balances, Balances]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,13 +380,13 @@ sp_api::impl_runtime_apis! {
|
|||||||
Tendermint::session()
|
Tendermint::session()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validators() -> Vec<Public> {
|
fn validators() -> Vec<PublicKey> {
|
||||||
Session::validators()
|
Session::validators()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
|
impl frame_system_rpc_runtime_api::AccountNonceApi<Block, SeraiAddress, Index> for Runtime {
|
||||||
fn account_nonce(account: AccountId) -> Index {
|
fn account_nonce(account: SeraiAddress) -> Index {
|
||||||
System::account_nonce(account)
|
System::account_nonce(account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,10 +401,11 @@ sp_api::impl_runtime_apis! {
|
|||||||
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
|
) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
|
||||||
TransactionPayment::query_info(uxt, len)
|
TransactionPayment::query_info(uxt, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_fee_details(
|
fn query_fee_details(
|
||||||
uxt: <Block as BlockT>::Extrinsic,
|
uxt: <Block as BlockT>::Extrinsic,
|
||||||
len: u32,
|
len: u32,
|
||||||
) -> pallet_transaction_payment::FeeDetails<Balance> {
|
) -> transaction_payment::FeeDetails<Balance> {
|
||||||
TransactionPayment::query_fee_details(uxt, len)
|
TransactionPayment::query_fee_details(uxt, len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
substrate/serai/client/Cargo.toml
Normal file
33
substrate/serai/client/Cargo.toml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
[package]
|
||||||
|
name = "serai-client"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Client library for the Serai network"
|
||||||
|
license = "AGPL-3.0-only"
|
||||||
|
repository = "https://github.com/serai-dex/serai/tree/develop/client"
|
||||||
|
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||||
|
keywords = ["serai"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "1"
|
||||||
|
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
|
scale = { package = "parity-scale-codec", version = "3" }
|
||||||
|
scale-value = "0.6"
|
||||||
|
subxt = "0.25"
|
||||||
|
|
||||||
|
serai-primitives = { path = "../primitives", version = "0.1" }
|
||||||
|
in-instructions-primitives = { path = "../../in-instructions/primitives", version = "0.1" }
|
||||||
|
serai-runtime = { path = "../../runtime", version = "0.1" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
lazy_static = "1"
|
||||||
|
|
||||||
|
tokio = "1"
|
||||||
|
|
||||||
|
jsonrpsee-server = "0.16"
|
||||||
15
substrate/serai/client/LICENSE
Normal file
15
substrate/serai/client/LICENSE
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
AGPL-3.0-only license
|
||||||
|
|
||||||
|
Copyright (c) 2022-2023 Luke Parker
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License Version 3 as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
49
substrate/serai/client/src/in_instructions.rs
Normal file
49
substrate/serai/client/src/in_instructions.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use scale::Decode;
|
||||||
|
|
||||||
|
use serai_runtime::{
|
||||||
|
support::traits::PalletInfo as PalletInfoTrait, PalletInfo, in_instructions, InInstructions,
|
||||||
|
Runtime,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use in_instructions_primitives as primitives;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
primitives::{Coin, BlockNumber},
|
||||||
|
Serai, SeraiError,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PALLET: &str = "InInstructions";
|
||||||
|
|
||||||
|
pub type InInstructionsEvent = in_instructions::Event<Runtime>;
|
||||||
|
|
||||||
|
impl Serai {
|
||||||
|
pub async fn get_batch_events(
|
||||||
|
&self,
|
||||||
|
block: [u8; 32],
|
||||||
|
) -> Result<Vec<InInstructionsEvent>, SeraiError> {
|
||||||
|
let mut res = vec![];
|
||||||
|
for event in
|
||||||
|
self.0.events().at(Some(block.into())).await.map_err(|_| SeraiError::RpcError)?.iter()
|
||||||
|
{
|
||||||
|
let event = event.map_err(|_| SeraiError::InvalidRuntime)?;
|
||||||
|
if PalletInfo::index::<InInstructions>().unwrap() == usize::from(event.pallet_index()) {
|
||||||
|
let mut with_variant: &[u8] =
|
||||||
|
&[[event.variant_index()].as_ref(), event.field_bytes()].concat();
|
||||||
|
let event =
|
||||||
|
InInstructionsEvent::decode(&mut with_variant).map_err(|_| SeraiError::InvalidRuntime)?;
|
||||||
|
if matches!(event, InInstructionsEvent::Batch { .. }) {
|
||||||
|
res.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_coin_block_number(
|
||||||
|
&self,
|
||||||
|
coin: Coin,
|
||||||
|
block: [u8; 32],
|
||||||
|
) -> Result<BlockNumber, SeraiError> {
|
||||||
|
Ok(self.storage(PALLET, "BlockNumbers", Some(coin), block).await?.unwrap_or(BlockNumber(0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
77
substrate/serai/client/src/lib.rs
Normal file
77
substrate/serai/client/src/lib.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
use scale::Decode;
|
||||||
|
|
||||||
|
use subxt::{tx::BaseExtrinsicParams, Config as SubxtConfig, OnlineClient};
|
||||||
|
|
||||||
|
pub use serai_primitives as primitives;
|
||||||
|
use primitives::{Signature, SeraiAddress};
|
||||||
|
|
||||||
|
use serai_runtime::{system::Config, Runtime};
|
||||||
|
|
||||||
|
pub mod in_instructions;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
pub(crate) struct SeraiConfig;
|
||||||
|
impl SubxtConfig for SeraiConfig {
|
||||||
|
type BlockNumber = <Runtime as Config>::BlockNumber;
|
||||||
|
|
||||||
|
type Hash = <Runtime as Config>::Hash;
|
||||||
|
type Hashing = <Runtime as Config>::Hashing;
|
||||||
|
|
||||||
|
type Index = <Runtime as Config>::Index;
|
||||||
|
type AccountId = <Runtime as Config>::AccountId;
|
||||||
|
// TODO: Bech32m
|
||||||
|
type Address = SeraiAddress;
|
||||||
|
|
||||||
|
type Header = <Runtime as Config>::Header;
|
||||||
|
type Signature = Signature;
|
||||||
|
|
||||||
|
type ExtrinsicParams = BaseExtrinsicParams<SeraiConfig, ()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Error, Debug)]
|
||||||
|
pub enum SeraiError {
|
||||||
|
#[error("failed to connect to serai")]
|
||||||
|
RpcError,
|
||||||
|
#[error("serai-client library was intended for a different runtime version")]
|
||||||
|
InvalidRuntime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Serai(OnlineClient<SeraiConfig>);
|
||||||
|
|
||||||
|
impl Serai {
|
||||||
|
pub async fn new(url: &str) -> Result<Self, SeraiError> {
|
||||||
|
Ok(Serai(OnlineClient::<SeraiConfig>::from_url(url).await.map_err(|_| SeraiError::RpcError)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn storage<K: Serialize, R: Decode>(
|
||||||
|
&self,
|
||||||
|
pallet: &'static str,
|
||||||
|
name: &'static str,
|
||||||
|
key: Option<K>,
|
||||||
|
block: [u8; 32],
|
||||||
|
) -> Result<Option<R>, SeraiError> {
|
||||||
|
let mut keys = vec![];
|
||||||
|
if let Some(key) = key {
|
||||||
|
keys.push(scale_value::serde::to_value(key).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let storage = self.0.storage();
|
||||||
|
let address = subxt::dynamic::storage(pallet, name, keys);
|
||||||
|
debug_assert!(storage.validate(&address).is_ok());
|
||||||
|
|
||||||
|
storage
|
||||||
|
.fetch(&address, Some(block.into()))
|
||||||
|
.await
|
||||||
|
.map_err(|_| SeraiError::RpcError)?
|
||||||
|
.map(|res| R::decode(&mut res.encoded()).map_err(|_| SeraiError::InvalidRuntime))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_latest_block_hash(&self) -> Result<[u8; 32], SeraiError> {
|
||||||
|
Ok(self.0.rpc().finalized_head().await.map_err(|_| SeraiError::RpcError)?.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
50
substrate/serai/client/tests/runner.rs
Normal file
50
substrate/serai/client/tests/runner.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
pub const URL: &str = "ws://127.0.0.1:9944";
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SEQUENTIAL: Mutex<()> = Mutex::new(());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! serai_test {
|
||||||
|
($(async fn $name: ident() $body: block)*) => {
|
||||||
|
$(
|
||||||
|
#[tokio::test]
|
||||||
|
async fn $name() {
|
||||||
|
let guard = runner::SEQUENTIAL.lock().await;
|
||||||
|
|
||||||
|
// Spawn a fresh Serai node
|
||||||
|
let mut command = {
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{path::Path, process::Command};
|
||||||
|
|
||||||
|
let node = {
|
||||||
|
let this_crate = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
let top_level = this_crate.join("../../../");
|
||||||
|
top_level.join("target/debug/serai-node")
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = Command::new(node).arg("--dev").spawn().unwrap();
|
||||||
|
while Serai::new(URL).await.is_err() {
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
command
|
||||||
|
};
|
||||||
|
|
||||||
|
let local = tokio::task::LocalSet::new();
|
||||||
|
local.run_until(async move {
|
||||||
|
if let Err(err) = tokio::task::spawn_local(async move { $body }).await {
|
||||||
|
drop(guard);
|
||||||
|
let _ = command.kill();
|
||||||
|
Err(err).unwrap()
|
||||||
|
} else {
|
||||||
|
command.kill().unwrap();
|
||||||
|
}
|
||||||
|
}).await;
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
60
substrate/serai/client/tests/updates.rs
Normal file
60
substrate/serai/client/tests/updates.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
use serai_runtime::in_instructions::{Batch, Update};
|
||||||
|
|
||||||
|
use jsonrpsee_server::RpcModule;
|
||||||
|
|
||||||
|
use serai_client::{
|
||||||
|
primitives::{BlockNumber, BlockHash, SeraiAddress, BITCOIN},
|
||||||
|
in_instructions::{primitives::InInstruction, InInstructionsEvent},
|
||||||
|
Serai,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod runner;
|
||||||
|
use runner::URL;
|
||||||
|
|
||||||
|
serai_test!(
|
||||||
|
async fn publish_update() {
|
||||||
|
let mut rpc = RpcModule::new(());
|
||||||
|
rpc
|
||||||
|
.register_async_method("processor_coinUpdates", |_, _| async move {
|
||||||
|
let batch = Batch {
|
||||||
|
id: BlockHash([0xaa; 32]),
|
||||||
|
instructions: vec![InInstruction::Transfer(SeraiAddress::from_raw([0xff; 32]))],
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(vec![Some(Update { block_number: BlockNumber(123), batches: vec![batch] })])
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let _handle = jsonrpsee_server::ServerBuilder::default()
|
||||||
|
.build("127.0.0.1:5134")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.start(rpc)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let serai = Serai::new(URL).await.unwrap();
|
||||||
|
loop {
|
||||||
|
let latest = serai.get_latest_block_hash().await.unwrap();
|
||||||
|
let batches = serai.get_batch_events(latest).await.unwrap();
|
||||||
|
if let Some(batch) = batches.get(0) {
|
||||||
|
match batch {
|
||||||
|
InInstructionsEvent::Batch { coin, id } => {
|
||||||
|
assert_eq!(coin, &BITCOIN);
|
||||||
|
assert_eq!(id, &BlockHash([0xaa; 32]));
|
||||||
|
assert_eq!(
|
||||||
|
serai.get_coin_block_number(BITCOIN, latest).await.unwrap(),
|
||||||
|
BlockNumber(123)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => panic!("get_batches returned non-batch"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(50)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -15,11 +15,10 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["scale/std", "scale-info/std", "serde", "sp-core/std", "sp-std/std"]
|
std = ["scale/std", "scale-info/std", "serde", "sp-core/std"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use serde::{Serialize, Deserialize};
|
|||||||
|
|
||||||
/// The type used for amounts.
|
/// The type used for amounts.
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
|
Clone, Copy, PartialEq, Eq, PartialOrd, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||||
)]
|
)]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
pub struct Amount(pub u64);
|
pub struct Amount(pub u64);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use scale_info::TypeInfo;
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
/// The type used to identify coins.
|
/// The type used to identify coins.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
pub struct Coin(pub u32);
|
pub struct Coin(pub u32);
|
||||||
impl From<u32> for Coin {
|
impl From<u32> for Coin {
|
||||||
@@ -13,7 +13,8 @@ impl From<u32> for Coin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const BITCOIN: Coin = Coin(0);
|
pub const SERAI: Coin = Coin(0);
|
||||||
pub const ETHER: Coin = Coin(1);
|
pub const BITCOIN: Coin = Coin(1);
|
||||||
pub const DAI: Coin = Coin(2);
|
pub const ETHER: Coin = Coin(2);
|
||||||
pub const MONERO: Coin = Coin(3);
|
pub const DAI: Coin = Coin(3);
|
||||||
|
pub const MONERO: Coin = Coin(4);
|
||||||
|
|||||||
@@ -1,7 +1,63 @@
|
|||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
use scale::{Encode, Decode, MaxEncodedLen};
|
||||||
|
use scale_info::TypeInfo;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use sp_core::{
|
||||||
|
H256,
|
||||||
|
sr25519::{Public, Signature as RistrettoSignature},
|
||||||
|
};
|
||||||
|
|
||||||
mod amount;
|
mod amount;
|
||||||
pub use amount::*;
|
pub use amount::*;
|
||||||
|
|
||||||
mod coins;
|
mod coins;
|
||||||
pub use coins::*;
|
pub use coins::*;
|
||||||
|
|
||||||
|
pub type PublicKey = Public;
|
||||||
|
pub type SeraiAddress = PublicKey;
|
||||||
|
pub type Signature = RistrettoSignature;
|
||||||
|
|
||||||
|
/// The type used to identify block numbers.
|
||||||
|
// Doesn't re-export tendermint-machine's due to traits.
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Default, PartialEq, Eq, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
|
pub struct BlockNumber(pub u32);
|
||||||
|
impl From<u32> for BlockNumber {
|
||||||
|
fn from(number: u32) -> 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(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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ sp-consensus = { git = "https://github.com/serai-dex/substrate" }
|
|||||||
|
|
||||||
sp-tendermint = { path = "../primitives" }
|
sp-tendermint = { path = "../primitives" }
|
||||||
|
|
||||||
sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
sc-executor = { git = "https://github.com/serai-dex/substrate" }
|
|
||||||
sc-network-common = { git = "https://github.com/serai-dex/substrate" }
|
sc-network-common = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-network = { git = "https://github.com/serai-dex/substrate" }
|
sc-network = { git = "https://github.com/serai-dex/substrate" }
|
||||||
sc-network-gossip = { git = "https://github.com/serai-dex/substrate" }
|
sc-network-gossip = { git = "https://github.com/serai-dex/substrate" }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ thiserror = "1"
|
|||||||
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
parity-scale-codec = { version = "3.2", features = ["derive"] }
|
parity-scale-codec = { version = "3", features = ["derive"] }
|
||||||
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
tokio = { version = "1", features = ["macros", "sync", "time", "rt"] }
|
tokio = { version = "1", features = ["macros", "sync", "time", "rt"] }
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ pub mod pallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::genesis_config]
|
#[pallet::genesis_config]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen)]
|
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||||
pub struct GenesisConfig<T: Config> {
|
pub struct GenesisConfig<T: Config> {
|
||||||
/// Bond requirement to join the initial validator set.
|
/// Bond requirement to join the initial validator set.
|
||||||
/// Every participant at genesis will automatically be assumed to have this much bond.
|
/// Every participant at genesis will automatically be assumed to have this much bond.
|
||||||
/// This bond cannot be withdrawn however as there's no stake behind it.
|
/// This bond cannot be withdrawn however as there's no stake behind it.
|
||||||
pub bond: Amount,
|
pub bond: Amount,
|
||||||
/// Amount of coins to spawn the network with in the initial validator set.
|
/// Coins to spawn the network with in the initial validator set.
|
||||||
pub coins: Coin,
|
pub coins: Vec<Coin>,
|
||||||
/// List of participants to place in the genesis set.
|
/// List of participants to place in the genesis set.
|
||||||
pub participants: Vec<T::AccountId>,
|
pub participants: Vec<T::AccountId>,
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ pub mod pallet {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl<T: Config> Default for GenesisConfig<T> {
|
impl<T: Config> Default for GenesisConfig<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
GenesisConfig { bond: Amount(1), coins: Coin(0), participants: vec![] }
|
GenesisConfig { bond: Amount(1), coins: vec![], participants: vec![] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,11 +95,6 @@ pub mod pallet {
|
|||||||
#[pallet::genesis_build]
|
#[pallet::genesis_build]
|
||||||
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
|
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
|
||||||
fn build(&self) {
|
fn build(&self) {
|
||||||
let mut coins = Vec::new();
|
|
||||||
for coin in 0 .. self.coins.0 {
|
|
||||||
coins.push(Coin(coin));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut participants = Vec::new();
|
let mut participants = Vec::new();
|
||||||
for participant in self.participants.clone() {
|
for participant in self.participants.clone() {
|
||||||
participants.push((participant, self.bond));
|
participants.push((participant, self.bond));
|
||||||
@@ -109,7 +104,7 @@ pub mod pallet {
|
|||||||
ValidatorSetInstance(Session(0), ValidatorSetIndex(0)),
|
ValidatorSetInstance(Session(0), ValidatorSetIndex(0)),
|
||||||
Some(ValidatorSet {
|
Some(ValidatorSet {
|
||||||
bond: self.bond,
|
bond: self.bond,
|
||||||
coins: BoundedVec::try_from(coins).unwrap(),
|
coins: BoundedVec::try_from(self.coins.clone()).unwrap(),
|
||||||
participants: BoundedVec::try_from(participants).unwrap(),
|
participants: BoundedVec::try_from(participants).unwrap(),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,11 +15,8 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["scale/std", "scale-info/std", "serde", "sp-core/std", "sp-std/std"]
|
std = ["scale/std", "scale-info/std", "serde"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
Reference in New Issue
Block a user