44 Commits

Author SHA1 Message Date
Luke Parker
d304cd97e1 Merge branch 'next' into next-polkadot-sdk 2025-09-06 04:26:10 -04:00
Luke Parker
2b56dcdf3f Update patch-polkadot-sdk for bug fixes, removal of is-terminal
Adds a deny entry for `is-terminal` to stop it from secretly reappearing.

Restores the `is-terminal` patch for `is_terminal_polyfill` to have one less
external dependency.
2025-09-06 04:25:21 -04:00
Luke Parker
90804c4c30 Update deny.toml 2025-09-05 14:08:04 -04:00
Luke Parker
46caca2f51 Update patch-polkadot-sdk to remove scale_info 2025-09-05 14:07:52 -04:00
Luke Parker
2077e485bb Add borsh impls for SignedEmbeddedEllipticCurveKeys 2025-09-05 07:21:07 -04:00
Luke Parker
28dbef8a1c Update to the latest patch-polkadot-sdk
Removes several dependencies.
2025-09-05 06:57:30 -04:00
Luke Parker
3541197aa5 Merge branch 'next' into next-polkadot-sdk 2025-09-03 16:44:26 -04:00
Luke Parker
a2209dd6ff Misc clippy fixes 2025-09-03 06:10:54 -04:00
Luke Parker
2032cf355f Expose coins::Pallet::transfer_internal as transfer_fn
It is safe to call and assumes no preconditions.
2025-09-03 00:48:17 -04:00
Luke Parker
fe41b09fd4 Properly handle the error in validator-sets 2025-09-02 11:07:45 -04:00
Luke Parker
74bad049a7 Add abstraction for the embedded elliptic curve keys
It's minimal but still pleasant.
2025-09-02 10:42:06 -04:00
Luke Parker
72fefb3d85 Strongly type EmbeddedEllipticCurveKeys
Adds a signed variant to validate knowledge and ownership.

Add SCALE derivations for `EmbeddedEllipticCurveKeys`
2025-09-02 10:42:02 -04:00
Luke Parker
200c1530a4 WIP changes to validator-sets
Actually use the added `Allocations` abstraction

Start using the sessions API in the validator-sets pallet

Get a `substrate/validator-sets` approximate to compiling
2025-09-02 10:41:58 -04:00
Luke Parker
5736b87b57 Remove final references to scale in coordinator/processor
Slight tweaks to processor
2025-09-02 10:41:55 -04:00
Luke Parker
ada94e8c5d Get all processors to compile again
Requires splitting `serai-cosign` into `serai-cosign` and `serai-cosign-types`
so the processor don't require `serai-client/serai` (not correct yet).
2025-09-02 02:17:10 -04:00
Luke Parker
75240ed327 Update serai-message-queue to the new serai-primitives 2025-09-02 02:17:10 -04:00
Luke Parker
6177cf5c07 Have serai-runtime compile again 2025-09-02 02:17:10 -04:00
Luke Parker
0d38dc96b6 Use serai-primitives, not serai-client, when possible in coordinator/*
Also updates `serai-coordinator-tributary` to prefer `borsh` to SCALE.
2025-09-02 02:17:10 -04:00
Luke Parker
e8094523ff Use borsh instead of SCALE within tendermint-machine, tributary-sdk
Not only does this follow our general practice, the latest SCALE has a
possibly-lossy truncation in its current implementation for `enum`s I'd like to
avoid without simply silencing.
2025-09-02 02:17:09 -04:00
Luke Parker
53a64bc7e2 Update serai-abi, and dependencies, to patch-polkadot-sdk 2025-09-02 02:17:09 -04:00
Luke Parker
3c6e889732 Update Cargo.lock after rebase 2025-08-30 19:36:46 -04:00
Luke Parker
354efc0192 Add deallocate function to validator-sets session abstraction 2025-08-30 18:34:20 -04:00
Luke Parker
e20058feae Add a Sessions abstraction for validator-sets storage 2025-08-30 18:34:20 -04:00
Luke Parker
09f0714894 Add a dedicated Allocations struct for managing validator set allocations
Part of the DB abstraction necessary for this spaghetti.
2025-08-30 18:34:15 -04:00
Luke Parker
d3d539553c Restore the coins pallet to the runtime 2025-08-30 18:32:26 -04:00
Luke Parker
b08ae8e6a7 Add a non-canonical SCALE derivations feature
Enables representing IUMT within `StorageValues`. Applied to a variety of
values.

Fixes a bug where `Some([0; 32])` would be considered a valid block anchor.
2025-08-30 18:32:21 -04:00
Luke Parker
35db2924b4 Populate UnbalancedMerkleTrees in headers 2025-08-30 18:32:20 -04:00
Luke Parker
bfff823bf7 Add an UnbalancedMerkleTree primitive
The reasoning for it is documented with itself. The plan is to use it within
our header for committing to the DAG (allowing one header per epoch, yet
logarithmic proofs for any header within the epoch), the transactions
commitment (allowing logarithmic proofs of a transaction within a block,
without padding), and the events commitment (allowing logarithmic proofs of
unique events within a block, despite events not having a unique ID inherent).

This also defines transaction hashes and performs the necessary modifications
for transactions to be unique.
2025-08-30 18:32:16 -04:00
Luke Parker
352af85498 Use borsh entirely in create_db 2025-08-30 18:32:07 -04:00
Luke Parker
ecad89b269 Remove now-consolidated primitives crates 2025-08-30 18:32:06 -04:00
Luke Parker
48f5ed71d7 Skeleton ruintime with new types 2025-08-30 18:30:38 -04:00
Luke Parker
ed9cbdd8e0 Have apply return Ok even if calls failed
This ensures fees are paid, and block building isn't interrupted, even for TXs
which error.
2025-08-30 18:27:23 -04:00
Luke Parker
0ac11defcc Serialize BoundedVec not with a u32 length, but the minimum-viable uN where N%8==0
This does break borsh's definition of a Vec EXCEPT if the BoundedVec is
considered an enum. For sufficiently low bounds, this is viable, though it
requires automated code generation to be sane.
2025-08-30 18:27:23 -04:00
Luke Parker
24e89316d5 Correct distinction/flow of check/validate/apply 2025-08-30 18:27:23 -04:00
Luke Parker
3f03dac050 Make transaction an enum of Unsigned, Signed 2025-08-30 18:27:23 -04:00
Luke Parker
820b710928 Remove RuntimeCall from Transaction
I believe this was originally here as we needed to return a reference, not an
owned instance, so this caching enabled returning a reference? Regardless, it
isn't valuable now.
2025-08-30 18:27:23 -04:00
Luke Parker
88c7ae3e7d Add traits necessary for serai_abi::Transaction to be usable in-runtime 2025-08-30 18:27:22 -04:00
Luke Parker
dd5e43760d Add the UNIX timestamp (in milliseconds to the block
This is read from the BABE pre-digest when converting from a SubstrateHeader.
This causes the genesis block to have time 0 and all blocks produced with BABE
to have a time of the slot time. While the slot time is in 6-second intervals
(due to our target block time), defining in milliseconds preserves the ABI for
long-term goals (sub-second blocks).

Usage of the slot time deduplicates this field with BABE, and leaves the only
possible manipulation to propose during a slot or to not propose during a slot.

The actual reason this was implemented this way is because the Header trait is
overly restrictive and doesn't allow definition with new fields. Even if we
wanted to express the timestamp within the SubstrateHeader, we can't without
replacing Header::new and making a variety of changes to the polkadot-sdk
accordingly. Those aren't worth it at this moment compared to the solution
implemented.
2025-08-30 18:27:09 -04:00
Luke Parker
776e417fd2 Redo primitives, abi
Consolidates all primitives into a single crate. We didn't benefit from its
fragmentation. I'm hesitant to say the new internal-organization is better (it
may be just as clunky), but it's at least in a single crate (not spread out
over micro-crates).

The ABI is the most distinct. We now entirely own it. Block header hashes don't
directly commit to any BABE data (avoiding potentially ~4 KB headers upon
session changes), and are hashed as borsh (a more widely used codec than
SCALE). There are still Substrate variants, using SCALE and with the BABE data,
but they're prunable from a protocol design perspective.

Defines a transaction as a Vec of Calls, allowing atomic operations.
2025-08-30 18:26:37 -04:00
Luke Parker
2f8ce15a92 Update deny, rust-src component 2025-08-30 18:25:02 -04:00
Luke Parker
af56304676 Update the git tags
Does no actual migration work. This allows establishing the difference in
dependencies between substrate and polkadot-sdk/substrate.
2025-08-30 18:23:49 -04:00
Luke Parker
62a2c4f20e Update nightly version 2025-08-30 18:22:48 -04:00
Luke Parker
c69841710a Remove unnecessary to_string for clone 2025-08-30 18:08:08 -04:00
Luke Parker
3158590675 Remove unused patch for parking_lot_core 2025-08-30 16:20:29 -04:00
249 changed files with 7830 additions and 7805 deletions

View File

@@ -69,7 +69,7 @@ jobs:
uses: ./.github/actions/build-dependencies
- name: Buld Rust docs
run: |
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32v1-none -c rust-docs
rustup toolchain install ${{ steps.nightly.outputs.version }} --profile minimal -t wasm32v1-none -c rust-docs -c rust-src
RUSTDOCFLAGS="--cfg docsrs" cargo +${{ steps.nightly.outputs.version }} doc --workspace --all-features
mv target/doc docs/_site/rust

View File

@@ -61,6 +61,7 @@ jobs:
-p serai-monero-processor \
-p tendermint-machine \
-p tributary-sdk \
-p serai-cosign-types \
-p serai-cosign \
-p serai-coordinator-substrate \
-p serai-coordinator-tributary \

4226
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,6 @@
[workspace]
resolver = "2"
members = [
# Version patches
"patches/parking_lot",
"patches/zstd",
"patches/rocksdb",
# std patches
"patches/matches",
"patches/is-terminal",
# Rewrites/redirects
"patches/option-ext",
"patches/directories-next",
@@ -89,6 +80,7 @@ members = [
"coordinator/tributary-sdk/tendermint",
"coordinator/tributary-sdk",
"coordinator/cosign/types",
"coordinator/cosign",
"coordinator/substrate",
"coordinator/tributary",
@@ -97,31 +89,17 @@ members = [
"coordinator",
"substrate/primitives",
"substrate/coins/primitives",
"substrate/coins/pallet",
"substrate/dex/pallet",
"substrate/validator-sets/primitives",
"substrate/validator-sets/pallet",
"substrate/genesis-liquidity/primitives",
"substrate/genesis-liquidity/pallet",
"substrate/emissions/primitives",
"substrate/emissions/pallet",
"substrate/economic-security/pallet",
"substrate/in-instructions/primitives",
"substrate/in-instructions/pallet",
"substrate/signals/primitives",
"substrate/signals/pallet",
"substrate/abi",
"substrate/coins",
"substrate/validator-sets",
"substrate/signals",
"substrate/dex",
"substrate/genesis-liquidity",
"substrate/economic-security",
"substrate/emissions",
"substrate/in-instructions",
"substrate/runtime",
"substrate/node",
@@ -205,17 +183,6 @@ modular-frost = { path = "crypto/frost" }
# https://github.com/rust-lang-nursery/lazy-static.rs/issues/201
lazy_static = { git = "https://github.com/rust-lang-nursery/lazy-static.rs", rev = "5735630d46572f1e5377c8f2ba0f79d18f53b10c" }
parking_lot = { path = "patches/parking_lot" }
# wasmtime pulls in an old version for this
zstd = { path = "patches/zstd" }
# Needed for WAL compression
rocksdb = { path = "patches/rocksdb" }
# is-terminal now has an std-based solution with an equivalent API
is-terminal = { path = "patches/is-terminal" }
# So does matches
matches = { path = "patches/matches" }
# directories-next was created because directories was unmaintained
# directories-next is now unmaintained while directories is maintained
# The directories author pulls in ridiculously pointless crates and prefers
@@ -228,12 +195,15 @@ directories-next = { path = "patches/directories-next" }
k256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab163781a88cd4a49beae812a89a44e8c3" }
# Patch due to `std` now including the required functionality
is_terminal_polyfill = { path = "./patches/is_terminal_polyfill" }
[workspace.lints.clippy]
incompatible_msrv = "allow" # Manually verified with a GitHub workflow
manual_is_multiple_of = "allow"
unwrap_or_default = "allow"
map_unwrap_or = "allow"
needless_continue = "allow"
manual_is_multiple_of = "allow"
incompatible_msrv = "allow" # Manually verified with a GitHub workflow
borrow_as_ptr = "deny"
cast_lossless = "deny"
cast_possible_truncation = "deny"

View File

@@ -15,7 +15,7 @@ pub fn serai_db_key(
///
/// Creates a unit struct and a default implementation for the `key`, `get`, and `set`. The macro
/// uses a syntax similar to defining a function. Parameters are concatenated to produce a key,
/// they must be `scale` encodable. The return type is used to auto encode and decode the database
/// they must be `borsh` serializable. The return type is used to auto (de)serialize the database
/// value bytes using `borsh`.
///
/// # Arguments
@@ -54,11 +54,10 @@ macro_rules! create_db {
)?;
impl$(<$($generic_name: $generic_type),+>)? $field_name$(<$($generic_name),+>)? {
pub(crate) fn key($($arg: $arg_type),*) -> Vec<u8> {
use scale::Encode;
$crate::serai_db_key(
stringify!($db_name).as_bytes(),
stringify!($field_name).as_bytes(),
($($arg),*).encode()
&borsh::to_vec(&($($arg),*)).unwrap(),
)
}
pub(crate) fn set(

View File

@@ -19,7 +19,7 @@ workspace = true
[dependencies]
rustversion = { version = "1", default-features = false }
spin = { version = "0.10", default-features = false, features = ["use_ticket_mutex", "once", "lazy"] }
hashbrown = { version = "0.15", default-features = false, features = ["default-hasher", "inline-more"] }
hashbrown = { version = "0.16", default-features = false, features = ["default-hasher", "inline-more"] }
[features]
std = []

View File

@@ -31,7 +31,6 @@ frost = { package = "modular-frost", path = "../crypto/frost" }
frost-schnorrkel = { path = "../crypto/schnorrkel" }
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive", "bit-vec"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
zalloc = { path = "../common/zalloc" }
@@ -43,7 +42,7 @@ messages = { package = "serai-processor-messages", path = "../processor/messages
message-queue = { package = "serai-message-queue", path = "../message-queue" }
tributary-sdk = { path = "./tributary-sdk" }
serai-client = { path = "../substrate/client", default-features = false, features = ["serai", "borsh"] }
serai-client = { path = "../substrate/client", default-features = false, features = ["serai"] }
log = { version = "0.4", default-features = false, features = ["std"] }
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }

View File

@@ -21,9 +21,8 @@ workspace = true
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai"] }
log = { version = "0.4", default-features = false, features = ["std"] }
@@ -31,3 +30,5 @@ tokio = { version = "1", default-features = false }
serai-db = { path = "../../common/db", version = "0.1.1" }
serai-task = { path = "../../common/task", version = "0.1" }
serai-cosign-types = { path = "./types" }

View File

@@ -7,7 +7,6 @@ use std::{sync::Arc, collections::HashMap, time::Instant};
use blake2::{Digest, Blake2s256};
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{
@@ -19,6 +18,8 @@ use serai_client::{
use serai_db::*;
use serai_task::*;
use serai_cosign_types::*;
/// The cosigns which are intended to be performed.
mod intend;
/// The evaluator of the cosigns.
@@ -78,68 +79,6 @@ enum HasEvents {
No,
}
/// An intended cosign.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct CosignIntent {
/// The global session this cosign is being performed under.
pub global_session: [u8; 32],
/// The number of the block to cosign.
pub block_number: u64,
/// The hash of the block to cosign.
pub block_hash: [u8; 32],
/// If this cosign must be handled before further cosigns are.
pub notable: bool,
}
/// A cosign.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, BorshSerialize, BorshDeserialize)]
pub struct Cosign {
/// The global session this cosign is being performed under.
pub global_session: [u8; 32],
/// The number of the block to cosign.
pub block_number: u64,
/// The hash of the block to cosign.
pub block_hash: [u8; 32],
/// The actual cosigner.
pub cosigner: ExternalNetworkId,
}
impl CosignIntent {
/// Convert this into a `Cosign`.
pub fn into_cosign(self, cosigner: ExternalNetworkId) -> Cosign {
let CosignIntent { global_session, block_number, block_hash, notable: _ } = self;
Cosign { global_session, block_number, block_hash, cosigner }
}
}
impl Cosign {
/// The message to sign to sign this cosign.
///
/// This must be signed with schnorrkel, the context set to `COSIGN_CONTEXT`.
pub fn signature_message(&self) -> Vec<u8> {
// We use a schnorrkel context to domain-separate this
self.encode()
}
}
/// A signed cosign.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct SignedCosign {
/// The cosign.
pub cosign: Cosign,
/// The signature for the cosign.
pub signature: [u8; 64],
}
impl SignedCosign {
fn verify_signature(&self, signer: serai_client::Public) -> bool {
let Ok(signer) = schnorrkel::PublicKey::from_bytes(&signer.0) else { return false };
let Ok(signature) = schnorrkel::Signature::from_bytes(&self.signature) else { return false };
signer.verify_simple(COSIGN_CONTEXT, &self.cosign.signature_message(), &signature).is_ok()
}
}
create_db! {
Cosign {
// The following are populated by the intend task and used throughout the library

View File

@@ -0,0 +1,25 @@
[package]
name = "serai-cosign-types"
version = "0.1.0"
description = "Evaluator of cosigns for the Serai network"
license = "AGPL-3.0-only"
repository = "https://github.com/serai-dex/serai/tree/develop/coordinator/cosign"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
publish = false
rust-version = "1.85"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
[dependencies]
schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-primitives = { path = "../../../substrate/primitives", default-features = false, features = ["std"] }

View File

@@ -0,0 +1,72 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![deny(missing_docs)]
//! Types used when cosigning Serai. For more info, please see `serai-cosign`.
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::{crypto::Public, network_id::ExternalNetworkId};
/// The schnorrkel context to used when signing a cosign.
pub const COSIGN_CONTEXT: &[u8] = b"/serai/coordinator/cosign";
/// An intended cosign.
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct CosignIntent {
/// The global session this cosign is being performed under.
pub global_session: [u8; 32],
/// The number of the block to cosign.
pub block_number: u64,
/// The hash of the block to cosign.
pub block_hash: [u8; 32],
/// If this cosign must be handled before further cosigns are.
pub notable: bool,
}
/// A cosign.
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Cosign {
/// The global session this cosign is being performed under.
pub global_session: [u8; 32],
/// The number of the block to cosign.
pub block_number: u64,
/// The hash of the block to cosign.
pub block_hash: [u8; 32],
/// The actual cosigner.
pub cosigner: ExternalNetworkId,
}
impl CosignIntent {
/// Convert this into a `Cosign`.
pub fn into_cosign(self, cosigner: ExternalNetworkId) -> Cosign {
let CosignIntent { global_session, block_number, block_hash, notable: _ } = self;
Cosign { global_session, block_number, block_hash, cosigner }
}
}
impl Cosign {
/// The message to sign to sign this cosign.
///
/// This must be signed with schnorrkel, the context set to `COSIGN_CONTEXT`.
pub fn signature_message(&self) -> Vec<u8> {
// We use a schnorrkel context to domain-separate this
borsh::to_vec(self).unwrap()
}
}
/// A signed cosign.
#[derive(Clone, Debug, BorshSerialize, BorshDeserialize)]
pub struct SignedCosign {
/// The cosign.
pub cosign: Cosign,
/// The signature for the cosign.
pub signature: [u8; 64],
}
impl SignedCosign {
/// Verify a cosign's signature.
pub fn verify_signature(&self, signer: Public) -> bool {
let Ok(signer) = schnorrkel::PublicKey::from_bytes(&signer.0) else { return false };
let Ok(signature) = schnorrkel::Signature::from_bytes(&self.signature) else { return false };
signer.verify_simple(COSIGN_CONTEXT, &self.cosign.signature_message(), &signature).is_ok()
}
}

View File

@@ -22,7 +22,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
serai-db = { path = "../../common/db", version = "0.1" }
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-cosign = { path = "../cosign" }
tributary-sdk = { path = "../tributary-sdk" }

View File

@@ -29,7 +29,7 @@ schnorrkel = { version = "0.11", default-features = false, features = ["std"] }
hex = { version = "0.4", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai", "borsh"] }
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai"] }
serai-cosign = { path = "../../cosign" }
tributary-sdk = { path = "../../tributary-sdk" }

View File

@@ -1,7 +1,7 @@
use core::future::Future;
use std::time::{Duration, SystemTime};
use serai_client::validator_sets::primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
use serai_primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
use futures_lite::FutureExt;

View File

@@ -7,7 +7,7 @@ use std::collections::HashMap;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{primitives::ExternalNetworkId, validator_sets::primitives::ExternalValidatorSet};
use serai_primitives::{network_id::ExternalNetworkId, validator_sets::ExternalValidatorSet};
use serai_db::Db;
use tributary_sdk::{ReadWrite, TransactionTrait, Tributary, TributaryReader};

View File

@@ -11,7 +11,6 @@ use tokio::sync::mpsc;
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
use scale::Encode;
use serai_client::validator_sets::primitives::ExternalValidatorSet;
use tributary_sdk::{TransactionKind, TransactionError, ProvidedError, TransactionTrait, Tributary};
@@ -479,7 +478,8 @@ pub(crate) async fn spawn_tributary<P: P2p>(
return;
}
let genesis = <[u8; 32]>::from(Blake2s::<U32>::digest((set.serai_block, set.set).encode()));
let genesis =
<[u8; 32]>::from(Blake2s::<U32>::digest(borsh::to_vec(&(set.serai_block, set.set)).unwrap()));
// Since the Serai block will be finalized, then cosigned, before we handle this, this time will
// be a couple of minutes stale. While the Tributary will still function with a start time in the

View File

@@ -20,12 +20,11 @@ workspace = true
[dependencies]
bitvec = { version = "1", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive", "bit-vec"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai", "borsh"] }
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai"] }
log = { version = "0.4", default-features = false, features = ["std"] }

View File

@@ -4,7 +4,6 @@
use std::collections::HashMap;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use dkg::Participant;
@@ -178,14 +177,13 @@ impl Keys {
signature_participants,
signature,
);
_public_db::Keys::set(txn, set.network, &(set.session, tx.encode()));
_public_db::Keys::set(txn, set.network, &(set.session, tx));
}
pub(crate) fn take(
txn: &mut impl DbTxn,
network: ExternalNetworkId,
) -> Option<(Session, Transaction)> {
let (session, tx) = _public_db::Keys::take(txn, network)?;
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
_public_db::Keys::take(txn, network)
}
}
@@ -226,13 +224,12 @@ impl SlashReports {
slash_report,
signature,
);
_public_db::SlashReports::set(txn, set.network, &(set.session, tx.encode()));
_public_db::SlashReports::set(txn, set.network, &(set.session, tx));
}
pub(crate) fn take(
txn: &mut impl DbTxn,
network: ExternalNetworkId,
) -> Option<(Session, Transaction)> {
let (session, tx) = _public_db::SlashReports::take(txn, network)?;
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
_public_db::SlashReports::take(txn, network)
}
}

View File

@@ -36,7 +36,7 @@ log = { version = "0.4", default-features = false, features = ["std"] }
serai-db = { path = "../../common/db", version = "0.1" }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
futures-util = { version = "0.3", default-features = false, features = ["std", "sink", "channel"] }
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }
tendermint = { package = "tendermint-machine", path = "./tendermint", version = "0.2" }

View File

@@ -5,7 +5,7 @@ use ciphersuite::{group::GroupEncoding, *};
use serai_db::{Get, DbTxn, Db};
use scale::Decode;
use borsh::BorshDeserialize;
use tendermint::ext::{Network, Commit};
@@ -62,7 +62,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
D::key(
b"tributary_blockchain",
b"next_nonce",
[genesis.as_ref(), signer.to_bytes().as_ref(), order].concat(),
[genesis.as_slice(), signer.to_bytes().as_slice(), order].concat(),
)
}
@@ -109,7 +109,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
pub(crate) fn block_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Block<T>> {
db.get(Self::block_key(&genesis, block))
.map(|bytes| Block::<T>::read::<&[u8]>(&mut bytes.as_ref()).unwrap())
.map(|bytes| Block::<T>::read::<&[u8]>(&mut bytes.as_slice()).unwrap())
}
pub(crate) fn commit_from_db(db: &D, genesis: [u8; 32], block: &[u8; 32]) -> Option<Vec<u8>> {
@@ -169,7 +169,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
// we must have a commit per valid hash
let commit = Self::commit_from_db(db, genesis, &hash).unwrap();
// commit has to be valid if it is coming from our db
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).unwrap())
Some(Commit::<N::SignatureScheme>::deserialize_reader(&mut commit.as_slice()).unwrap())
};
let unsigned_in_chain =
|hash: [u8; 32]| db.get(Self::unsigned_included_key(&self.genesis, &hash)).is_some();
@@ -244,7 +244,7 @@ impl<D: Db, T: TransactionTrait> Blockchain<D, T> {
let commit = |block: u64| -> Option<Commit<N::SignatureScheme>> {
let commit = self.commit_by_block_number(block)?;
// commit has to be valid if it is coming from our db
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).unwrap())
Some(Commit::<N::SignatureScheme>::deserialize_reader(&mut commit.as_slice()).unwrap())
};
let mut txn_db = db.clone();

View File

@@ -3,10 +3,11 @@ use std::{sync::Arc, io};
use zeroize::Zeroizing;
use borsh::BorshDeserialize;
use ciphersuite::*;
use dalek_ff_group::Ristretto;
use scale::Decode;
use futures_channel::mpsc::UnboundedReceiver;
use futures_util::{StreamExt, SinkExt};
use ::tendermint::{
@@ -177,7 +178,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
let block_number = BlockNumber(blockchain.block_number());
let start_time = if let Some(commit) = blockchain.commit(&blockchain.tip()) {
Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time
Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
} else {
start_time
};
@@ -276,8 +277,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
}
let block = TendermintBlock(block.serialize());
let mut commit_ref = commit.as_ref();
let Ok(commit) = Commit::<Arc<Validators>>::decode(&mut commit_ref) else {
let mut commit_ref = commit.as_slice();
let Ok(commit) = Commit::<Arc<Validators>>::deserialize_reader(&mut commit_ref) else {
log::error!("sent an invalidly serialized commit");
return false;
};
@@ -327,7 +328,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
Some(&TENDERMINT_MESSAGE) => {
let Ok(msg) =
SignedMessageFor::<TendermintNetwork<D, T, P>>::decode::<&[u8]>(&mut &msg[1 ..])
SignedMessageFor::<TendermintNetwork<D, T, P>>::deserialize_reader(&mut &msg[1 ..])
else {
log::error!("received invalid tendermint message");
return false;
@@ -367,15 +368,17 @@ impl<D: Db, T: TransactionTrait> TributaryReader<D, T> {
Blockchain::<D, T>::commit_from_db(&self.0, self.1, hash)
}
pub fn parsed_commit(&self, hash: &[u8; 32]) -> Option<Commit<Validators>> {
self.commit(hash).map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).unwrap())
self
.commit(hash)
.map(|commit| Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap())
}
pub fn block_after(&self, hash: &[u8; 32]) -> Option<[u8; 32]> {
Blockchain::<D, T>::block_after(&self.0, self.1, hash)
}
pub fn time_of_block(&self, hash: &[u8; 32]) -> Option<u64> {
self
.commit(hash)
.map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time)
self.commit(hash).map(|commit| {
Commit::<Validators>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
})
}
pub fn locally_provided_txs_in_block(&self, hash: &[u8; 32], order: &str) -> bool {

View File

@@ -21,7 +21,7 @@ use schnorr::{
use serai_db::Db;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use tendermint::{
SignedMessageFor,
ext::{
@@ -248,7 +248,7 @@ impl Weights for Validators {
}
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct TendermintBlock(pub Vec<u8>);
impl BlockTrait for TendermintBlock {
type Id = [u8; 32];
@@ -300,7 +300,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
fn broadcast(&mut self, msg: SignedMessageFor<Self>) -> impl Send + Future<Output = ()> {
async move {
let mut to_broadcast = vec![TENDERMINT_MESSAGE];
to_broadcast.extend(msg.encode());
msg.serialize(&mut to_broadcast).unwrap();
self.p2p.broadcast(self.genesis, to_broadcast).await
}
}
@@ -390,7 +390,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Network for TendermintNetwork<D, T, P>
return invalid_block();
};
let encoded_commit = commit.encode();
let encoded_commit = borsh::to_vec(&commit).unwrap();
loop {
let block_res = self.blockchain.write().await.add_block::<Self>(
&block,

View File

@@ -1,6 +1,6 @@
use std::io;
use scale::{Encode, Decode, IoReader};
use borsh::BorshDeserialize;
use blake2::{Digest, Blake2s256};
@@ -27,14 +27,14 @@ pub enum TendermintTx {
impl ReadWrite for TendermintTx {
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
Evidence::decode(&mut IoReader(reader))
Evidence::deserialize_reader(reader)
.map(TendermintTx::SlashEvidence)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid evidence format"))
}
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
match self {
TendermintTx::SlashEvidence(ev) => writer.write_all(&ev.encode()),
TendermintTx::SlashEvidence(ev) => writer.write_all(&borsh::to_vec(&ev).unwrap()),
}
}
}

View File

@@ -10,8 +10,6 @@ use dalek_ff_group::Ristretto;
use ciphersuite::{group::Group, *};
use schnorr::SchnorrSignature;
use scale::Encode;
use ::tendermint::{
ext::{Network, Signer as SignerTrait, SignatureScheme, BlockNumber, RoundNumber},
SignedMessageFor, DataFor, Message, SignedMessage, Data, Evidence,
@@ -200,7 +198,7 @@ pub async fn signed_from_data<N: Network>(
round: RoundNumber(round_number),
data,
};
let sig = signer.sign(&msg.encode()).await;
let sig = signer.sign(&borsh::to_vec(&msg).unwrap()).await;
SignedMessage { msg, sig }
}
@@ -213,5 +211,5 @@ pub async fn random_evidence_tx<N: Network>(
let data = Data::Proposal(Some(RoundNumber(0)), b);
let signer_id = signer.validator_id().await.unwrap();
let signed = signed_from_data::<N>(signer, signer_id, 0, 0, data).await;
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()))
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap()))
}

View File

@@ -6,8 +6,6 @@ use rand::{RngCore, rngs::OsRng};
use dalek_ff_group::Ristretto;
use ciphersuite::*;
use scale::Encode;
use tendermint::{
time::CanonicalInstant,
round::RoundData,
@@ -52,7 +50,10 @@ async fn invalid_valid_round() {
async move {
let data = Data::Proposal(valid_round, TendermintBlock(vec![]));
let signed = signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, data).await;
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode())))
(
signed.clone(),
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap())),
)
}
};
@@ -70,7 +71,8 @@ async fn invalid_valid_round() {
let mut random_sig = [0u8; 64];
OsRng.fill_bytes(&mut random_sig);
signed.sig = random_sig;
let tx = TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()));
let tx =
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(borsh::to_vec(&signed).unwrap()));
// should fail
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
@@ -90,7 +92,10 @@ async fn invalid_precommit_signature() {
let signed =
signed_from_data::<N>(signer.clone().into(), signer_id, 1, 0, Data::Precommit(precommit))
.await;
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode())))
(
signed.clone(),
TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap())),
)
}
};
@@ -120,7 +125,8 @@ async fn invalid_precommit_signature() {
let mut random_sig = [0u8; 64];
OsRng.fill_bytes(&mut random_sig);
signed.sig = random_sig;
let tx = TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode()));
let tx =
TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap()));
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
}
}
@@ -138,24 +144,32 @@ async fn evidence_with_prevote() {
// it should fail for all reasons.
let mut txs = vec![];
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await
.encode(),
borsh::to_vec(
&&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await,
)
.unwrap(),
)));
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidValidRound(
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await
.encode(),
borsh::to_vec(
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await,
)
.unwrap(),
)));
// Since these require a second message, provide this one again
// ConflictingMessages can be fired for actually conflicting Prevotes however
txs.push(TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await
.encode(),
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await
.encode(),
borsh::to_vec(
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await,
)
.unwrap(),
borsh::to_vec(
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
.await,
)
.unwrap(),
)));
txs
}
@@ -189,16 +203,16 @@ async fn conflicting_msgs_evidence_tx() {
// non-conflicting data should fail
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x11]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_1.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_1).unwrap(),
));
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
// conflicting data should pass
let signed_2 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
@@ -206,16 +220,16 @@ async fn conflicting_msgs_evidence_tx() {
// (except for Precommit)
let signed_2 = signed_for_b_r(0, 1, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
// Proposals for different block numbers should also fail as evidence
let signed_2 = signed_for_b_r(1, 0, Data::Proposal(None, TendermintBlock(vec![0x22]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
}
@@ -225,16 +239,16 @@ async fn conflicting_msgs_evidence_tx() {
// non-conflicting data should fail
let signed_1 = signed_for_b_r(0, 0, Data::Prevote(Some([0x11; 32]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_1.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_1).unwrap(),
));
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
// conflicting data should pass
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
@@ -242,16 +256,16 @@ async fn conflicting_msgs_evidence_tx() {
// (except for Precommit)
let signed_2 = signed_for_b_r(0, 1, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
// Proposals for different block numbers should also fail as evidence
let signed_2 = signed_for_b_r(1, 0, Data::Prevote(Some([0x22; 32]))).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
}
@@ -273,8 +287,8 @@ async fn conflicting_msgs_evidence_tx() {
.await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
// update schema so that we don't fail due to invalid signature
@@ -292,8 +306,8 @@ async fn conflicting_msgs_evidence_tx() {
let signed_1 = signed_for_b_r(0, 0, Data::Proposal(None, TendermintBlock(vec![]))).await;
let signed_2 = signed_for_b_r(0, 0, Data::Prevote(None)).await;
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
signed_1.encode(),
signed_2.encode(),
borsh::to_vec(&signed_1).unwrap(),
borsh::to_vec(&signed_2).unwrap(),
));
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
}

View File

@@ -21,7 +21,7 @@ thiserror = { version = "2", default-features = false, features = ["std"] }
hex = { version = "0.4", default-features = false, features = ["std"] }
log = { version = "0.4", default-features = false, features = ["std"] }
parity-scale-codec = { version = "3", default-features = false, features = ["std", "derive"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
futures-util = { version = "0.3", default-features = false, features = ["std", "async-await-macro", "sink", "channel"] }
futures-channel = { version = "0.3", default-features = false, features = ["std", "sink"] }

View File

@@ -3,33 +3,41 @@ use std::{sync::Arc, collections::HashSet};
use thiserror::Error;
use parity_scale_codec::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use crate::{SignedMessageFor, SlashEvent, commit_msg};
/// An alias for a series of traits required for a type to be usable as a validator ID,
/// automatically implemented for all types satisfying those traits.
pub trait ValidatorId:
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + BorshSerialize + BorshDeserialize
{
}
impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode> ValidatorId
for V
#[rustfmt::skip]
impl<
V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + BorshSerialize + BorshDeserialize,
> ValidatorId for V
{
}
/// An alias for a series of traits required for a type to be usable as a signature,
/// automatically implemented for all types satisfying those traits.
pub trait Signature: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {}
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode> Signature for S {}
pub trait Signature:
Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize
{
}
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize> Signature
for S
{
}
// Type aliases which are distinct according to the type system
/// A struct containing a Block Number, wrapped to have a distinct type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub struct BlockNumber(pub u64);
/// A struct containing a round number, wrapped to have a distinct type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub struct RoundNumber(pub u32);
/// A signer for a validator.
@@ -127,7 +135,7 @@ impl<S: SignatureScheme> SignatureScheme for Arc<S> {
/// A commit for a specific block.
///
/// The list of validators have weight exceeding the threshold for a valid commit.
#[derive(PartialEq, Debug, Encode, Decode)]
#[derive(PartialEq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Commit<S: SignatureScheme> {
/// End time of the round which created this commit, used as the start time of the next block.
pub end_time: u64,
@@ -185,7 +193,7 @@ impl<W: Weights> Weights for Arc<W> {
}
/// Simplified error enum representing a block's validity.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, Encode, Decode)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, BorshSerialize, BorshDeserialize)]
pub enum BlockError {
/// Malformed block which is wholly invalid.
#[error("invalid block")]
@@ -197,9 +205,20 @@ pub enum BlockError {
}
/// Trait representing a Block.
pub trait Block: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {
pub trait Block:
Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize
{
// Type used to identify blocks. Presumably a cryptographic hash of the block.
type Id: Send + Sync + Copy + Clone + PartialEq + Eq + AsRef<[u8]> + Debug + Encode + Decode;
type Id: Send
+ Sync
+ Copy
+ Clone
+ PartialEq
+ Eq
+ AsRef<[u8]>
+ Debug
+ BorshSerialize
+ BorshDeserialize;
/// Return the deterministic, unique ID for this block.
fn id(&self) -> Self::Id;

View File

@@ -6,7 +6,7 @@ use std::{
collections::{VecDeque, HashMap},
};
use parity_scale_codec::{Encode, Decode, IoReader};
use borsh::{BorshSerialize, BorshDeserialize};
use futures_channel::mpsc;
use futures_util::{
@@ -41,14 +41,14 @@ pub fn commit_msg(end_time: u64, id: &[u8]) -> Vec<u8> {
[&end_time.to_le_bytes(), id].concat()
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub enum Step {
Propose,
Prevote,
Precommit,
}
#[derive(Clone, Eq, Debug, Encode, Decode)]
#[derive(Clone, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Data<B: Block, S: Signature> {
Proposal(Option<RoundNumber>, B),
Prevote(Option<B::Id>),
@@ -90,7 +90,7 @@ impl<B: Block, S: Signature> Data<B, S> {
}
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct Message<V: ValidatorId, B: Block, S: Signature> {
pub sender: V,
pub block: BlockNumber,
@@ -100,7 +100,7 @@ pub struct Message<V: ValidatorId, B: Block, S: Signature> {
}
/// A signed Tendermint consensus message to be broadcast to the other validators.
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
pub msg: Message<V, B, S>,
pub sig: S,
@@ -117,18 +117,18 @@ impl<V: ValidatorId, B: Block, S: Signature> SignedMessage<V, B, S> {
&self,
signer: &Scheme,
) -> bool {
signer.verify(self.msg.sender, &self.msg.encode(), &self.sig)
signer.verify(self.msg.sender, &borsh::to_vec(&self.msg).unwrap(), &self.sig)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum SlashReason {
FailToPropose,
InvalidBlock,
InvalidProposer,
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Evidence {
ConflictingMessages(Vec<u8>, Vec<u8>),
InvalidPrecommit(Vec<u8>),
@@ -159,7 +159,7 @@ pub type SignedMessageFor<N> = SignedMessage<
>;
pub fn decode_signed_message<N: Network>(mut data: &[u8]) -> Option<SignedMessageFor<N>> {
SignedMessageFor::<N>::decode(&mut data).ok()
SignedMessageFor::<N>::deserialize_reader(&mut data).ok()
}
fn decode_and_verify_signed_message<N: Network>(
@@ -339,7 +339,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
target: "tendermint",
"proposer for block {}, round {round:?} was {} (me: {res})",
self.block.number.0,
hex::encode(proposer.encode()),
hex::encode(borsh::to_vec(&proposer).unwrap()),
);
res
}
@@ -420,7 +420,11 @@ impl<N: Network + 'static> TendermintMachine<N> {
// TODO: If the new slash event has evidence, emit to prevent a low-importance slash from
// cancelling emission of high-importance slashes
if !self.block.slashes.contains(&validator) {
log::info!(target: "tendermint", "Slashing validator {}", hex::encode(validator.encode()));
log::info!(
target: "tendermint",
"Slashing validator {}",
hex::encode(borsh::to_vec(&validator).unwrap()),
);
self.block.slashes.insert(validator);
self.network.slash(validator, slash_event).await;
}
@@ -670,7 +674,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
self
.slash(
msg.sender,
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(signed.encode())),
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap())),
)
.await;
Err(TendermintError::Malicious)?;
@@ -741,7 +745,10 @@ impl<N: Network + 'static> TendermintMachine<N> {
self.broadcast(Data::Prevote(None));
}
self
.slash(msg.sender, SlashEvent::WithEvidence(Evidence::InvalidValidRound(msg.encode())))
.slash(
msg.sender,
SlashEvent::WithEvidence(Evidence::InvalidValidRound(borsh::to_vec(&msg).unwrap())),
)
.await;
Err(TendermintError::Malicious)?;
}
@@ -1032,7 +1039,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
while !messages.is_empty() {
self.network.broadcast(
SignedMessageFor::<N>::decode(&mut IoReader(&mut messages))
SignedMessageFor::<N>::deserialize_reader(&mut messages)
.expect("saved invalid message to DB")
).await;
}
@@ -1057,7 +1064,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
} {
if our_message {
assert!(sig.is_none());
sig = Some(self.signer.sign(&msg.encode()).await);
sig = Some(self.signer.sign(&borsh::to_vec(&msg).unwrap()).await);
}
let sig = sig.unwrap();
@@ -1077,7 +1084,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
let message_tape_key = message_tape_key(self.genesis);
let mut txn = self.db.txn();
let mut message_tape = txn.get(&message_tape_key).unwrap_or(vec![]);
message_tape.extend(signed_msg.encode());
signed_msg.serialize(&mut message_tape).unwrap();
txn.put(&message_tape_key, message_tape);
txn.commit();
}

View File

@@ -1,7 +1,5 @@
use std::{sync::Arc, collections::HashMap};
use parity_scale_codec::Encode;
use crate::{ext::*, RoundNumber, Step, DataFor, SignedMessageFor, Evidence};
type RoundLog<N> = HashMap<<N as Network>::ValidatorId, HashMap<Step, SignedMessageFor<N>>>;
@@ -39,7 +37,10 @@ impl<N: Network> MessageLog<N> {
target: "tendermint",
"Validator sent multiple messages for the same block + round + step"
);
Err(Evidence::ConflictingMessages(existing.encode(), signed.encode()))?;
Err(Evidence::ConflictingMessages(
borsh::to_vec(&existing).unwrap(),
borsh::to_vec(&signed).unwrap(),
))?;
}
return Ok(false);
}

View File

@@ -4,7 +4,7 @@ use std::{
time::{UNIX_EPOCH, SystemTime, Duration},
};
use parity_scale_codec::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use futures_util::sink::SinkExt;
use tokio::{sync::RwLock, time::sleep};
@@ -89,7 +89,7 @@ impl Weights for TestWeights {
}
}
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
struct TestBlock {
id: TestBlockId,
valid: Result<(), BlockError>,

View File

@@ -21,7 +21,6 @@ workspace = true
zeroize = { version = "^1.5", default-features = false, features = ["std"] }
rand_core = { version = "0.6", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
blake2 = { version = "0.11.0-rc.0", default-features = false, features = ["alloc"] }
@@ -30,7 +29,7 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = fals
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std"] }
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr", default-features = false, features = ["std"] }
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-db = { path = "../../common/db" }
serai-task = { path = "../../common/task", version = "0.1" }

View File

@@ -1,9 +1,8 @@
use std::collections::HashMap;
use scale::Encode;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
use serai_primitives::{address::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
use messages::sign::{VariantSignId, SignId};
@@ -14,7 +13,7 @@ use serai_cosign::CosignIntent;
use crate::transaction::SigningProtocolRound;
/// A topic within the database which the group participates in
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum Topic {
/// Vote to remove a participant
RemoveParticipant {
@@ -123,7 +122,7 @@ impl Topic {
Topic::DkgConfirmation { attempt, round: _ } => Some({
let id = {
let mut id = [0; 32];
let encoded_set = set.encode();
let encoded_set = borsh::to_vec(set).unwrap();
id[.. encoded_set.len()].copy_from_slice(&encoded_set);
VariantSignId::Batch(id)
};

View File

@@ -8,9 +8,9 @@ use std::collections::HashMap;
use ciphersuite::group::GroupEncoding;
use dkg::Participant;
use serai_client::{
primitives::SeraiAddress,
validator_sets::primitives::{ExternalValidatorSet, Slash},
use serai_primitives::{
address::SeraiAddress,
validator_sets::{ExternalValidatorSet, Slash},
};
use serai_db::*;

View File

@@ -12,10 +12,9 @@ use ciphersuite::{
use dalek_ff_group::Ristretto;
use schnorr::SchnorrSignature;
use scale::Encode;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::MAX_KEY_SHARES_PER_SET};
use serai_primitives::{addess::SeraiAddress, validator_sets::MAX_KEY_SHARES_PER_SET};
use messages::sign::VariantSignId;
@@ -29,7 +28,7 @@ use tributary_sdk::{
use crate::db::Topic;
/// The round this data is for, within a signing protocol.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
pub enum SigningProtocolRound {
/// A preprocess.
Preprocess,
@@ -242,19 +241,20 @@ impl TransactionTrait for Transaction {
fn kind(&self) -> TransactionKind {
match self {
Transaction::RemoveParticipant { participant, signed } => TransactionKind::Signed(
(b"RemoveParticipant", participant).encode(),
borsh::to_vec(&(b"RemoveParticipant".as_slice(), participant)).unwrap(),
signed.to_tributary_signed(0),
),
Transaction::DkgParticipation { signed, .. } => {
TransactionKind::Signed(b"DkgParticipation".encode(), signed.to_tributary_signed(0))
}
Transaction::DkgParticipation { signed, .. } => TransactionKind::Signed(
borsh::to_vec(b"DkgParticipation".as_slice()).unwrap(),
signed.to_tributary_signed(0),
),
Transaction::DkgConfirmationPreprocess { attempt, signed, .. } => TransactionKind::Signed(
(b"DkgConfirmation", attempt).encode(),
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
signed.to_tributary_signed(0),
),
Transaction::DkgConfirmationShare { attempt, signed, .. } => TransactionKind::Signed(
(b"DkgConfirmation", attempt).encode(),
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
signed.to_tributary_signed(1),
),
@@ -264,13 +264,14 @@ impl TransactionTrait for Transaction {
Transaction::Batch { .. } => TransactionKind::Provided("Batch"),
Transaction::Sign { id, attempt, round, signed, .. } => TransactionKind::Signed(
(b"Sign", id, attempt).encode(),
borsh::to_vec(b"Sign".as_slice(), id, attempt).unwrap(),
signed.to_tributary_signed(round.nonce()),
),
Transaction::SlashReport { signed, .. } => {
TransactionKind::Signed(b"SlashReport".encode(), signed.to_tributary_signed(0))
}
Transaction::SlashReport { signed, .. } => TransactionKind::Signed(
borsh::to_vec(b"SlashReport".as_slice()).unwrap(),
signed.to_tributary_signed(0),
),
}
}

View File

@@ -7,11 +7,10 @@ db-urls = ["https://github.com/rustsec/advisory-db"]
yanked = "deny"
ignore = [
"RUSTSEC-2020-0168", # mach is unmaintained
"RUSTSEC-2021-0139", # https://github.com/serai-dex/serai/228
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
"RUSTSEC-2024-0384", # instant is unmaintained
"RUSTSEC-2024-0436", # paste is unmaintained
]
[licenses]
@@ -30,7 +29,6 @@ allow = [
"ISC",
"Zlib",
"Unicode-3.0",
"OpenSSL",
# Non-invasive copyleft
"MPL-2.0",
@@ -73,6 +71,7 @@ exceptions = [
{ allow = ["AGPL-3.0"], name = "serai-monero-processor" },
{ allow = ["AGPL-3.0"], name = "tributary-sdk" },
{ allow = ["AGPL-3.0"], name = "serai-cosign-types" },
{ allow = ["AGPL-3.0"], name = "serai-cosign" },
{ allow = ["AGPL-3.0"], name = "serai-coordinator-substrate" },
{ allow = ["AGPL-3.0"], name = "serai-coordinator-tributary" },
@@ -124,6 +123,8 @@ highlight = "all"
deny = [
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
{ name = "hashbrown", version = "=0.15.0" },
# Legacy which _no one_ should use anymore
{ name = "is-terminal", version = "*" },
]
[sources]
@@ -132,10 +133,7 @@ unknown-git = "deny"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = [
"https://github.com/rust-lang-nursery/lazy-static.rs",
"https://github.com/kayabaNerve/hybrid-array",
"https://github.com/kayabaNerve/elliptic-curves",
"https://github.com/monero-oxide/monero-oxide",
"https://github.com/serai-dex/substrate-bip39",
"https://github.com/serai-dex/substrate",
"https://github.com/serai-dex/patch-polkadot-sdk",
]

View File

@@ -46,7 +46,7 @@ serai-db = { path = "../common/db", optional = true }
serai-env = { path = "../common/env" }
serai-primitives = { path = "../substrate/primitives", features = ["borsh"] }
serai-primitives = { path = "../substrate/primitives", default-features = false, features = ["std"] }
[features]
parity-db = ["serai-db/parity-db"]

View File

@@ -7,7 +7,7 @@ use dalek_ff_group::Ristretto;
pub(crate) use ciphersuite::{group::GroupEncoding, WrappedGroup, GroupCanonicalEncoding};
pub(crate) use schnorr_signatures::SchnorrSignature;
pub(crate) use serai_primitives::ExternalNetworkId;
pub(crate) use serai_primitives::network_id::ExternalNetworkId;
pub(crate) use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
@@ -198,7 +198,7 @@ async fn main() {
KEYS.write().unwrap().insert(service, key);
let mut queues = QUEUES.write().unwrap();
if service == Service::Coordinator {
for network in serai_primitives::EXTERNAL_NETWORKS {
for network in ExternalNetworkId::all() {
queues.insert(
(service, Service::Processor(network)),
RwLock::new(Queue(db.clone(), service, Service::Processor(network))),
@@ -213,12 +213,13 @@ async fn main() {
};
// Make queues for each ExternalNetworkId
for network in serai_primitives::EXTERNAL_NETWORKS {
for network in ExternalNetworkId::all() {
// Use a match so we error if the list of NetworkIds changes
let Some(key) = read_key(match network {
ExternalNetworkId::Bitcoin => "BITCOIN_KEY",
ExternalNetworkId::Ethereum => "ETHEREUM_KEY",
ExternalNetworkId::Monero => "MONERO_KEY",
_ => panic!("unrecognized network"),
}) else {
continue;
};

View File

@@ -4,7 +4,7 @@ use ciphersuite::{group::GroupEncoding, FromUniformBytes, WrappedGroup, WithPref
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::ExternalNetworkId;
use serai_primitives::network_id::ExternalNetworkId;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub enum Service {

View File

@@ -11,6 +11,7 @@ RUN rm -rf /etc/apt/sources.list.d/debian.sources && \
RUN apt update -y && apt upgrade -y && apt install -y clang
# Add the wasm toolchain
RUN rustup component add rust-src
RUN rustup target add wasm32v1-none
FROM deterministic

View File

@@ -163,6 +163,7 @@ RUN apt install -y pkg-config clang
RUN apt install -y make protobuf-compiler
# Add the wasm toolchain
RUN rustup component add rust-src
RUN rustup target add wasm32v1-none
{prelude}

View File

@@ -1,6 +1,6 @@
[package]
name = "directories-next"
version = "2.0.0"
version = "2.0.99"
description = "Patch from directories-next back to directories"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/directories-next"
@@ -13,4 +13,4 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
directories = "5"
directories = "6"

View File

@@ -1,9 +1,9 @@
[package]
name = "is-terminal"
version = "0.4.10"
description = "is-terminal written around std::io::IsTerminal"
name = "is_terminal_polyfill"
version = "1.70.1"
description = "is_terminal_polyfill written around std::io::IsTerminal"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/is-terminal"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/is_terminal_polyfill"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"

View File

@@ -1,14 +0,0 @@
[package]
name = "matches"
version = "0.1.10"
description = "Replacement for the matches polyfill which uses the std impl"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/matches"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2018"
rust-version = "1.42"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

View File

@@ -1 +0,0 @@
pub use std::matches;

View File

@@ -1,16 +0,0 @@
[package]
name = "parking_lot"
version = "0.11.2"
description = "parking_lot which patches to the latest update"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/parking_lot"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
parking_lot = "0.12"

View File

@@ -1 +0,0 @@
pub use parking_lot::*;

View File

@@ -1,25 +0,0 @@
[package]
name = "rocksdb"
version = "0.21.0"
description = "rocksdb which patches to the latest update"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/rocksdb"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
rocksdb = { version = "0.24", default-features = false, features = ["bindgen-runtime"] }
[features]
jemalloc = [] # Dropped as this causes a compilation failure on windows
snappy = ["rocksdb/snappy"]
lz4 = ["rocksdb/lz4"]
zstd = ["rocksdb/zstd"]
zlib = ["rocksdb/zlib"]
bzip2 = ["rocksdb/bzip2"]
default = ["snappy", "lz4", "zstd", "zlib", "bzip2"]

View File

@@ -1 +0,0 @@
pub use rocksdb::*;

View File

@@ -1,16 +0,0 @@
[package]
name = "zstd"
version = "0.11.2+zstd.1.5.2"
description = "zstd which patches to the latest update"
license = "MIT"
repository = "https://github.com/serai-dex/serai/tree/develop/patches/zstd"
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
keywords = []
edition = "2021"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
zstd = "0.13"

View File

@@ -1 +0,0 @@
pub use zstd::*;

View File

@@ -20,14 +20,13 @@ workspace = true
zeroize = { version = "1", default-features = false, features = ["std"] }
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std"] }
serai-client = { path = "../../substrate/client", default-features = false }
serai-cosign = { path = "../../coordinator/cosign" }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-cosign = { package = "serai-cosign-types", path = "../../coordinator/cosign/types" }
log = { version = "0.4", default-features = false, features = ["std"] }
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }

View File

@@ -3,10 +3,10 @@ use std::sync::{LazyLock, Arc, Mutex};
use tokio::sync::mpsc;
use serai_client::{
primitives::Signature,
validator_sets::primitives::{Session, SlashReport},
in_instructions::primitives::SignedBatch,
use serai_primitives::{
crypto::Signature,
validator_sets::{Session, SlashReport},
instructions::SignedBatch,
};
use serai_cosign::SignedCosign;
@@ -34,7 +34,7 @@ static SEND_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
db_channel! {
ProcessorBinCoordinator {
SentCoordinatorMessages: () -> Vec<u8>,
SentCoordinatorMessages: () -> messages::ProcessorMessage,
}
}
@@ -48,7 +48,7 @@ impl CoordinatorSend {
fn send(&mut self, msg: &messages::ProcessorMessage) {
let _lock = SEND_LOCK.lock().unwrap();
let mut txn = self.db.txn();
SentCoordinatorMessages::send(&mut txn, &borsh::to_vec(msg).unwrap());
SentCoordinatorMessages::send(&mut txn, msg);
txn.commit();
self
.sent_message
@@ -114,12 +114,9 @@ impl Coordinator {
let mut txn = db.txn();
match SentCoordinatorMessages::try_recv(&mut txn) {
Some(msg) => {
let metadata = Metadata {
from: service,
to: Service::Coordinator,
intent: borsh::from_slice::<messages::ProcessorMessage>(&msg).unwrap().intent(),
};
message_queue.queue_with_retry(metadata, msg).await;
let metadata =
Metadata { from: service, to: Service::Coordinator, intent: msg.intent() };
message_queue.queue_with_retry(metadata, borsh::to_vec(&msg).unwrap()).await;
txn.commit();
}
None => {

View File

@@ -8,7 +8,7 @@ use ciphersuite::{
};
use dkg::Curves;
use serai_client::validator_sets::primitives::Session;
use serai_primitives::validator_sets::Session;
use serai_env as env;
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};

View File

@@ -20,7 +20,6 @@ workspace = true
rand_core = { version = "0.6", default-features = false }
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }

View File

@@ -10,12 +10,15 @@ use bitcoin_serai::{
wallet::ReceivedOutput as WalletOutput,
};
use scale::{Encode, Decode, IoReader};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::Get;
use serai_client::{
primitives::{ExternalCoin, Amount, ExternalBalance, ExternalAddress},
primitives::{
coin::ExternalCoin,
balance::{Amount, ExternalBalance},
address::ExternalAddress,
},
networks::bitcoin::Address,
};
@@ -26,7 +29,7 @@ use crate::{
scan::{offsets_for_key, presumed_origin, extract_serai_data},
};
#[derive(Clone, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize)]
#[derive(Clone, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub(crate) struct OutputId([u8; 36]);
impl Default for OutputId {
fn default() -> Self {
@@ -139,7 +142,7 @@ impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
self.kind.write(writer)?;
let presumed_origin: Option<ExternalAddress> = self.presumed_origin.clone().map(Into::into);
writer.write_all(&presumed_origin.encode())?;
presumed_origin.serialize(writer)?;
self.output.write(writer)?;
writer.write_all(&u16::try_from(self.data.len()).unwrap().to_le_bytes())?;
writer.write_all(&self.data)
@@ -149,7 +152,7 @@ impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
Ok(Output {
kind: OutputType::read(reader)?,
presumed_origin: {
Option::<ExternalAddress>::decode(&mut IoReader(&mut reader))
Option::<ExternalAddress>::deserialize_reader(&mut reader)
.map_err(|e| io::Error::other(format!("couldn't decode ExternalAddress: {e:?}")))?
.map(|address| {
Address::try_from(address)

View File

@@ -2,7 +2,7 @@ use core::future::Future;
use bitcoin_serai::rpc::{RpcError, Rpc as BRpc};
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
use serai_client::primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
use serai_db::Db;
use scanner::ScannerFeed;

View File

@@ -9,7 +9,7 @@ use bitcoin_serai::{
};
use serai_client::{
primitives::{ExternalCoin, Amount},
primitives::{coin::ExternalCoin, balance::Amount},
networks::bitcoin::Address,
};

View File

@@ -21,7 +21,6 @@ rand_core = { version = "0.6", default-features = false }
const-hex = { version = "1", default-features = false, features = ["std"] }
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }

View File

@@ -41,7 +41,6 @@ ethereum-primitives = { package = "serai-processor-ethereum-primitives", path =
ethereum-deployer = { package = "serai-processor-ethereum-deployer", path = "../deployer", default-features = false }
erc20 = { package = "serai-processor-ethereum-erc20", path = "../erc20", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
serai-client = { path = "../../../substrate/client", default-features = false, features = ["ethereum"] }
futures-util = { version = "0.3", default-features = false, features = ["std"] }

View File

@@ -19,7 +19,7 @@ interface IRouterWithoutCollisions {
/// @param from The address which called `inInstruction` and caused this event to be emitted
/// @param coin The coin transferred in
/// @param amount The amount of the coin transferred in
/// @param instruction The Shorthand-encoded InInstruction for Serai to decode and handle
/// @param instruction The encoded `RefundableInInstruction` for Serai to decode and handle
event InInstruction(
address indexed from, address indexed coin, uint256 amount, bytes instruction
);
@@ -81,8 +81,8 @@ interface IRouterWithoutCollisions {
/// @param coin The coin to transfer in (address(0) if Ether)
/// @param amount The amount to transfer in (msg.value if Ether)
/**
* @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
* transfer in
* @param instruction The encoded `RefundableInInstruction` for Serai to associate with this
* transfer in
*/
// Re-entrancy doesn't bork this function
// slither-disable-next-line reentrancy-events

View File

@@ -293,7 +293,7 @@ contract Router is IRouterWithoutCollisions {
/// @param coin The coin to transfer in (address(0) if Ether)
/// @param amount The amount to transfer in (msg.value if Ether)
/**
* @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
* @param instruction The encoded `RefundableInInstruction` for Serai to associate with this
* transfer in
*/
// This function doesn't require nonReentrant as re-entrancy isn't an issue with this function

View File

@@ -21,9 +21,8 @@ use alloy_rpc_types_eth::{BlockId, Log, Filter, TransactionInput, TransactionReq
use alloy_transport::{TransportErrorKind, RpcError};
use alloy_provider::{Provider, RootProvider};
use scale::Encode;
use serai_client::{
in_instructions::primitives::Shorthand, networks::ethereum::Address as SeraiAddress,
primitives::instructions::RefundableInInstruction, networks::ethereum::Address as SeraiAddress,
};
use ethereum_primitives::LogIndex;
@@ -351,20 +350,29 @@ impl Router {
}
}
/// Construct a transaction to send coins with an InInstruction to Serai.
/// Construct a transaction to send coins with an `InInstruction` to Serai.
///
/// If coin is an ERC20, this will not create a transaction calling the Router but will create a
/// top-level transfer of the ERC20 to the Router. This avoids needing to call `approve` before
/// publishing the transaction calling the Router.
///
/// The gas limit and gas price are not set and are left to the caller.
pub fn in_instruction(&self, coin: Coin, amount: U256, in_instruction: &Shorthand) -> TxLegacy {
pub fn in_instruction(
&self,
coin: Coin,
amount: U256,
in_instruction: &RefundableInInstruction,
) -> TxLegacy {
match coin {
Coin::Ether => TxLegacy {
to: self.address.into(),
input: abi::inInstructionCall::new((coin.into(), amount, in_instruction.encode().into()))
.abi_encode()
.into(),
input: abi::inInstructionCall::new((
coin.into(),
amount,
borsh::to_vec(&in_instruction).unwrap().into(),
))
.abi_encode()
.into(),
value: amount,
..Default::default()
},
@@ -373,7 +381,7 @@ impl Router {
input: erc20::transferWithInInstructionCall::new((
self.address,
amount,
in_instruction.encode().into(),
borsh::to_vec(&in_instruction).unwrap().into(),
))
.abi_encode()
.into(),

View File

@@ -5,12 +5,9 @@ use alloy_sol_types::SolCall;
use alloy_consensus::{TxLegacy, Signed};
use scale::Encode;
use serai_client::{
primitives::SeraiAddress,
in_instructions::primitives::{
InInstruction as SeraiInInstruction, RefundableInInstruction, Shorthand,
},
primitives::instructions::{InInstruction as SeraiInInstruction, RefundableInInstruction},
};
use ethereum_primitives::LogIndex;
@@ -18,14 +15,14 @@ use ethereum_primitives::LogIndex;
use crate::{InInstruction, tests::*};
impl Test {
pub(crate) fn in_instruction() -> Shorthand {
Shorthand::Raw(RefundableInInstruction {
pub(crate) fn in_instruction() -> RefundableInInstruction {
RefundableInInstruction {
origin: None,
instruction: SeraiInInstruction::Transfer(SeraiAddress([0xff; 32])),
})
}
}
pub(crate) fn eth_in_instruction_tx(&self) -> (Coin, U256, Shorthand, TxLegacy) {
pub(crate) fn eth_in_instruction_tx(&self) -> (Coin, U256, RefundableInInstruction, TxLegacy) {
let coin = Coin::Ether;
let amount = U256::from(1);
let shorthand = Self::in_instruction();
@@ -42,7 +39,7 @@ impl Test {
tx: Signed<TxLegacy>,
coin: Coin,
amount: U256,
shorthand: &Shorthand,
shorthand: &RefundableInInstruction,
) {
let receipt = ethereum_test_primitives::publish_tx(&self.provider, tx.clone()).await;
assert!(receipt.status());
@@ -81,7 +78,7 @@ impl Test {
from: tx.recover_signer().unwrap(),
coin,
amount,
data: shorthand.encode(),
data: borsh::to_vec(&shorthand).unwrap(),
}
);
}
@@ -140,9 +137,13 @@ async fn test_erc20_router_in_instruction() {
gas_limit: 1_000_000,
to: test.router.address().into(),
value: U256::ZERO,
input: crate::abi::inInstructionCall::new((coin.into(), amount, shorthand.encode().into()))
.abi_encode()
.into(),
input: crate::abi::inInstructionCall::new((
coin.into(),
amount,
borsh::to_vec(shorthand).unwrap().into(),
))
.abi_encode()
.into(),
};
// If no `approve` was granted, this should fail

View File

@@ -14,7 +14,7 @@ use alloy_simple_request_transport::SimpleRequest;
use alloy_rpc_client::ClientBuilder;
use alloy_provider::{Provider, RootProvider};
use serai_client::validator_sets::primitives::Session;
use serai_client::primitives::validator_sets::Session;
use serai_env as env;
use serai_db::{Get, DbTxn, create_db};

View File

@@ -1,6 +1,6 @@
use alloy_core::primitives::{FixedBytes, Address};
use serai_client::primitives::Amount;
use serai_client::primitives::balance::Amount;
pub(crate) mod output;
pub(crate) mod transaction;

View File

@@ -5,11 +5,14 @@ use ciphersuite_kp256::Secp256k1;
use alloy_core::primitives::U256;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{
primitives::{ExternalNetworkId, ExternalCoin, Amount, ExternalBalance},
primitives::{
network_id::ExternalNetworkId,
coin::ExternalCoin,
balance::{Amount, ExternalBalance},
},
networks::ethereum::Address,
};
@@ -39,9 +42,7 @@ fn amount_to_serai_amount(coin: ExternalCoin, amount: U256) -> Amount {
Amount(u64::try_from(amount / divisor).unwrap())
}
#[derive(
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub(crate) struct OutputId(pub(crate) [u8; 40]);
impl Default for OutputId {
fn default() -> Self {

View File

@@ -6,7 +6,7 @@ use alloy_rpc_types_eth::{Header, BlockNumberOrTag};
use alloy_transport::{RpcError, TransportErrorKind};
use alloy_provider::{Provider, RootProvider};
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
use serai_client::primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
use tokio::task::JoinSet;

View File

@@ -3,7 +3,7 @@ use std::collections::HashMap;
use alloy_core::primitives::U256;
use serai_client::{
primitives::{ExternalNetworkId, ExternalCoin, ExternalBalance},
primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::ExternalBalance},
networks::ethereum::Address,
};

View File

@@ -15,7 +15,7 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-machete]
ignored = ["borsh", "scale"]
ignored = ["borsh"]
[lints]
workspace = true
@@ -25,11 +25,10 @@ rand_core = { version = "0.6", default-features = false, features = ["std", "get
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
log = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-db = { path = "../../common/db" }

View File

@@ -7,7 +7,7 @@ use frost::{
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine},
};
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use serai_db::{Get, DbTxn, Db, create_db};
use messages::sign::{VariantSignId, SignId, ProcessorMessage};

View File

@@ -6,7 +6,7 @@ use std::collections::HashMap;
use frost::{Participant, sign::PreprocessMachine};
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use serai_db::{DbTxn, Db};
use messages::sign::{VariantSignId, ProcessorMessage, CoordinatorMessage};

View File

@@ -14,9 +14,6 @@ rust-version = "1.89"
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-machete]
ignored = ["scale"]
[lints]
workspace = true
@@ -37,10 +34,9 @@ dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features =
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["ristretto"] }
# Substrate
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
# Encoders
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
# Application

View File

@@ -6,7 +6,7 @@ use zeroize::Zeroizing;
use ciphersuite::{group::GroupEncoding, *};
use dkg::*;
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn};
@@ -36,7 +36,7 @@ pub(crate) struct Participations {
}
mod _db {
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use serai_db::{Get, DbTxn, create_db};

View File

@@ -6,7 +6,7 @@ use std::{
use dkg::*;
use serai_validator_sets_primitives::MAX_KEY_SHARES_PER_SET;
use serai_primitives::constants::MAX_KEY_SHARES_PER_SET;
/// A cache of the generators used by the eVRF DKG.
///

View File

@@ -17,7 +17,7 @@ use ciphersuite::{
};
use dkg::*;
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use messages::key_gen::*;
use serai_db::{Get, DbTxn};

View File

@@ -20,14 +20,10 @@ workspace = true
[dependencies]
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
dkg = { path = "../../crypto/dkg", default-features = false, features = ["std", "borsh"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std", "borsh"] }
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std", "borsh"] }
coins-primitives = { package = "serai-coins-primitives", path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std", "borsh"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-cosign = { path = "../../coordinator/cosign", default-features = false }
serai-cosign = { package = "serai-cosign-types", path = "../../coordinator/cosign/types" }

View File

@@ -1,15 +1,16 @@
use core::fmt;
use std::collections::HashMap;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use dkg::Participant;
use serai_primitives::BlockHash;
use validator_sets_primitives::{Session, KeyPair, SlashReport};
use coins_primitives::OutInstructionWithBalance;
use in_instructions_primitives::SignedBatch;
use serai_primitives::{
BlockHash,
crypto::KeyPair,
validator_sets::{Session, SlashReport},
instructions::{SignedBatch, OutInstructionWithBalance},
};
use serai_cosign::{Cosign, SignedCosign};
@@ -87,7 +88,7 @@ pub mod key_gen {
pub mod sign {
use super::*;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, BorshSerialize, BorshDeserialize)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)]
pub enum VariantSignId {
Cosign(u64),
Batch([u8; 32]),
@@ -111,9 +112,7 @@ pub mod sign {
}
}
#[derive(
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub struct SignId {
pub session: Session,
pub id: VariantSignId,
@@ -295,7 +294,7 @@ impl CoordinatorMessage {
let (sub, id) = match msg {
// Unique since we only have one attempt per session
key_gen::CoordinatorMessage::GenerateKey { session, .. } => {
(0, borsh::to_vec(session).unwrap())
(0, borsh::to_vec(&session).unwrap())
}
// Unique since one participation per participant per session
key_gen::CoordinatorMessage::Participation { session, participant, .. } => {
@@ -316,17 +315,19 @@ impl CoordinatorMessage {
};
let mut res = vec![COORDINATOR_UID, TYPE_SIGN_UID, sub];
res.extend(id.encode());
res.extend(borsh::to_vec(&id).unwrap());
res
}
CoordinatorMessage::Coordinator(msg) => {
let (sub, id) = match msg {
// We only cosign a block once, and Reattempt is a separate message
coordinator::CoordinatorMessage::CosignSubstrateBlock { cosign, .. } => {
(0, cosign.block_number.encode())
(0, borsh::to_vec(&cosign.block_number).unwrap())
}
// We only sign one slash report, and Reattempt is a separate message
coordinator::CoordinatorMessage::SignSlashReport { session, .. } => (1, session.encode()),
coordinator::CoordinatorMessage::SignSlashReport { session, .. } => {
(1, borsh::to_vec(&session).unwrap())
}
};
let mut res = vec![COORDINATOR_UID, TYPE_COORDINATOR_UID, sub];
@@ -335,10 +336,14 @@ impl CoordinatorMessage {
}
CoordinatorMessage::Substrate(msg) => {
let (sub, id) = match msg {
substrate::CoordinatorMessage::SetKeys { session, .. } => (0, session.encode()),
substrate::CoordinatorMessage::SlashesReported { session } => (1, session.encode()),
substrate::CoordinatorMessage::SetKeys { session, .. } => {
(0, borsh::to_vec(&session).unwrap())
}
substrate::CoordinatorMessage::SlashesReported { session } => {
(1, borsh::to_vec(&session).unwrap())
}
substrate::CoordinatorMessage::Block { serai_block_number, .. } => {
(2, serai_block_number.encode())
(2, borsh::to_vec(&serai_block_number).unwrap())
}
};
@@ -363,10 +368,10 @@ impl ProcessorMessage {
let (sub, id) = match msg {
// Unique since we only have one participation per session (due to no re-attempts)
key_gen::ProcessorMessage::Participation { session, .. } => {
(0, borsh::to_vec(session).unwrap())
(0, borsh::to_vec(&session).unwrap())
}
key_gen::ProcessorMessage::GeneratedKeyPair { session, .. } => {
(1, borsh::to_vec(session).unwrap())
(1, borsh::to_vec(&session).unwrap())
}
// Unique since we only blame a participant once (as this is fatal)
key_gen::ProcessorMessage::Blame { session, participant } => {
@@ -382,11 +387,11 @@ impl ProcessorMessage {
let (sub, id) = match msg {
// Unique since we'll only fatally slash a a participant once
sign::ProcessorMessage::InvalidParticipant { session, participant } => {
(0, (session, u16::from(*participant)).encode())
(0, borsh::to_vec(&(session, u16::from(*participant))).unwrap())
}
// Unique since SignId
sign::ProcessorMessage::Preprocesses { id, .. } => (1, id.encode()),
sign::ProcessorMessage::Shares { id, .. } => (2, id.encode()),
sign::ProcessorMessage::Preprocesses { id, .. } => (1, borsh::to_vec(&id).unwrap()),
sign::ProcessorMessage::Shares { id, .. } => (2, borsh::to_vec(&id).unwrap()),
};
let mut res = vec![PROCESSOR_UID, TYPE_SIGN_UID, sub];
@@ -396,10 +401,14 @@ impl ProcessorMessage {
ProcessorMessage::Coordinator(msg) => {
let (sub, id) = match msg {
coordinator::ProcessorMessage::CosignedBlock { cosign } => {
(0, cosign.cosign.block_hash.encode())
(0, borsh::to_vec(&cosign.cosign.block_hash).unwrap())
}
coordinator::ProcessorMessage::SignedBatch { batch, .. } => {
(1, borsh::to_vec(&batch.batch.id()).unwrap())
}
coordinator::ProcessorMessage::SignedSlashReport { session, .. } => {
(2, borsh::to_vec(&session).unwrap())
}
coordinator::ProcessorMessage::SignedBatch { batch, .. } => (1, batch.batch.id.encode()),
coordinator::ProcessorMessage::SignedSlashReport { session, .. } => (2, session.encode()),
};
let mut res = vec![PROCESSOR_UID, TYPE_COORDINATOR_UID, sub];
@@ -408,7 +417,9 @@ impl ProcessorMessage {
}
ProcessorMessage::Substrate(msg) => {
let (sub, id) = match msg {
substrate::ProcessorMessage::SubstrateBlockAck { block, .. } => (0, block.encode()),
substrate::ProcessorMessage::SubstrateBlockAck { block, .. } => {
(0, borsh::to_vec(&block).unwrap())
}
};
let mut res = vec![PROCESSOR_UID, TYPE_SUBSTRATE_UID, sub];

View File

@@ -21,7 +21,6 @@ rand_core = { version = "0.6", default-features = false }
rand_chacha = { version = "0.3", default-features = false, features = ["std"] }
zeroize = { version = "1", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std"] }

View File

@@ -5,11 +5,13 @@ use dalek_ff_group::Ed25519;
use monero_wallet::WalletOutput;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_client::{
primitives::{ExternalCoin, Amount, ExternalBalance},
primitives::{
coin::ExternalCoin,
balance::{Amount, ExternalBalance},
},
networks::monero::Address,
};
@@ -17,10 +19,7 @@ use primitives::{OutputType, ReceivedOutput};
use crate::{EXTERNAL_SUBADDRESS, BRANCH_SUBADDRESS, CHANGE_SUBADDRESS, FORWARDED_SUBADDRESS};
#[rustfmt::skip]
#[derive(
Clone, Copy, PartialEq, Eq, Default, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
)]
#[derive(Clone, Copy, PartialEq, Eq, Default, Hash, Debug, BorshSerialize, BorshDeserialize)]
pub(crate) struct OutputId(pub(crate) [u8; 32]);
impl AsRef<[u8]> for OutputId {
fn as_ref(&self) -> &[u8] {

View File

@@ -3,7 +3,7 @@ use core::future::Future;
use monero_wallet::rpc::{RpcError, Rpc as RpcTrait};
use monero_simple_request_rpc::SimpleRequestRpc;
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
use serai_client::primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
use scanner::ScannerFeed;
use signers::TransactionPublisher;

View File

@@ -10,7 +10,7 @@ use dalek_ff_group::Ed25519;
use monero_wallet::rpc::{FeeRate, RpcError};
use serai_client::{
primitives::{ExternalCoin, Amount},
primitives::{coin::ExternalCoin, balance::Amount},
networks::monero::Address,
};

View File

@@ -20,10 +20,8 @@ workspace = true
[dependencies]
group = { version = "0.13", default-features = false }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std", "borsh"] }
serai-coins-primitives = { path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
log = { version = "0.4", default-features = false, features = ["std"] }

View File

@@ -6,7 +6,6 @@ use core::{hash::Hash, fmt::Debug};
use group::GroupEncoding;
use scale::{Encode, Decode};
use borsh::{BorshSerialize, BorshDeserialize};
/// A module for task-related structs and functionality.
@@ -40,8 +39,6 @@ pub trait Id:
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Debug
+ Encode
+ Decode
+ BorshSerialize
+ BorshDeserialize
{
@@ -57,22 +54,15 @@ impl<
+ AsRef<[u8]>
+ AsMut<[u8]>
+ Debug
+ Encode
+ Decode
+ BorshSerialize
+ BorshDeserialize,
> Id for I
{
}
/// A wrapper for a group element which implements the scale/borsh traits.
/// A wrapper for a group element which implements the `borsh` traits.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct EncodableG<G: GroupEncoding>(pub G);
impl<G: GroupEncoding> Encode for EncodableG<G> {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
f(self.0.to_bytes().as_ref())
}
}
impl<G: GroupEncoding> BorshSerialize for EncodableG<G> {
fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
writer.write_all(self.0.to_bytes().as_ref())

View File

@@ -5,7 +5,7 @@ use group::GroupEncoding;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::{ExternalAddress, ExternalBalance};
use serai_primitives::{address::ExternalAddress, balance::ExternalBalance};
use crate::Id;

View File

@@ -1,10 +1,11 @@
use std::io;
use scale::{Encode, Decode, IoReader};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_primitives::ExternalBalance;
use serai_coins_primitives::OutInstructionWithBalance;
use serai_primitives::{
balance::ExternalBalance,
instructions::{OutInstruction, OutInstructionWithBalance},
};
use crate::Address;
@@ -18,9 +19,11 @@ pub struct Payment<A: Address> {
impl<A: Address> TryFrom<OutInstructionWithBalance> for Payment<A> {
type Error = ();
fn try_from(out_instruction_with_balance: OutInstructionWithBalance) -> Result<Self, ()> {
Ok(Payment {
address: out_instruction_with_balance.instruction.address.try_into().map_err(|_| ())?,
balance: out_instruction_with_balance.balance,
Ok(match out_instruction_with_balance.instruction {
OutInstruction::Transfer(address) => Payment {
address: address.try_into().map_err(|_| ())?,
balance: out_instruction_with_balance.balance,
},
})
}
}
@@ -43,14 +46,12 @@ impl<A: Address> Payment<A> {
/// Read a Payment.
pub fn read(reader: &mut impl io::Read) -> io::Result<Self> {
let address = A::deserialize_reader(reader)?;
let reader = &mut IoReader(reader);
let balance = ExternalBalance::decode(reader).map_err(io::Error::other)?;
let balance = ExternalBalance::deserialize_reader(reader).map_err(io::Error::other)?;
Ok(Self { address, balance })
}
/// Write the Payment.
pub fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
self.address.serialize(writer)?;
self.balance.encode_to(writer);
Ok(())
self.balance.serialize(writer)
}
}

View File

@@ -20,7 +20,6 @@ workspace = true
[dependencies]
# Encoders
hex = { version = "0.4", default-features = false, features = ["std"] }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
# Cryptography
@@ -36,9 +35,6 @@ serai-db = { path = "../../common/db" }
messages = { package = "serai-processor-messages", path = "../messages" }
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/primitives", default-features = false, features = ["std", "borsh"] }
serai-in-instructions-primitives = { path = "../../substrate/in-instructions/primitives", default-features = false, features = ["std", "borsh"] }
serai-coins-primitives = { path = "../../substrate/coins/primitives", default-features = false, features = ["std", "borsh"] }
primitives = { package = "serai-processor-primitives", path = "../primitives" }
scheduler-primitives = { package = "serai-processor-scheduler-primitives", path = "../scheduler/primitives" }

View File

@@ -3,12 +3,10 @@ use std::io::{Read, Write};
use group::GroupEncoding;
use scale::{Encode, Decode, IoReader};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn, create_db};
use serai_primitives::ExternalBalance;
use serai_validator_sets_primitives::Session;
use serai_primitives::{balance::ExternalBalance, validator_sets::Session};
use primitives::EncodableG;
use crate::{ScannerFeed, KeyFor, AddressFor};
@@ -94,7 +92,7 @@ impl<S: ScannerFeed> BatchDb<S> {
if let Some(ReturnInformation { address, balance }) = return_information {
buf.write_all(&[1]).unwrap();
address.serialize(&mut buf).unwrap();
balance.encode_to(&mut buf);
balance.serialize(&mut buf).unwrap();
} else {
buf.write_all(&[0]).unwrap();
}
@@ -116,7 +114,7 @@ impl<S: ScannerFeed> BatchDb<S> {
res.push((opt[0] == 1).then(|| {
let address = AddressFor::<S>::deserialize_reader(&mut buf).unwrap();
let balance = ExternalBalance::decode(&mut IoReader(&mut buf)).unwrap();
let balance = ExternalBalance::deserialize_reader(&mut buf).unwrap();
ReturnInformation { address, balance }
}));
}

View File

@@ -2,11 +2,9 @@ use core::{marker::PhantomData, future::Future};
use blake2::{digest::typenum::U32, Digest, Blake2b};
use scale::Encode;
use serai_db::{DbTxn, Db};
use serai_primitives::BlockHash;
use serai_in_instructions_primitives::{MAX_BATCH_SIZE, Batch};
use serai_primitives::{BlockHash, instructions::Batch};
use primitives::{
EncodableG,
@@ -111,12 +109,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for BatchTask<D, S> {
let mut batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
// start with empty batch
let mut batches = vec![Batch {
network,
id: batch_id,
external_network_block_hash,
instructions: vec![],
}];
let mut batches = vec![Batch::new(network, batch_id, external_network_block_hash)];
// We also track the return information for the InInstructions within a Batch in case
// they error
let mut return_information = vec![vec![]];
@@ -125,23 +118,19 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for BatchTask<D, S> {
let balance = in_instruction.balance;
let batch = batches.last_mut().unwrap();
batch.instructions.push(in_instruction);
// check if batch is over-size
if batch.encode().len() > MAX_BATCH_SIZE {
// pop the last instruction so it's back in size
let in_instruction = batch.instructions.pop().unwrap();
if batch.push_instruction(in_instruction.clone()).is_err() {
// bump the id for the new batch
batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
// make a new batch with this instruction included
batches.push(Batch {
network,
id: batch_id,
external_network_block_hash,
instructions: vec![in_instruction],
});
let mut batch = Batch::new(network, batch_id, external_network_block_hash);
batch
.push_instruction(in_instruction)
.expect("single InInstruction exceeded Batch size limit");
batches.push(batch);
// Since we're allocating a new batch, allocate a new set of return addresses for it
return_information.push(vec![]);
}
@@ -157,16 +146,16 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for BatchTask<D, S> {
// Now that we've finalized the Batches, save the information for each to the database
assert_eq!(batches.len(), return_information.len());
for (batch, return_information) in batches.iter().zip(&return_information) {
assert_eq!(batch.instructions.len(), return_information.len());
assert_eq!(batch.instructions().len(), return_information.len());
BatchDb::<S>::save_batch_info(
&mut txn,
batch.id,
batch.id(),
block_number,
session_to_sign_batch,
external_key_for_session_to_sign_batch,
Blake2b::<U32>::digest(batch.instructions.encode()).into(),
Blake2b::<U32>::digest(borsh::to_vec(&batch.instructions()).unwrap()).into(),
);
BatchDb::<S>::save_return_information(&mut txn, batch.id, return_information);
BatchDb::<S>::save_return_information(&mut txn, batch.id(), return_information);
}
for batch in batches {

View File

@@ -3,13 +3,13 @@ use std::io::{self, Read, Write};
use group::GroupEncoding;
use scale::{Encode, Decode, IoReader};
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn, create_db, db_channel};
use serai_coins_primitives::OutInstructionWithBalance;
use serai_validator_sets_primitives::Session;
use serai_in_instructions_primitives::{InInstructionWithBalance, Batch};
use serai_primitives::{
validator_sets::Session,
instructions::{InInstructionWithBalance, Batch, OutInstructionWithBalance},
};
use primitives::{EncodableG, ReceivedOutput};
@@ -56,7 +56,7 @@ impl<S: ScannerFeed> OutputWithInInstruction<S> {
(opt[0] == 1).then(|| AddressFor::<S>::deserialize_reader(reader)).transpose()?
};
let in_instruction =
InInstructionWithBalance::decode(&mut IoReader(reader)).map_err(io::Error::other)?;
InInstructionWithBalance::deserialize_reader(reader).map_err(io::Error::other)?;
Ok(Self { output, return_address, in_instruction })
}
pub(crate) fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
@@ -67,7 +67,7 @@ impl<S: ScannerFeed> OutputWithInInstruction<S> {
} else {
writer.write_all(&[0])?;
}
self.in_instruction.encode_to(writer);
self.in_instruction.serialize(writer)?;
Ok(())
}
}
@@ -76,10 +76,10 @@ create_db!(
ScannerGlobal {
StartBlock: () -> u64,
QueuedKey: <K: Encode>(key: K) -> (),
QueuedKey: <K: BorshSerialize>(key: K) -> (),
ActiveKeys: <K: Borshy>() -> Vec<SeraiKeyDbEntry<K>>,
RetireAt: <K: Encode>(key: K) -> u64,
RetireAt: <K: BorshSerialize>(key: K) -> u64,
// Highest acknowledged block
HighestAcknowledgedBlock: () -> u64,
@@ -294,7 +294,7 @@ impl<S: ScannerFeed> ScannerGlobalDb<S> {
assert!((opt[0] == 0) || (opt[0] == 1));
let address = (opt[0] == 1).then(|| AddressFor::<S>::deserialize_reader(&mut buf).unwrap());
Some((address, InInstructionWithBalance::decode(&mut IoReader(buf)).unwrap()))
Some((address, InInstructionWithBalance::deserialize_reader(&mut buf).unwrap()))
}
}
@@ -357,7 +357,7 @@ impl<S: ScannerFeed> ScanToEventualityDb<S> {
} else {
buf.write_all(&[0]).unwrap();
}
forward.in_instruction.encode_to(&mut buf);
forward.in_instruction.serialize(&mut buf).unwrap();
SerializedForwardedOutput::set(txn, forward.output.id().as_ref(), &buf);
}
@@ -454,7 +454,7 @@ impl<S: ScannerFeed> Returnable<S> {
(opt[0] == 1).then(|| AddressFor::<S>::deserialize_reader(reader)).transpose()?;
let in_instruction =
InInstructionWithBalance::decode(&mut IoReader(reader)).map_err(io::Error::other)?;
InInstructionWithBalance::deserialize_reader(reader).map_err(io::Error::other)?;
Ok(Returnable { return_address, in_instruction })
}
fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
@@ -464,7 +464,7 @@ impl<S: ScannerFeed> Returnable<S> {
} else {
writer.write_all(&[0])?;
}
self.in_instruction.encode_to(writer);
self.in_instruction.serialize(writer)?;
Ok(())
}
}
@@ -494,7 +494,7 @@ impl<S: ScannerFeed> ScanToBatchDb<S> {
block_number: u64,
data: &InInstructionData<S>,
) {
let mut buf = data.session_to_sign_batch.encode();
let mut buf = borsh::to_vec(&data.session_to_sign_batch).unwrap();
buf.extend(data.external_key_for_session_to_sign_batch.to_bytes().as_ref());
for returnable_in_instruction in &data.returnable_in_instructions {
returnable_in_instruction.write(&mut buf).unwrap();
@@ -517,7 +517,7 @@ impl<S: ScannerFeed> ScanToBatchDb<S> {
);
let mut buf = data.returnable_in_instructions.as_slice();
let session_to_sign_batch = Session::decode(&mut buf).unwrap();
let session_to_sign_batch = Session::deserialize_reader(&mut buf).unwrap();
let external_key_for_session_to_sign_batch = {
let mut external_key_for_session_to_sign_batch =
<KeyFor<S> as GroupEncoding>::Repr::default();
@@ -595,7 +595,7 @@ impl SubstrateToEventualityDb {
}
mod _public_db {
use serai_in_instructions_primitives::Batch;
use serai_primitives::instructions::Batch;
use serai_db::{Get, DbTxn, create_db, db_channel};

View File

@@ -1,6 +1,7 @@
use core::marker::PhantomData;
use scale::Encode;
use borsh::BorshSerialize;
use serai_db::{Get, DbTxn, create_db};
use primitives::{EncodableG, ReceivedOutput, Eventuality, EventualityTracker};
@@ -14,7 +15,7 @@ create_db!(
// The latest block this task has handled which was notable
LatestHandledNotableBlock: () -> u64,
SerializedEventualities: <K: Encode>(key: K) -> Vec<u8>,
SerializedEventualities: <K: BorshSerialize>(key: K) -> Vec<u8>,
AccumulatedOutput: (id: &[u8]) -> (),
}

View File

@@ -10,8 +10,10 @@ use group::GroupEncoding;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn, Db};
use serai_primitives::{ExternalNetworkId, ExternalCoin, Amount};
use serai_coins_primitives::OutInstructionWithBalance;
use serai_primitives::{
network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount,
instructions::OutInstructionWithBalance,
};
use messages::substrate::ExecutedBatch;
use primitives::{task::*, Address, ReceivedOutput, Block, Payment};

View File

@@ -1,6 +1,6 @@
use serai_db::{Get, DbTxn, create_db};
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
create_db!(
ScannerBatch {

View File

@@ -2,7 +2,7 @@ use core::{marker::PhantomData, future::Future};
use serai_db::{DbTxn, Db};
use serai_validator_sets_primitives::Session;
use serai_primitives::validator_sets::Session;
use primitives::task::{DoesNotError, ContinuallyRan};
use crate::{
@@ -70,12 +70,12 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
// Because this boolean was expanded, we lose short-circuiting. That's fine
let handover_batch = last_session != session_to_sign_batch;
let batch_after_handover_batch =
(last_session == session_to_sign_batch) && ((first_batch + 1) == batch.id);
(last_session == session_to_sign_batch) && ((first_batch + 1) == batch.id());
if handover_batch || batch_after_handover_batch {
let verified_prior_batch = substrate::last_acknowledged_batch::<S>(&txn)
// Since `batch.id = 0` in the Session(0)-never-published-a-Batch case, we don't
// check `last_acknowledged_batch >= (batch.id - 1)` but instead this
.map(|last_acknowledged_batch| (last_acknowledged_batch + 1) >= batch.id)
// Since `batch.id() = 0` in the Session(0)-never-published-a-Batch case, we don't
// check `last_acknowledged_batch >= (batch.id() - 1)` but instead this
.map(|last_acknowledged_batch| (last_acknowledged_batch + 1) >= batch.id())
// We've never verified any Batches
.unwrap_or(false);
if !verified_prior_batch {
@@ -90,7 +90,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
BatchDb::set_last_session_to_sign_batch_and_first_batch(
&mut txn,
session_to_sign_batch,
batch.id,
batch.id(),
);
}
}

View File

@@ -1,12 +1,12 @@
use core::future::Future;
use std::collections::HashMap;
use scale::Decode;
use borsh::BorshDeserialize;
use serai_db::{Get, DbTxn, Db};
use serai_in_instructions_primitives::{
Shorthand, RefundableInInstruction, InInstruction, InInstructionWithBalance,
};
#[rustfmt::skip]
use serai_primitives::instructions::{RefundableInInstruction, InInstruction, InInstructionWithBalance};
use primitives::{task::ContinuallyRan, OutputType, ReceivedOutput, Block};
@@ -55,26 +55,22 @@ fn in_instruction_from_output<S: ScannerFeed>(
let presumed_origin = output.presumed_origin();
let mut data = output.data();
let shorthand = match Shorthand::decode(&mut data) {
Ok(shorthand) => shorthand,
Err(e) => {
log::info!("data in output {} wasn't valid shorthand: {e:?}", hex::encode(output.id()));
return (presumed_origin, None);
}
};
let instruction = match RefundableInInstruction::try_from(shorthand) {
let instruction = match RefundableInInstruction::deserialize_reader(&mut data) {
Ok(instruction) => instruction,
Err(e) => {
log::info!(
"shorthand in output {} wasn't convertible to a RefundableInInstruction: {e:?}",
hex::encode(output.id())
"data in output {} wasn't a valid `RefundableInInstruction`: {e:?}",
hex::encode(output.id()),
);
return (presumed_origin, None);
}
};
(
instruction.origin.and_then(|addr| AddressFor::<S>::try_from(addr).ok()).or(presumed_origin),
instruction
.return_address
.and_then(|addr| AddressFor::<S>::try_from(addr).ok())
.or(presumed_origin),
Some(instruction.instruction),
)
}

View File

@@ -5,7 +5,7 @@ use group::GroupEncoding;
use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn, create_db, db_channel};
use serai_coins_primitives::OutInstructionWithBalance;
use serai_primitives::instructions::OutInstructionWithBalance;
use messages::substrate::ExecutedBatch;

View File

@@ -2,7 +2,7 @@ use core::{marker::PhantomData, future::Future};
use serai_db::{Get, DbTxn, Db};
use serai_coins_primitives::{OutInstruction, OutInstructionWithBalance};
use serai_primitives::instructions::{OutInstruction, OutInstructionWithBalance};
use messages::substrate::ExecutedBatch;
use primitives::task::{DoesNotError, ContinuallyRan};
@@ -150,7 +150,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for SubstrateTask<D, S> {
if let Some(batch::ReturnInformation { address, balance }) = return_information {
burns.push(OutInstructionWithBalance {
instruction: OutInstruction { address: address.into() },
instruction: OutInstruction::Transfer(address.into()),
balance,
});
}

View File

@@ -15,7 +15,7 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-machete]
ignored = ["scale", "borsh"]
ignored = ["borsh"]
[lints]
workspace = true
@@ -24,7 +24,6 @@ workspace = true
ciphersuite = { path = "../../../crypto/ciphersuite", default-features = false, features = ["std"] }
frost = { package = "modular-frost", path = "../../../crypto/frost", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-db = { path = "../../../common/db" }

View File

@@ -15,7 +15,7 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.cargo-machete]
ignored = ["scale", "borsh"]
ignored = ["borsh"]
[lints]
workspace = true
@@ -23,7 +23,6 @@ workspace = true
[dependencies]
group = { version = "0.13", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std"] }
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
serai-db = { path = "../../../common/db" }

Some files were not shown because too many files have changed in this diff Show More