mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Compare commits
16 Commits
a793aa18ef
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca93c82156 | ||
|
|
5b1875dae6 | ||
|
|
bcd68441be | ||
|
|
4ebf9ad9c7 | ||
|
|
807572199c | ||
|
|
3cdc1536c5 | ||
|
|
9e13e5ebff | ||
|
|
9b2c254eee | ||
|
|
0883479068 | ||
|
|
c5480c63be | ||
|
|
4280ee6987 | ||
|
|
91673d7ae3 | ||
|
|
927f07b62b | ||
|
|
7e774d6d2d | ||
|
|
fccd06b376 | ||
|
|
e3edc0a7fc |
24
.github/workflows/tests.yml
vendored
24
.github/workflows/tests.yml
vendored
@@ -61,7 +61,6 @@ 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 \
|
||||
@@ -83,19 +82,23 @@ jobs:
|
||||
run: |
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features \
|
||||
-p serai-primitives \
|
||||
-p serai-abi \
|
||||
-p serai-core-pallet \
|
||||
-p serai-coins-primitives \
|
||||
-p serai-coins-pallet \
|
||||
-p serai-validator-sets-pallet \
|
||||
-p serai-signals-pallet \
|
||||
-p serai-dex-pallet \
|
||||
-p serai-validator-sets-primitives \
|
||||
-p serai-validator-sets-pallet \
|
||||
-p serai-genesis-liquidity-primitives \
|
||||
-p serai-genesis-liquidity-pallet \
|
||||
-p serai-economic-security-pallet \
|
||||
-p serai-emissions-primitives \
|
||||
-p serai-emissions-pallet \
|
||||
-p serai-economic-security-pallet \
|
||||
-p serai-in-instructions-primitives \
|
||||
-p serai-in-instructions-pallet \
|
||||
-p serai-signals-primitives \
|
||||
-p serai-signals-pallet \
|
||||
-p serai-abi \
|
||||
-p serai-runtime \
|
||||
-p serai-node
|
||||
-p serai-substrate-tests
|
||||
|
||||
test-serai-client:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -106,9 +109,4 @@ jobs:
|
||||
uses: ./.github/actions/build-dependencies
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client-bitcoin
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client-ethereum
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client-monero
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client-serai
|
||||
GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client
|
||||
run: GITHUB_CI=true RUST_BACKTRACE=1 cargo test --all-features -p serai-client
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,10 +2,11 @@ target
|
||||
|
||||
# Don't commit any `Cargo.lock` which aren't the workspace's
|
||||
Cargo.lock
|
||||
!/Cargo.lock
|
||||
!./Cargo.lock
|
||||
|
||||
# Don't commit any `Dockerfile`, as they're auto-generated, except the only one which isn't
|
||||
Dockerfile
|
||||
Dockerfile.fast-epoch
|
||||
!orchestration/runtime/Dockerfile
|
||||
|
||||
.test-logs
|
||||
|
||||
898
Cargo.lock
generated
898
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
49
Cargo.toml
49
Cargo.toml
@@ -62,14 +62,13 @@ members = [
|
||||
"processor/ethereum/primitives",
|
||||
"processor/ethereum/test-primitives",
|
||||
"processor/ethereum/deployer",
|
||||
"processor/ethereum/erc20",
|
||||
"processor/ethereum/router",
|
||||
"processor/ethereum/erc20",
|
||||
"processor/ethereum",
|
||||
"processor/monero",
|
||||
|
||||
"coordinator/tributary-sdk/tendermint",
|
||||
"coordinator/tributary-sdk",
|
||||
"coordinator/cosign/types",
|
||||
"coordinator/cosign",
|
||||
"coordinator/substrate",
|
||||
"coordinator/tributary",
|
||||
@@ -78,25 +77,34 @@ members = [
|
||||
"coordinator",
|
||||
|
||||
"substrate/primitives",
|
||||
"substrate/abi",
|
||||
|
||||
"substrate/core",
|
||||
"substrate/coins",
|
||||
"substrate/validator-sets",
|
||||
"substrate/signals",
|
||||
"substrate/dex",
|
||||
"substrate/genesis-liquidity",
|
||||
"substrate/economic-security",
|
||||
"substrate/emissions",
|
||||
"substrate/in-instructions",
|
||||
"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/runtime",
|
||||
"substrate/node",
|
||||
|
||||
"substrate/client/bitcoin",
|
||||
"substrate/client/ethereum",
|
||||
"substrate/client/monero",
|
||||
"substrate/client/serai",
|
||||
"substrate/client",
|
||||
|
||||
"orchestration",
|
||||
@@ -109,7 +117,6 @@ members = [
|
||||
"tests/message-queue",
|
||||
# TODO "tests/processor",
|
||||
# TODO "tests/coordinator",
|
||||
"tests/substrate",
|
||||
# TODO "tests/full-stack",
|
||||
"tests/reproducible-runtime",
|
||||
]
|
||||
@@ -177,12 +184,10 @@ simple-request = { path = "patches/simple-request" }
|
||||
multiexp = { path = "crypto/multiexp" }
|
||||
flexible-transcript = { path = "crypto/transcript" }
|
||||
ciphersuite = { path = "patches/ciphersuite" }
|
||||
dalek-ff-group = { path = "crypto/dalek-ff-group" }
|
||||
dalek-ff-group = { path = "patches/dalek-ff-group" }
|
||||
minimal-ed448 = { path = "crypto/ed448" }
|
||||
modular-frost = { path = "crypto/frost" }
|
||||
|
||||
# Patch due to `std` now including the required functionality
|
||||
is_terminal_polyfill = { path = "./patches/is_terminal_polyfill" }
|
||||
# This has a non-deprecated `std` alternative since Rust's 2024 edition
|
||||
home = { path = "patches/home" }
|
||||
|
||||
@@ -212,11 +217,11 @@ p256 = { git = "https://github.com/kayabaNerve/elliptic-curves", rev = "4994c9ab
|
||||
librocksdb-sys = { path = "patches/librocksdb-sys" }
|
||||
|
||||
[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"
|
||||
|
||||
@@ -17,7 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
parity-db = { version = "0.5", default-features = false, features = ["arc"], optional = true }
|
||||
parity-db = { version = "0.5", default-features = false, optional = true }
|
||||
rocksdb = { version = "0.24", default-features = false, features = ["zstd"], optional = true }
|
||||
|
||||
[features]
|
||||
|
||||
@@ -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 `borsh` serializable. The return type is used to auto (de)serialize the database
|
||||
/// they must be `scale` encodable. The return type is used to auto encode and decode the database
|
||||
/// value bytes using `borsh`.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -54,10 +54,11 @@ 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(),
|
||||
&borsh::to_vec(&($($arg),*)).unwrap(),
|
||||
($($arg),*).encode()
|
||||
)
|
||||
}
|
||||
pub(crate) fn set(
|
||||
|
||||
@@ -31,6 +31,7 @@ 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" }
|
||||
@@ -42,7 +43,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"] }
|
||||
serai-client = { path = "../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
||||
|
||||
@@ -19,9 +19,11 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
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-abi = { path = "../../substrate/abi", default-features = false, features = ["std"] }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -29,5 +31,3 @@ 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" }
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use core::future::Future;
|
||||
use std::{sync::Arc, collections::HashMap};
|
||||
|
||||
use serai_abi::primitives::{
|
||||
balance::Amount, validator_sets::ExternalValidatorSet, address::SeraiAddress,
|
||||
use serai_client::{
|
||||
primitives::{SeraiAddress, Amount},
|
||||
validator_sets::primitives::ExternalValidatorSet,
|
||||
Serai,
|
||||
};
|
||||
use serai_client::Serai;
|
||||
|
||||
use serai_db::*;
|
||||
use serai_task::ContinuallyRan;
|
||||
@@ -36,7 +37,7 @@ async fn block_has_events_justifying_a_cosign(
|
||||
block_number: u64,
|
||||
) -> Result<(Block, HasEvents), String> {
|
||||
let block = serai
|
||||
.block_by_number(block_number)
|
||||
.finalized_block_by_number(block_number)
|
||||
.await
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
.ok_or_else(|| "couldn't get block which should've been finalized".to_string())?;
|
||||
@@ -66,7 +67,7 @@ impl<D: Db> ContinuallyRan for CosignIntendTask<D> {
|
||||
async move {
|
||||
let start_block_number = ScanCosignFrom::get(&self.db).unwrap_or(1);
|
||||
let latest_block_number =
|
||||
self.serai.latest_finalized_block_number().await.map_err(|e| format!("{e:?}"))?.number();
|
||||
self.serai.latest_finalized_block().await.map_err(|e| format!("{e:?}"))?.number();
|
||||
|
||||
for block_number in start_block_number ..= latest_block_number {
|
||||
let mut txn = self.db.txn();
|
||||
|
||||
@@ -7,24 +7,18 @@ use std::{sync::Arc, collections::HashMap, time::Instant};
|
||||
|
||||
use blake2::{Digest, Blake2s256};
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_abi::{
|
||||
primitives::{
|
||||
crypto::{Public, KeyPair},
|
||||
network_id::ExternalNetworkId,
|
||||
validator_sets::{Session, ExternalValidatorSet},
|
||||
address::SeraiAddress,
|
||||
},
|
||||
Block,
|
||||
use serai_client::{
|
||||
primitives::{ExternalNetworkId, SeraiAddress},
|
||||
validator_sets::primitives::{Session, ExternalValidatorSet, KeyPair},
|
||||
Public, Block, Serai, TemporalSerai,
|
||||
};
|
||||
use serai_client::{Serai, TemporalSerai};
|
||||
|
||||
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.
|
||||
@@ -34,6 +28,9 @@ mod delay;
|
||||
pub use delay::BROADCAST_FREQUENCY;
|
||||
use delay::LatestCosignedBlockNumber;
|
||||
|
||||
/// The schnorrkel context to used when signing a cosign.
|
||||
pub const COSIGN_CONTEXT: &[u8] = b"/serai/coordinator/cosign";
|
||||
|
||||
/// A 'global session', defined as all validator sets used for cosigning at a given moment.
|
||||
///
|
||||
/// We evaluate cosign faults within a global session. This ensures even if cosigners cosign
|
||||
@@ -81,6 +78,68 @@ 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
|
||||
@@ -160,8 +219,8 @@ async fn keys_for_network(
|
||||
async fn cosigning_sets(
|
||||
serai: &TemporalSerai<'_>,
|
||||
) -> Result<Vec<(ExternalValidatorSet, Public)>, String> {
|
||||
let mut sets = vec![];
|
||||
for network in ExternalNetworkId::all() {
|
||||
let mut sets = Vec::with_capacity(serai_client::primitives::EXTERNAL_NETWORKS.len());
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
let Some((session, keys)) = keys_for_network(serai, network).await? else {
|
||||
// If this network doesn't have usable keys, move on
|
||||
continue;
|
||||
@@ -287,8 +346,8 @@ impl<D: Db> Cosigning<D> {
|
||||
/// If this global session hasn't produced any notable cosigns, this will return the latest
|
||||
/// cosigns for this session.
|
||||
pub fn notable_cosigns(getter: &impl Get, global_session: [u8; 32]) -> Vec<SignedCosign> {
|
||||
let mut cosigns = vec![];
|
||||
for network in ExternalNetworkId::all() {
|
||||
let mut cosigns = Vec::with_capacity(serai_client::primitives::EXTERNAL_NETWORKS.len());
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
if let Some(cosign) = NetworksLatestCosignedBlock::get(getter, global_session, network) {
|
||||
cosigns.push(cosign);
|
||||
}
|
||||
@@ -305,7 +364,7 @@ impl<D: Db> Cosigning<D> {
|
||||
let mut cosigns = Faults::get(&self.db, faulted).expect("faulted with no faults");
|
||||
// Also include all of our recognized-as-honest cosigns in an attempt to induce fault
|
||||
// identification in those who see the faulty cosigns as honest
|
||||
for network in ExternalNetworkId::all() {
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
if let Some(cosign) = NetworksLatestCosignedBlock::get(&self.db, faulted, network) {
|
||||
if cosign.cosign.global_session == faulted {
|
||||
cosigns.push(cosign);
|
||||
@@ -317,8 +376,8 @@ impl<D: Db> Cosigning<D> {
|
||||
let Some(global_session) = evaluator::currently_evaluated_global_session(&self.db) else {
|
||||
return vec![];
|
||||
};
|
||||
let mut cosigns = vec![];
|
||||
for network in ExternalNetworkId::all() {
|
||||
let mut cosigns = Vec::with_capacity(serai_client::primitives::EXTERNAL_NETWORKS.len());
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
if let Some(cosign) = NetworksLatestCosignedBlock::get(&self.db, global_session, network) {
|
||||
cosigns.push(cosign);
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[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"] }
|
||||
@@ -1,72 +0,0 @@
|
||||
#![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()
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
||||
|
||||
serai-db = { path = "../../common/db", version = "0.1" }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||
serai-cosign = { path = "../cosign" }
|
||||
tributary-sdk = { path = "../tributary-sdk" }
|
||||
|
||||
|
||||
@@ -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"] }
|
||||
serai-client = { path = "../../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||
serai-cosign = { path = "../../cosign" }
|
||||
tributary-sdk = { path = "../../tributary-sdk" }
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::future::Future;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use serai_primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
|
||||
use serai_client::validator_sets::primitives::{MAX_KEY_SHARES_PER_SET, ExternalValidatorSet};
|
||||
|
||||
use futures_lite::FutureExt;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::collections::HashMap;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{network_id::ExternalNetworkId, validator_sets::ExternalValidatorSet};
|
||||
use serai_client::{primitives::ExternalNetworkId, validator_sets::primitives::ExternalValidatorSet};
|
||||
|
||||
use serai_db::Db;
|
||||
use tributary_sdk::{ReadWrite, TransactionTrait, Tributary, TributaryReader};
|
||||
|
||||
@@ -103,7 +103,7 @@ mod _internal_db {
|
||||
// Tributary transactions to publish from the DKG confirmation task
|
||||
TributaryTransactionsFromDkgConfirmation: (set: ExternalValidatorSet) -> Transaction,
|
||||
// Participants to remove
|
||||
RemoveParticipant: (set: ExternalValidatorSet) -> Participant,
|
||||
RemoveParticipant: (set: ExternalValidatorSet) -> u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,10 +139,11 @@ impl RemoveParticipant {
|
||||
pub(crate) fn send(txn: &mut impl DbTxn, set: ExternalValidatorSet, participant: Participant) {
|
||||
// If this set has yet to be retired, send this transaction
|
||||
if RetiredTributary::get(txn, set.network).map(|session| session.0) < Some(set.session.0) {
|
||||
_internal_db::RemoveParticipant::send(txn, set, &participant);
|
||||
_internal_db::RemoveParticipant::send(txn, set, &u16::from(participant));
|
||||
}
|
||||
}
|
||||
pub(crate) fn try_recv(txn: &mut impl DbTxn, set: ExternalValidatorSet) -> Option<Participant> {
|
||||
_internal_db::RemoveParticipant::try_recv(txn, set)
|
||||
.map(|i| Participant::new(i).expect("sent invalid participant index for removal"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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};
|
||||
@@ -478,8 +479,7 @@ pub(crate) async fn spawn_tributary<P: P2p>(
|
||||
return;
|
||||
}
|
||||
|
||||
let genesis =
|
||||
<[u8; 32]>::from(Blake2s::<U32>::digest(borsh::to_vec(&(set.serai_block, set.set)).unwrap()));
|
||||
let genesis = <[u8; 32]>::from(Blake2s::<U32>::digest((set.serai_block, set.set).encode()));
|
||||
|
||||
// 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
|
||||
|
||||
@@ -20,11 +20,12 @@ 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"] }
|
||||
serai-client = { path = "../../substrate/client", version = "0.1", default-features = false, features = ["serai", "borsh"] }
|
||||
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use dkg::Participant;
|
||||
@@ -177,13 +178,14 @@ impl Keys {
|
||||
signature_participants,
|
||||
signature,
|
||||
);
|
||||
_public_db::Keys::set(txn, set.network, &(set.session, tx));
|
||||
_public_db::Keys::set(txn, set.network, &(set.session, tx.encode()));
|
||||
}
|
||||
pub(crate) fn take(
|
||||
txn: &mut impl DbTxn,
|
||||
network: ExternalNetworkId,
|
||||
) -> Option<(Session, Transaction)> {
|
||||
_public_db::Keys::take(txn, network)
|
||||
let (session, tx) = _public_db::Keys::take(txn, network)?;
|
||||
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,12 +226,13 @@ impl SlashReports {
|
||||
slash_report,
|
||||
signature,
|
||||
);
|
||||
_public_db::SlashReports::set(txn, set.network, &(set.session, tx));
|
||||
_public_db::SlashReports::set(txn, set.network, &(set.session, tx.encode()));
|
||||
}
|
||||
pub(crate) fn take(
|
||||
txn: &mut impl DbTxn,
|
||||
network: ExternalNetworkId,
|
||||
) -> Option<(Session, Transaction)> {
|
||||
_public_db::SlashReports::take(txn, network)
|
||||
let (session, tx) = _public_db::SlashReports::take(txn, network)?;
|
||||
Some((session, <_>::decode(&mut tx.as_slice()).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
serai-db = { path = "../../common/db", version = "0.1" }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["std", "derive"] }
|
||||
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" }
|
||||
|
||||
@@ -5,7 +5,7 @@ use ciphersuite::{group::GroupEncoding, *};
|
||||
|
||||
use serai_db::{Get, DbTxn, Db};
|
||||
|
||||
use borsh::BorshDeserialize;
|
||||
use scale::Decode;
|
||||
|
||||
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_slice(), signer.to_bytes().as_slice(), order].concat(),
|
||||
[genesis.as_ref(), signer.to_bytes().as_ref(), 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_slice()).unwrap())
|
||||
.map(|bytes| Block::<T>::read::<&[u8]>(&mut bytes.as_ref()).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>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
||||
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).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>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
||||
Some(Commit::<N::SignatureScheme>::decode(&mut commit.as_ref()).unwrap())
|
||||
};
|
||||
|
||||
let mut txn_db = db.clone();
|
||||
|
||||
@@ -3,11 +3,10 @@ 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::{
|
||||
@@ -178,7 +177,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>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
|
||||
Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time
|
||||
} else {
|
||||
start_time
|
||||
};
|
||||
@@ -277,8 +276,8 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
||||
}
|
||||
|
||||
let block = TendermintBlock(block.serialize());
|
||||
let mut commit_ref = commit.as_slice();
|
||||
let Ok(commit) = Commit::<Arc<Validators>>::deserialize_reader(&mut commit_ref) else {
|
||||
let mut commit_ref = commit.as_ref();
|
||||
let Ok(commit) = Commit::<Arc<Validators>>::decode(&mut commit_ref) else {
|
||||
log::error!("sent an invalidly serialized commit");
|
||||
return false;
|
||||
};
|
||||
@@ -328,7 +327,7 @@ impl<D: Db, T: TransactionTrait, P: P2p> Tributary<D, T, P> {
|
||||
|
||||
Some(&TENDERMINT_MESSAGE) => {
|
||||
let Ok(msg) =
|
||||
SignedMessageFor::<TendermintNetwork<D, T, P>>::deserialize_reader(&mut &msg[1 ..])
|
||||
SignedMessageFor::<TendermintNetwork<D, T, P>>::decode::<&[u8]>(&mut &msg[1 ..])
|
||||
else {
|
||||
log::error!("received invalid tendermint message");
|
||||
return false;
|
||||
@@ -368,17 +367,15 @@ 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>::deserialize_reader(&mut commit.as_slice()).unwrap())
|
||||
self.commit(hash).map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).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>::deserialize_reader(&mut commit.as_slice()).unwrap().end_time
|
||||
})
|
||||
self
|
||||
.commit(hash)
|
||||
.map(|commit| Commit::<Validators>::decode(&mut commit.as_ref()).unwrap().end_time)
|
||||
}
|
||||
|
||||
pub fn locally_provided_txs_in_block(&self, hash: &[u8; 32], order: &str) -> bool {
|
||||
|
||||
@@ -21,7 +21,7 @@ use schnorr::{
|
||||
|
||||
use serai_db::Db;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use scale::{Encode, Decode};
|
||||
use tendermint::{
|
||||
SignedMessageFor,
|
||||
ext::{
|
||||
@@ -248,7 +248,7 @@ impl Weights for Validators {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
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];
|
||||
msg.serialize(&mut to_broadcast).unwrap();
|
||||
to_broadcast.extend(msg.encode());
|
||||
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 = borsh::to_vec(&commit).unwrap();
|
||||
let encoded_commit = commit.encode();
|
||||
loop {
|
||||
let block_res = self.blockchain.write().await.add_block::<Self>(
|
||||
&block,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
use borsh::BorshDeserialize;
|
||||
use scale::{Encode, Decode, IoReader};
|
||||
|
||||
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::deserialize_reader(reader)
|
||||
Evidence::decode(&mut IoReader(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(&borsh::to_vec(&ev).unwrap()),
|
||||
TendermintTx::SlashEvidence(ev) => writer.write_all(&ev.encode()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ use dalek_ff_group::Ristretto;
|
||||
use ciphersuite::*;
|
||||
use schnorr::SchnorrSignature;
|
||||
|
||||
use scale::Encode;
|
||||
|
||||
use ::tendermint::{
|
||||
ext::{Network, Signer as SignerTrait, SignatureScheme, BlockNumber, RoundNumber},
|
||||
SignedMessageFor, DataFor, Message, SignedMessage, Data, Evidence,
|
||||
@@ -198,7 +200,7 @@ pub async fn signed_from_data<N: Network>(
|
||||
round: RoundNumber(round_number),
|
||||
data,
|
||||
};
|
||||
let sig = signer.sign(&borsh::to_vec(&msg).unwrap()).await;
|
||||
let sig = signer.sign(&msg.encode()).await;
|
||||
SignedMessage { msg, sig }
|
||||
}
|
||||
|
||||
@@ -211,5 +213,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(borsh::to_vec(&signed).unwrap()))
|
||||
TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()))
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use rand::{RngCore, rngs::OsRng};
|
||||
use dalek_ff_group::Ristretto;
|
||||
use ciphersuite::*;
|
||||
|
||||
use scale::Encode;
|
||||
|
||||
use tendermint::{
|
||||
time::CanonicalInstant,
|
||||
round::RoundData,
|
||||
@@ -50,10 +52,7 @@ 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(borsh::to_vec(&signed).unwrap())),
|
||||
)
|
||||
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode())))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -71,8 +70,7 @@ 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(borsh::to_vec(&signed).unwrap()));
|
||||
let tx = TendermintTx::SlashEvidence(Evidence::InvalidValidRound(signed.encode()));
|
||||
|
||||
// should fail
|
||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||
@@ -92,10 +90,7 @@ 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(borsh::to_vec(&signed).unwrap())),
|
||||
)
|
||||
(signed.clone(), TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode())))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -125,8 +120,7 @@ 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(borsh::to_vec(&signed).unwrap()));
|
||||
let tx = TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(signed.encode()));
|
||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||
}
|
||||
}
|
||||
@@ -144,32 +138,24 @@ async fn evidence_with_prevote() {
|
||||
// it should fail for all reasons.
|
||||
let mut txs = vec![];
|
||||
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidPrecommit(
|
||||
borsh::to_vec(
|
||||
&&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||
.await,
|
||||
)
|
||||
.unwrap(),
|
||||
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||
.await
|
||||
.encode(),
|
||||
)));
|
||||
txs.push(TendermintTx::SlashEvidence(Evidence::InvalidValidRound(
|
||||
borsh::to_vec(
|
||||
&signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||
.await,
|
||||
)
|
||||
.unwrap(),
|
||||
signed_from_data::<N>(signer.clone().into(), signer_id, 0, 0, Data::Prevote(block_id))
|
||||
.await
|
||||
.encode(),
|
||||
)));
|
||||
// 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(
|
||||
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(),
|
||||
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(),
|
||||
)));
|
||||
txs
|
||||
}
|
||||
@@ -203,16 +189,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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_1.encode(),
|
||||
));
|
||||
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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
||||
|
||||
@@ -220,16 +206,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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||
}
|
||||
@@ -239,16 +225,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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_1.encode(),
|
||||
));
|
||||
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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap();
|
||||
|
||||
@@ -256,16 +242,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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
verify_tendermint_tx::<N>(&tx, &validators, commit).unwrap_err();
|
||||
}
|
||||
@@ -287,8 +273,8 @@ async fn conflicting_msgs_evidence_tx() {
|
||||
.await;
|
||||
|
||||
let tx = TendermintTx::SlashEvidence(Evidence::ConflictingMessages(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
|
||||
// update schema so that we don't fail due to invalid signature
|
||||
@@ -306,8 +292,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(
|
||||
borsh::to_vec(&signed_1).unwrap(),
|
||||
borsh::to_vec(&signed_2).unwrap(),
|
||||
signed_1.encode(),
|
||||
signed_2.encode(),
|
||||
));
|
||||
assert!(verify_tendermint_tx::<N>(&tx, &validators, commit).is_err());
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["std", "derive", "de_strict_order"] }
|
||||
parity-scale-codec = { version = "3", default-features = false, features = ["std", "derive"] }
|
||||
|
||||
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"] }
|
||||
|
||||
@@ -3,41 +3,33 @@ use std::{sync::Arc, collections::HashSet};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
|
||||
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 + BorshSerialize + BorshDeserialize
|
||||
Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode
|
||||
{
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
impl<
|
||||
V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + BorshSerialize + BorshDeserialize,
|
||||
> ValidatorId for V
|
||||
impl<V: Send + Sync + Clone + Copy + PartialEq + Eq + Hash + Debug + Encode + Decode> 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 + BorshSerialize + BorshDeserialize
|
||||
{
|
||||
}
|
||||
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize> Signature
|
||||
for S
|
||||
{
|
||||
}
|
||||
pub trait Signature: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {}
|
||||
impl<S: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode> 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, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||
pub struct BlockNumber(pub u64);
|
||||
/// A struct containing a round number, wrapped to have a distinct type.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||
pub struct RoundNumber(pub u32);
|
||||
|
||||
/// A signer for a validator.
|
||||
@@ -135,7 +127,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, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(PartialEq, Debug, Encode, Decode)]
|
||||
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,
|
||||
@@ -193,7 +185,7 @@ impl<W: Weights> Weights for Arc<W> {
|
||||
}
|
||||
|
||||
/// Simplified error enum representing a block's validity.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error, Encode, Decode)]
|
||||
pub enum BlockError {
|
||||
/// Malformed block which is wholly invalid.
|
||||
#[error("invalid block")]
|
||||
@@ -205,20 +197,9 @@ pub enum BlockError {
|
||||
}
|
||||
|
||||
/// Trait representing a Block.
|
||||
pub trait Block:
|
||||
Send + Sync + Clone + PartialEq + Eq + Debug + BorshSerialize + BorshDeserialize
|
||||
{
|
||||
pub trait Block: Send + Sync + Clone + PartialEq + Eq + Debug + Encode + Decode {
|
||||
// Type used to identify blocks. Presumably a cryptographic hash of the block.
|
||||
type Id: Send
|
||||
+ Sync
|
||||
+ Copy
|
||||
+ Clone
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ AsRef<[u8]>
|
||||
+ Debug
|
||||
+ BorshSerialize
|
||||
+ BorshDeserialize;
|
||||
type Id: Send + Sync + Copy + Clone + PartialEq + Eq + AsRef<[u8]> + Debug + Encode + Decode;
|
||||
|
||||
/// Return the deterministic, unique ID for this block.
|
||||
fn id(&self) -> Self::Id;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![expect(clippy::cast_possible_truncation)]
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use std::{
|
||||
@@ -6,7 +8,7 @@ use std::{
|
||||
collections::{VecDeque, HashMap},
|
||||
};
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use parity_scale_codec::{Encode, Decode, IoReader};
|
||||
|
||||
use futures_channel::mpsc;
|
||||
use futures_util::{
|
||||
@@ -41,14 +43,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, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode)]
|
||||
pub enum Step {
|
||||
Propose,
|
||||
Prevote,
|
||||
Precommit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Eq, Debug, Encode, Decode)]
|
||||
pub enum Data<B: Block, S: Signature> {
|
||||
Proposal(Option<RoundNumber>, B),
|
||||
Prevote(Option<B::Id>),
|
||||
@@ -90,7 +92,7 @@ impl<B: Block, S: Signature> Data<B, S> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct Message<V: ValidatorId, B: Block, S: Signature> {
|
||||
pub sender: V,
|
||||
pub block: BlockNumber,
|
||||
@@ -100,7 +102,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, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct SignedMessage<V: ValidatorId, B: Block, S: Signature> {
|
||||
pub msg: Message<V, B, S>,
|
||||
pub sig: S,
|
||||
@@ -117,18 +119,18 @@ impl<V: ValidatorId, B: Block, S: Signature> SignedMessage<V, B, S> {
|
||||
&self,
|
||||
signer: &Scheme,
|
||||
) -> bool {
|
||||
signer.verify(self.msg.sender, &borsh::to_vec(&self.msg).unwrap(), &self.sig)
|
||||
signer.verify(self.msg.sender, &self.msg.encode(), &self.sig)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub enum SlashReason {
|
||||
FailToPropose,
|
||||
InvalidBlock,
|
||||
InvalidProposer,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub enum Evidence {
|
||||
ConflictingMessages(Vec<u8>, Vec<u8>),
|
||||
InvalidPrecommit(Vec<u8>),
|
||||
@@ -159,7 +161,7 @@ pub type SignedMessageFor<N> = SignedMessage<
|
||||
>;
|
||||
|
||||
pub fn decode_signed_message<N: Network>(mut data: &[u8]) -> Option<SignedMessageFor<N>> {
|
||||
SignedMessageFor::<N>::deserialize_reader(&mut data).ok()
|
||||
SignedMessageFor::<N>::decode(&mut data).ok()
|
||||
}
|
||||
|
||||
fn decode_and_verify_signed_message<N: Network>(
|
||||
@@ -339,7 +341,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||
target: "tendermint",
|
||||
"proposer for block {}, round {round:?} was {} (me: {res})",
|
||||
self.block.number.0,
|
||||
hex::encode(borsh::to_vec(&proposer).unwrap()),
|
||||
hex::encode(proposer.encode()),
|
||||
);
|
||||
res
|
||||
}
|
||||
@@ -420,11 +422,7 @@ 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(borsh::to_vec(&validator).unwrap()),
|
||||
);
|
||||
log::info!(target: "tendermint", "Slashing validator {}", hex::encode(validator.encode()));
|
||||
self.block.slashes.insert(validator);
|
||||
self.network.slash(validator, slash_event).await;
|
||||
}
|
||||
@@ -674,7 +672,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||
self
|
||||
.slash(
|
||||
msg.sender,
|
||||
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(borsh::to_vec(&signed).unwrap())),
|
||||
SlashEvent::WithEvidence(Evidence::InvalidPrecommit(signed.encode())),
|
||||
)
|
||||
.await;
|
||||
Err(TendermintError::Malicious)?;
|
||||
@@ -745,10 +743,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||
self.broadcast(Data::Prevote(None));
|
||||
}
|
||||
self
|
||||
.slash(
|
||||
msg.sender,
|
||||
SlashEvent::WithEvidence(Evidence::InvalidValidRound(borsh::to_vec(&msg).unwrap())),
|
||||
)
|
||||
.slash(msg.sender, SlashEvent::WithEvidence(Evidence::InvalidValidRound(msg.encode())))
|
||||
.await;
|
||||
Err(TendermintError::Malicious)?;
|
||||
}
|
||||
@@ -1039,7 +1034,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||
|
||||
while !messages.is_empty() {
|
||||
self.network.broadcast(
|
||||
SignedMessageFor::<N>::deserialize_reader(&mut messages)
|
||||
SignedMessageFor::<N>::decode(&mut IoReader(&mut messages))
|
||||
.expect("saved invalid message to DB")
|
||||
).await;
|
||||
}
|
||||
@@ -1064,7 +1059,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
|
||||
} {
|
||||
if our_message {
|
||||
assert!(sig.is_none());
|
||||
sig = Some(self.signer.sign(&borsh::to_vec(&msg).unwrap()).await);
|
||||
sig = Some(self.signer.sign(&msg.encode()).await);
|
||||
}
|
||||
let sig = sig.unwrap();
|
||||
|
||||
@@ -1084,7 +1079,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![]);
|
||||
signed_msg.serialize(&mut message_tape).unwrap();
|
||||
message_tape.extend(signed_msg.encode());
|
||||
txn.put(&message_tape_key, message_tape);
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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>>>;
|
||||
@@ -37,10 +39,7 @@ impl<N: Network> MessageLog<N> {
|
||||
target: "tendermint",
|
||||
"Validator sent multiple messages for the same block + round + step"
|
||||
);
|
||||
Err(Evidence::ConflictingMessages(
|
||||
borsh::to_vec(&existing).unwrap(),
|
||||
borsh::to_vec(&signed).unwrap(),
|
||||
))?;
|
||||
Err(Evidence::ConflictingMessages(existing.encode(), signed.encode()))?;
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{
|
||||
time::{UNIX_EPOCH, SystemTime, Duration},
|
||||
};
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
|
||||
use futures_util::sink::SinkExt;
|
||||
use tokio::{sync::RwLock, time::sleep};
|
||||
@@ -89,7 +89,7 @@ impl Weights for TestWeights {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
struct TestBlock {
|
||||
id: TestBlockId,
|
||||
valid: Result<(), BlockError>,
|
||||
|
||||
@@ -21,6 +21,7 @@ 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"] }
|
||||
@@ -29,7 +30,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-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["serai", "borsh"] }
|
||||
|
||||
serai-db = { path = "../../common/db" }
|
||||
serai-task = { path = "../../common/task", version = "0.1" }
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use scale::Encode;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{address::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
|
||||
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::ExternalValidatorSet};
|
||||
|
||||
use messages::sign::{VariantSignId, SignId};
|
||||
|
||||
@@ -15,7 +16,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, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Topic {
|
||||
/// Vote to remove a participant
|
||||
RemoveParticipant {
|
||||
@@ -124,7 +125,7 @@ impl Topic {
|
||||
Topic::DkgConfirmation { attempt, round: _ } => Some({
|
||||
let id = {
|
||||
let mut id = [0; 32];
|
||||
let encoded_set = borsh::to_vec(set).unwrap();
|
||||
let encoded_set = set.encode();
|
||||
id[.. encoded_set.len()].copy_from_slice(&encoded_set);
|
||||
VariantSignId::Batch(id)
|
||||
};
|
||||
|
||||
@@ -8,9 +8,9 @@ use std::collections::HashMap;
|
||||
use ciphersuite::group::GroupEncoding;
|
||||
use dkg::Participant;
|
||||
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress,
|
||||
validator_sets::{ExternalValidatorSet, Slash},
|
||||
use serai_client::{
|
||||
primitives::SeraiAddress,
|
||||
validator_sets::primitives::{ExternalValidatorSet, Slash},
|
||||
};
|
||||
|
||||
use serai_db::*;
|
||||
|
||||
@@ -12,9 +12,10 @@ use ciphersuite::{
|
||||
use dalek_ff_group::Ristretto;
|
||||
use schnorr::SchnorrSignature;
|
||||
|
||||
use scale::Encode;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{addess::SeraiAddress, validator_sets::MAX_KEY_SHARES_PER_SET};
|
||||
use serai_client::{primitives::SeraiAddress, validator_sets::primitives::MAX_KEY_SHARES_PER_SET};
|
||||
|
||||
use messages::sign::VariantSignId;
|
||||
|
||||
@@ -28,7 +29,7 @@ use tributary_sdk::{
|
||||
use crate::db::Topic;
|
||||
|
||||
/// The round this data is for, within a signing protocol.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, BorshSerialize, BorshDeserialize)]
|
||||
pub enum SigningProtocolRound {
|
||||
/// A preprocess.
|
||||
Preprocess,
|
||||
@@ -241,20 +242,19 @@ impl TransactionTrait for Transaction {
|
||||
fn kind(&self) -> TransactionKind {
|
||||
match self {
|
||||
Transaction::RemoveParticipant { participant, signed } => TransactionKind::Signed(
|
||||
borsh::to_vec(&(b"RemoveParticipant".as_slice(), participant)).unwrap(),
|
||||
(b"RemoveParticipant", participant).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::DkgParticipation { signed, .. } => {
|
||||
TransactionKind::Signed(b"DkgParticipation".encode(), signed.to_tributary_signed(0))
|
||||
}
|
||||
Transaction::DkgConfirmationPreprocess { attempt, signed, .. } => TransactionKind::Signed(
|
||||
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
|
||||
(b"DkgConfirmation", attempt).encode(),
|
||||
signed.to_tributary_signed(0),
|
||||
),
|
||||
Transaction::DkgConfirmationShare { attempt, signed, .. } => TransactionKind::Signed(
|
||||
borsh::to_vec(b"DkgConfirmation".as_slice(), attempt).unwrap(),
|
||||
(b"DkgConfirmation", attempt).encode(),
|
||||
signed.to_tributary_signed(1),
|
||||
),
|
||||
|
||||
@@ -264,14 +264,13 @@ impl TransactionTrait for Transaction {
|
||||
Transaction::Batch { .. } => TransactionKind::Provided("Batch"),
|
||||
|
||||
Transaction::Sign { id, attempt, round, signed, .. } => TransactionKind::Signed(
|
||||
borsh::to_vec(b"Sign".as_slice(), id, attempt).unwrap(),
|
||||
(b"Sign", id, attempt).encode(),
|
||||
signed.to_tributary_signed(round.nonce()),
|
||||
),
|
||||
|
||||
Transaction::SlashReport { signed, .. } => TransactionKind::Signed(
|
||||
borsh::to_vec(b"SlashReport".as_slice()).unwrap(),
|
||||
signed.to_tributary_signed(0),
|
||||
),
|
||||
Transaction::SlashReport { signed, .. } => {
|
||||
TransactionKind::Signed(b"SlashReport".encode(), signed.to_tributary_signed(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ rand_core = { version = "0.6", default-features = false }
|
||||
sha2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
||||
blake2 = { version = "0.11.0-rc.2", default-features = false, features = ["zeroize"] }
|
||||
|
||||
crypto-bigint = { version = "0.5", default-features = false }
|
||||
prime-field = { path = "../prime-field", default-features = false }
|
||||
ciphersuite = { version = "0.4.2", path = "../ciphersuite", default-features = false }
|
||||
|
||||
|
||||
@@ -286,21 +286,3 @@ prime_field::odd_prime_field_with_specific_repr!(
|
||||
false,
|
||||
crate::ThirtyTwoArray
|
||||
);
|
||||
|
||||
impl FieldElement {
|
||||
/// This method is hidden as it's not part of our API commitment and has no guarantees made for
|
||||
/// it. It MAY panic for an undefined class of inputs.
|
||||
// TODO: `monero-oxide` requires this. PR `monero-oxide` to not require this.
|
||||
#[doc(hidden)]
|
||||
pub const fn from_u256(value: &crypto_bigint::U256) -> Self {
|
||||
let mut bytes = [0; 32];
|
||||
|
||||
let mut i = 0;
|
||||
while i < 256 {
|
||||
bytes[i / 32] |= (value.bit_vartime(i) as u8) << (i % 8);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
FieldElement::from_bytes(&bytes).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
15
deny.toml
15
deny.toml
@@ -10,6 +10,7 @@ ignore = [
|
||||
"RUSTSEC-2022-0061", # https://github.com/serai-dex/serai/227
|
||||
"RUSTSEC-2024-0370", # proc-macro-error is unmaintained
|
||||
"RUSTSEC-2024-0436", # paste is unmaintained
|
||||
"RUSTSEC-2025-0057", # fxhash is unmaintained, fixed with bytecodealliance/wasmtime/pull/11634
|
||||
]
|
||||
|
||||
[licenses]
|
||||
@@ -28,6 +29,7 @@ allow = [
|
||||
"ISC",
|
||||
"Zlib",
|
||||
"Unicode-3.0",
|
||||
# "OpenSSL", # Commented as it's not currently in-use within the Serai tree
|
||||
"CDLA-Permissive-2.0",
|
||||
|
||||
# Non-invasive copyleft
|
||||
@@ -71,7 +73,6 @@ exceptions = [
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-monero-processor" },
|
||||
|
||||
{ allow = ["AGPL-3.0-only"], name = "tributary-sdk" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-cosign-types" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-cosign" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-substrate" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-tributary" },
|
||||
@@ -81,7 +82,6 @@ exceptions = [
|
||||
|
||||
{ allow = ["AGPL-3.0-only"], name = "pallet-session" },
|
||||
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-core-pallet" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-coins-pallet" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-dex-pallet" },
|
||||
|
||||
@@ -107,7 +107,6 @@ exceptions = [
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-message-queue-tests" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-processor-tests" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-coordinator-tests" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-substrate-tests" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-full-stack-tests" },
|
||||
{ allow = ["AGPL-3.0-only"], name = "serai-reproducible-runtime-tests" },
|
||||
]
|
||||
@@ -125,22 +124,12 @@ multiple-versions = "warn"
|
||||
wildcards = "warn"
|
||||
highlight = "all"
|
||||
deny = [
|
||||
# Contains a non-reproducible binary blob
|
||||
# https://github.com/serde-rs/serde/pull/2514
|
||||
# https://github.com/serde-rs/serde/issues/2575
|
||||
{ name = "serde_derive", version = ">=1.0.172, <1.0.185" },
|
||||
# Introduced an insecure implementation of `borsh` removed with `0.15.1`
|
||||
# https://github.com/rust-lang/hashbrown/issues/576
|
||||
{ name = "hashbrown", version = "=0.15.0" },
|
||||
|
||||
# Legacy which _no one_ should use anymore
|
||||
{ name = "is-terminal", version = "*" },
|
||||
# Stop introduction into the tree without realizing it
|
||||
{ name = "once_cell_polyfill", version = "*" },
|
||||
|
||||
# Conflicts with our usage of mimalloc
|
||||
# https://github.com/serai-dex/serai/issues/690
|
||||
{ name = "tikv-jemalloc-sys", version = "*" },
|
||||
]
|
||||
|
||||
[sources]
|
||||
|
||||
@@ -46,7 +46,7 @@ serai-db = { path = "../common/db", optional = true }
|
||||
|
||||
serai-env = { path = "../common/env" }
|
||||
|
||||
serai-primitives = { path = "../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-primitives = { path = "../substrate/primitives", features = ["borsh"] }
|
||||
|
||||
[features]
|
||||
parity-db = ["serai-db/parity-db"]
|
||||
|
||||
@@ -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::network_id::ExternalNetworkId;
|
||||
pub(crate) use serai_primitives::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 ExternalNetworkId::all() {
|
||||
for network in serai_primitives::EXTERNAL_NETWORKS {
|
||||
queues.insert(
|
||||
(service, Service::Processor(network)),
|
||||
RwLock::new(Queue(db.clone(), service, Service::Processor(network))),
|
||||
@@ -213,13 +213,12 @@ async fn main() {
|
||||
};
|
||||
|
||||
// Make queues for each ExternalNetworkId
|
||||
for network in ExternalNetworkId::all() {
|
||||
for network in serai_primitives::EXTERNAL_NETWORKS {
|
||||
// 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;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ use ciphersuite::{group::GroupEncoding, FromUniformBytes, WrappedGroup, WithPref
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::network_id::ExternalNetworkId;
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Service {
|
||||
|
||||
@@ -14,6 +14,8 @@ pub fn serai(
|
||||
// Always builds in release for performance reasons
|
||||
let setup =
|
||||
mimalloc(Os::Debian).to_string() + &build_serai_service("", Os::Debian, true, "", "serai-node");
|
||||
let setup_fast_epoch = mimalloc(Os::Debian).to_string() +
|
||||
&build_serai_service("", Os::Debian, true, "fast-epoch", "serai-node");
|
||||
|
||||
let env_vars = [("KEY", hex::encode(serai_key.to_repr()))];
|
||||
let mut env_vars_str = String::new();
|
||||
@@ -38,9 +40,16 @@ CMD {env_vars_str} "/run.sh"
|
||||
|
||||
let run = os(Os::Debian, "", "serai") + &run_serai;
|
||||
let res = setup + &run;
|
||||
let res_fast_epoch = setup_fast_epoch + &run;
|
||||
|
||||
let mut serai_path = orchestration_path.to_path_buf();
|
||||
serai_path.push("serai");
|
||||
|
||||
let mut serai_fast_epoch_path = serai_path.clone();
|
||||
|
||||
serai_path.push("Dockerfile");
|
||||
serai_fast_epoch_path.push("Dockerfile.fast-epoch");
|
||||
|
||||
write_dockerfile(serai_path, &res);
|
||||
write_dockerfile(serai_fast_epoch_path, &res_fast_epoch);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
std-shims = { path = "../../common/std-shims", version = "0.1.4", default-features = false, optional = true }
|
||||
|
||||
@@ -26,7 +26,7 @@ impl<C: ciphersuite::GroupIo> Ciphersuite for C {
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_G<R: io::Read>(reader: &mut R) -> io::Result<Self::G> {
|
||||
<C as ciphersuite::GroupIo>::read_G(reader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ed25519")]
|
||||
|
||||
29
patches/dalek-ff-group/Cargo.toml
Normal file
29
patches/dalek-ff-group/Cargo.toml
Normal file
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "dalek-ff-group"
|
||||
version = "0.5.99"
|
||||
description = "ff/group bindings around curve25519-dalek"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/crypto/dalek-ff-group"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["curve25519", "ed25519", "ristretto", "dalek", "group"]
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = false }
|
||||
|
||||
crypto-bigint-05 = { package = "crypto-bigint", version = "0.5", default-features = false, features = ["zeroize"] }
|
||||
crypto-bigint = { version = "0.6", default-features = false, features = ["zeroize"] }
|
||||
prime-field = { path = "../../crypto/prime-field", default-features = false }
|
||||
|
||||
[features]
|
||||
alloc = ["dalek-ff-group/alloc", "prime-field/alloc"]
|
||||
std = ["alloc", "dalek-ff-group/std", "prime-field/std"]
|
||||
default = ["std"]
|
||||
4
patches/dalek-ff-group/README.md
Normal file
4
patches/dalek-ff-group/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Dalek FF/Group
|
||||
|
||||
Patch for the `crates.io` `dalek-ff-group` to use the in-tree `dalek-ff-group`,
|
||||
resolving relevant breaking changes made since.
|
||||
44
patches/dalek-ff-group/src/lib.rs
Normal file
44
patches/dalek-ff-group/src/lib.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
#![allow(deprecated)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![allow(clippy::redundant_closure_call)]
|
||||
|
||||
pub use dalek_ff_group::{Scalar, EdwardsPoint, RistrettoPoint, Ed25519, Ristretto};
|
||||
|
||||
type ThirtyTwoArray = [u8; 32];
|
||||
prime_field::odd_prime_field_with_specific_repr!(
|
||||
FieldElement,
|
||||
"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
|
||||
"02",
|
||||
false,
|
||||
crate::ThirtyTwoArray
|
||||
);
|
||||
|
||||
impl FieldElement {
|
||||
/// Create a FieldElement from a `crypto_bigint::U256`.
|
||||
///
|
||||
/// This will reduce the `U256` by the modulus, into a member of the field.
|
||||
#[deprecated]
|
||||
pub const fn from_u256(u256: &crypto_bigint_05::U256) -> Self {
|
||||
const MODULUS: crypto_bigint::U256 = crypto_bigint::U256::from_be_hex(
|
||||
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
|
||||
);
|
||||
let mut u256 = crypto_bigint::U256::from_words(*u256.as_words());
|
||||
loop {
|
||||
let result = FieldElement::from_bytes(&u256.to_le_bytes());
|
||||
if let Some(result) = result {
|
||||
return result;
|
||||
}
|
||||
u256 = u256.wrapping_sub(&MODULUS);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `FieldElement` from the reduction of a 512-bit number.
|
||||
///
|
||||
/// The bytes are interpreted in little-endian format.
|
||||
#[deprecated]
|
||||
pub fn wide_reduce(value: [u8; 64]) -> Self {
|
||||
<FieldElement as prime_field::ff::FromUniformBytes<_>>::from_uniform_bytes(&value)
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,5 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
darling = { version = "0.21" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "directories-next"
|
||||
version = "2.0.99"
|
||||
version = "2.0.0"
|
||||
description = "Patch from directories-next back to directories"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/serai-dex/serai/tree/develop/patches/directories-next"
|
||||
@@ -12,7 +12,5 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
directories = "6"
|
||||
directories = "5"
|
||||
|
||||
@@ -12,7 +12,5 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
std = []
|
||||
|
||||
@@ -12,7 +12,5 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
std = []
|
||||
|
||||
@@ -12,8 +12,6 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
std = []
|
||||
serde = []
|
||||
|
||||
@@ -12,8 +12,6 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
std = []
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
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_polyfill"
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = []
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
@@ -1 +0,0 @@
|
||||
pub use std::io::IsTerminal;
|
||||
@@ -11,5 +11,3 @@ edition = "2021"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
@@ -7,17 +7,16 @@ repository = "https://github.com/serai-dex/serai/tree/develop/patches/simple-req
|
||||
authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
||||
keywords = ["nostd", "no_std", "alloc", "io"]
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
rust-version = "1.71"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
hyper-util = { version = "0.1", default-features = false, features = ["tokio"] }
|
||||
simple-request = { path = "../../common/request", features = ["tokio"] }
|
||||
simple-request = { path = "../../common/request", default-features = false, features = ["tokio"] }
|
||||
|
||||
[features]
|
||||
tls = ["simple-request/tls"]
|
||||
basic-auth = ["simple-request/basic-auth"]
|
||||
|
||||
@@ -1,21 +1,2 @@
|
||||
use hyper_util::rt::tokio::TokioExecutor;
|
||||
pub use simple_request::{hyper, Error, Request};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Client(simple_request::Client<TokioExecutor>);
|
||||
|
||||
pub type Response<'a> = simple_request::Response<'a, TokioExecutor>;
|
||||
|
||||
impl Client {
|
||||
pub fn with_connection_pool() -> Client {
|
||||
Self(simple_request::Client::with_connection_pool().unwrap())
|
||||
}
|
||||
|
||||
pub fn without_connection_pool(host: &str) -> Result<Client, Error> {
|
||||
simple_request::Client::without_connection_pool(host).map(Self)
|
||||
}
|
||||
|
||||
pub async fn request<R: Into<Request>>(&self, request: R) -> Result<Response<'_>, Error> {
|
||||
self.0.request(request).await
|
||||
}
|
||||
}
|
||||
pub use simple_request::{hyper, Error, Request, TokioClient as Client};
|
||||
pub type Response<'a> = simple_request::Response<'a, hyper_util::rt::tokio::TokioExecutor>;
|
||||
|
||||
@@ -13,7 +13,8 @@ rust-version = "1.65"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
std-shims = { path = "../../common/std-shims", default-features = false, features = ["alloc"] }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub extern crate alloc;
|
||||
|
||||
@@ -12,7 +12,5 @@ edition = "2021"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
thiserror = { version = "2", features = ["std"] }
|
||||
|
||||
@@ -20,13 +20,14 @@ 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-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-cosign = { package = "serai-cosign-types", path = "../../coordinator/cosign/types" }
|
||||
serai-client = { path = "../../substrate/client", default-features = false }
|
||||
serai-cosign = { path = "../../coordinator/cosign" }
|
||||
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
env_logger = { version = "0.10", default-features = false, features = ["humantime"] }
|
||||
|
||||
@@ -3,10 +3,10 @@ use std::sync::{LazyLock, Arc, Mutex};
|
||||
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use serai_primitives::{
|
||||
crypto::Signature,
|
||||
validator_sets::{Session, SlashReport},
|
||||
instructions::SignedBatch,
|
||||
use serai_client::{
|
||||
primitives::Signature,
|
||||
validator_sets::primitives::{Session, SlashReport},
|
||||
in_instructions::primitives::SignedBatch,
|
||||
};
|
||||
|
||||
use serai_cosign::SignedCosign;
|
||||
@@ -34,7 +34,7 @@ static SEND_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
|
||||
|
||||
db_channel! {
|
||||
ProcessorBinCoordinator {
|
||||
SentCoordinatorMessages: () -> messages::ProcessorMessage,
|
||||
SentCoordinatorMessages: () -> Vec<u8>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, msg);
|
||||
SentCoordinatorMessages::send(&mut txn, &borsh::to_vec(msg).unwrap());
|
||||
txn.commit();
|
||||
self
|
||||
.sent_message
|
||||
@@ -114,9 +114,12 @@ impl Coordinator {
|
||||
let mut txn = db.txn();
|
||||
match SentCoordinatorMessages::try_recv(&mut txn) {
|
||||
Some(msg) => {
|
||||
let metadata =
|
||||
Metadata { from: service, to: Service::Coordinator, intent: msg.intent() };
|
||||
message_queue.queue_with_retry(metadata, borsh::to_vec(&msg).unwrap()).await;
|
||||
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;
|
||||
txn.commit();
|
||||
}
|
||||
None => {
|
||||
|
||||
@@ -8,7 +8,7 @@ use ciphersuite::{
|
||||
};
|
||||
use dkg::Curves;
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_client::validator_sets::primitives::Session;
|
||||
|
||||
use serai_env as env;
|
||||
use serai_db::{Get, DbTxn, Db as DbTrait, create_db, db_channel};
|
||||
|
||||
@@ -20,6 +20,7 @@ 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"] }
|
||||
@@ -30,8 +31,7 @@ frost = { package = "modular-frost", path = "../../crypto/frost", default-featur
|
||||
secp256k1 = { version = "0.29", default-features = false, features = ["std", "global-context", "rand-std"] }
|
||||
bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, features = ["std", "rpc"] }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client-bitcoin = { path = "../../substrate/client/bitcoin", default-features = false }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["bitcoin"] }
|
||||
|
||||
zalloc = { path = "../../common/zalloc" }
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -6,7 +6,7 @@ use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use bitcoin_serai::bitcoin::block::{Header, Block as BBlock};
|
||||
|
||||
use serai_client_bitcoin::Address;
|
||||
use serai_client::networks::bitcoin::Address;
|
||||
|
||||
use serai_db::Db;
|
||||
use primitives::{ReceivedOutput, EventualityTracker};
|
||||
|
||||
@@ -10,15 +10,14 @@ use bitcoin_serai::{
|
||||
wallet::ReceivedOutput as WalletOutput,
|
||||
};
|
||||
|
||||
use scale::{Encode, Decode, IoReader};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serai_db::Get;
|
||||
|
||||
use serai_primitives::{
|
||||
coin::ExternalCoin,
|
||||
balance::{Amount, ExternalBalance},
|
||||
address::ExternalAddress,
|
||||
use serai_client::{
|
||||
primitives::{ExternalCoin, Amount, ExternalBalance, ExternalAddress},
|
||||
networks::bitcoin::Address,
|
||||
};
|
||||
use serai_client_bitcoin::Address;
|
||||
|
||||
use primitives::{OutputType, ReceivedOutput};
|
||||
|
||||
@@ -27,7 +26,7 @@ use crate::{
|
||||
scan::{offsets_for_key, presumed_origin, extract_serai_data},
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize)]
|
||||
pub(crate) struct OutputId([u8; 36]);
|
||||
impl Default for OutputId {
|
||||
fn default() -> Self {
|
||||
@@ -140,7 +139,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);
|
||||
presumed_origin.serialize(writer)?;
|
||||
writer.write_all(&presumed_origin.encode())?;
|
||||
self.output.write(writer)?;
|
||||
writer.write_all(&u16::try_from(self.data.len()).unwrap().to_le_bytes())?;
|
||||
writer.write_all(&self.data)
|
||||
@@ -150,7 +149,7 @@ impl ReceivedOutput<<Secp256k1 as WrappedGroup>::G, Address> for Output {
|
||||
Ok(Output {
|
||||
kind: OutputType::read(reader)?,
|
||||
presumed_origin: {
|
||||
Option::<ExternalAddress>::deserialize_reader(&mut reader)
|
||||
Option::<ExternalAddress>::decode(&mut IoReader(&mut reader))
|
||||
.map_err(|e| io::Error::other(format!("couldn't decode ExternalAddress: {e:?}")))?
|
||||
.map(|address| {
|
||||
Address::try_from(address)
|
||||
|
||||
@@ -18,7 +18,7 @@ use bitcoin_serai::{
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_client_bitcoin::Address;
|
||||
use serai_client::networks::bitcoin::Address;
|
||||
|
||||
use crate::output::OutputId;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::future::Future;
|
||||
|
||||
use bitcoin_serai::rpc::{RpcError, Rpc as BRpc};
|
||||
|
||||
use serai_primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
|
||||
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
|
||||
|
||||
use serai_db::Db;
|
||||
use scanner::ScannerFeed;
|
||||
|
||||
@@ -12,7 +12,7 @@ use bitcoin_serai::{
|
||||
wallet::Scanner,
|
||||
};
|
||||
|
||||
use serai_client_bitcoin::Address;
|
||||
use serai_client::networks::bitcoin::Address;
|
||||
|
||||
use serai_db::Get;
|
||||
use primitives::OutputType;
|
||||
|
||||
@@ -8,8 +8,10 @@ use bitcoin_serai::{
|
||||
wallet::{TransactionError, SignableTransaction as BSignableTransaction, p2tr_script_buf},
|
||||
};
|
||||
|
||||
use serai_primitives::{coin::ExternalCoin, balance::Amount};
|
||||
use serai_client_bitcoin::Address;
|
||||
use serai_client::{
|
||||
primitives::{ExternalCoin, Amount},
|
||||
networks::bitcoin::Address,
|
||||
};
|
||||
|
||||
use serai_db::Db;
|
||||
use primitives::{OutputType, ReceivedOutput, Payment};
|
||||
|
||||
@@ -21,6 +21,7 @@ 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"] }
|
||||
@@ -39,8 +40,7 @@ alloy-simple-request-transport = { path = "../../networks/ethereum/alloy-simple-
|
||||
alloy-rpc-client = { version = "1", default-features = false }
|
||||
alloy-provider = { version = "1", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client-ethereum = { path = "../../substrate/client/ethereum", default-features = false }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["ethereum"] }
|
||||
|
||||
zalloc = { path = "../../common/zalloc" }
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -41,8 +41,8 @@ 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 }
|
||||
|
||||
serai-primitives = { path = "../../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client-ethereum = { path = "../../../substrate/client/ethereum", 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"] }
|
||||
|
||||
|
||||
@@ -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 encoded `RefundableInInstruction` for Serai to decode and handle
|
||||
/// @param instruction The Shorthand-encoded InInstruction 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 encoded `RefundableInInstruction` for Serai to associate with this
|
||||
* transfer in
|
||||
* @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
|
||||
* transfer in
|
||||
*/
|
||||
// Re-entrancy doesn't bork this function
|
||||
// slither-disable-next-line reentrancy-events
|
||||
|
||||
@@ -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 encoded `RefundableInInstruction` for Serai to associate with this
|
||||
* @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
|
||||
* transfer in
|
||||
*/
|
||||
// This function doesn't require nonReentrant as re-entrancy isn't an issue with this function
|
||||
|
||||
@@ -21,8 +21,10 @@ use alloy_rpc_types_eth::{BlockId, Log, Filter, TransactionInput, TransactionReq
|
||||
use alloy_transport::{TransportErrorKind, RpcError};
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
use serai_primitives::instructions::RefundableInInstruction;
|
||||
use serai_client_ethereum::Address as SeraiAddress;
|
||||
use scale::Encode;
|
||||
use serai_client::{
|
||||
in_instructions::primitives::Shorthand, networks::ethereum::Address as SeraiAddress,
|
||||
};
|
||||
|
||||
use ethereum_primitives::LogIndex;
|
||||
use ethereum_schnorr::{PublicKey, Signature};
|
||||
@@ -349,29 +351,20 @@ 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: &RefundableInInstruction,
|
||||
) -> TxLegacy {
|
||||
pub fn in_instruction(&self, coin: Coin, amount: U256, in_instruction: &Shorthand) -> TxLegacy {
|
||||
match coin {
|
||||
Coin::Ether => TxLegacy {
|
||||
to: self.address.into(),
|
||||
input: abi::inInstructionCall::new((
|
||||
coin.into(),
|
||||
amount,
|
||||
borsh::to_vec(&in_instruction).unwrap().into(),
|
||||
))
|
||||
.abi_encode()
|
||||
.into(),
|
||||
input: abi::inInstructionCall::new((coin.into(), amount, in_instruction.encode().into()))
|
||||
.abi_encode()
|
||||
.into(),
|
||||
value: amount,
|
||||
..Default::default()
|
||||
},
|
||||
@@ -380,7 +373,7 @@ impl Router {
|
||||
input: erc20::transferWithInInstructionCall::new((
|
||||
self.address,
|
||||
amount,
|
||||
borsh::to_vec(&in_instruction).unwrap().into(),
|
||||
in_instruction.encode().into(),
|
||||
))
|
||||
.abi_encode()
|
||||
.into(),
|
||||
|
||||
@@ -5,9 +5,12 @@ use alloy_sol_types::SolCall;
|
||||
|
||||
use alloy_consensus::{TxLegacy, Signed};
|
||||
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress,
|
||||
instructions::{InInstruction as SeraiInInstruction, RefundableInInstruction},
|
||||
use scale::Encode;
|
||||
use serai_client::{
|
||||
primitives::SeraiAddress,
|
||||
in_instructions::primitives::{
|
||||
InInstruction as SeraiInInstruction, RefundableInInstruction, Shorthand,
|
||||
},
|
||||
};
|
||||
|
||||
use ethereum_primitives::LogIndex;
|
||||
@@ -15,14 +18,14 @@ use ethereum_primitives::LogIndex;
|
||||
use crate::{InInstruction, tests::*};
|
||||
|
||||
impl Test {
|
||||
pub(crate) fn in_instruction() -> RefundableInInstruction {
|
||||
RefundableInInstruction {
|
||||
return_address: None,
|
||||
instruction: SeraiInInstruction::Transfer { to: SeraiAddress([0xff; 32]) },
|
||||
}
|
||||
pub(crate) fn in_instruction() -> Shorthand {
|
||||
Shorthand::Raw(RefundableInInstruction {
|
||||
origin: None,
|
||||
instruction: SeraiInInstruction::Transfer(SeraiAddress([0xff; 32])),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn eth_in_instruction_tx(&self) -> (Coin, U256, RefundableInInstruction, TxLegacy) {
|
||||
pub(crate) fn eth_in_instruction_tx(&self) -> (Coin, U256, Shorthand, TxLegacy) {
|
||||
let coin = Coin::Ether;
|
||||
let amount = U256::from(1);
|
||||
let shorthand = Self::in_instruction();
|
||||
@@ -39,7 +42,7 @@ impl Test {
|
||||
tx: Signed<TxLegacy>,
|
||||
coin: Coin,
|
||||
amount: U256,
|
||||
shorthand: &RefundableInInstruction,
|
||||
shorthand: &Shorthand,
|
||||
) {
|
||||
let receipt = ethereum_test_primitives::publish_tx(&self.provider, tx.clone()).await;
|
||||
assert!(receipt.status());
|
||||
@@ -78,7 +81,7 @@ impl Test {
|
||||
from: tx.recover_signer().unwrap(),
|
||||
coin,
|
||||
amount,
|
||||
data: borsh::to_vec(&shorthand).unwrap(),
|
||||
data: shorthand.encode(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -137,13 +140,9 @@ 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,
|
||||
borsh::to_vec(&shorthand).unwrap().into(),
|
||||
))
|
||||
.abi_encode()
|
||||
.into(),
|
||||
input: crate::abi::inInstructionCall::new((coin.into(), amount, shorthand.encode().into()))
|
||||
.abi_encode()
|
||||
.into(),
|
||||
};
|
||||
|
||||
// If no `approve` was granted, this should fail
|
||||
|
||||
@@ -20,7 +20,7 @@ use alloy_provider::{
|
||||
|
||||
use alloy_node_bindings::{Anvil, AnvilInstance};
|
||||
|
||||
use serai_client_ethereum::{ContractDeployment, Address as SeraiEthereumAddress};
|
||||
use serai_client::networks::ethereum::{ContractDeployment, Address as SeraiEthereumAddress};
|
||||
|
||||
use ethereum_schnorr::{PublicKey, Signature};
|
||||
use ethereum_deployer::Deployer;
|
||||
|
||||
@@ -14,7 +14,7 @@ use alloy_simple_request_transport::SimpleRequest;
|
||||
use alloy_rpc_client::ClientBuilder;
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_client::validator_sets::primitives::Session;
|
||||
|
||||
use serai_env as env;
|
||||
use serai_db::{Get, DbTxn, create_db};
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
use ciphersuite::*;
|
||||
use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use serai_client_ethereum::Address;
|
||||
use serai_client::networks::ethereum::Address;
|
||||
|
||||
use primitives::{ReceivedOutput, EventualityTracker};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use alloy_core::primitives::{FixedBytes, Address};
|
||||
|
||||
use serai_primitives::balance::Amount;
|
||||
use serai_client::primitives::Amount;
|
||||
|
||||
pub(crate) mod output;
|
||||
pub(crate) mod transaction;
|
||||
|
||||
@@ -5,14 +5,13 @@ use ciphersuite_kp256::Secp256k1;
|
||||
|
||||
use alloy_core::primitives::U256;
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{
|
||||
network_id::ExternalNetworkId,
|
||||
coin::ExternalCoin,
|
||||
balance::{Amount, ExternalBalance},
|
||||
use serai_client::{
|
||||
primitives::{ExternalNetworkId, ExternalCoin, Amount, ExternalBalance},
|
||||
networks::ethereum::Address,
|
||||
};
|
||||
use serai_client_ethereum::Address;
|
||||
|
||||
use primitives::{OutputType, ReceivedOutput};
|
||||
use ethereum_router::{Coin as EthereumCoin, InInstruction as EthereumInInstruction};
|
||||
@@ -40,7 +39,9 @@ fn amount_to_serai_amount(coin: ExternalCoin, amount: U256) -> Amount {
|
||||
Amount(u64::try_from(amount / divisor).unwrap())
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub(crate) struct OutputId(pub(crate) [u8; 40]);
|
||||
impl Default for OutputId {
|
||||
fn default() -> Self {
|
||||
|
||||
@@ -5,7 +5,7 @@ use frost::dkg::ThresholdKeys;
|
||||
|
||||
use alloy_core::primitives::{U256, Address as EthereumAddress};
|
||||
|
||||
use serai_client_ethereum::Address;
|
||||
use serai_client::networks::ethereum::Address;
|
||||
|
||||
use scheduler::SignableTransaction;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use alloy_rpc_types_eth::{Header, BlockNumberOrTag};
|
||||
use alloy_transport::{RpcError, TransportErrorKind};
|
||||
use alloy_provider::{Provider, RootProvider};
|
||||
|
||||
use serai_primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
|
||||
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
|
||||
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ use std::collections::HashMap;
|
||||
|
||||
use alloy_core::primitives::U256;
|
||||
|
||||
use serai_primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::ExternalBalance};
|
||||
use serai_client_ethereum::Address;
|
||||
use serai_client::{
|
||||
primitives::{ExternalNetworkId, ExternalCoin, ExternalBalance},
|
||||
networks::ethereum::Address,
|
||||
};
|
||||
|
||||
use serai_db::Db;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["borsh"]
|
||||
ignored = ["borsh", "scale"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -25,10 +25,11 @@ rand_core = { version = "0.6", default-features = false, features = ["std", "get
|
||||
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/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" }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use frost::{
|
||||
sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine},
|
||||
};
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
|
||||
use serai_db::{Get, DbTxn, Db, create_db};
|
||||
use messages::sign::{VariantSignId, SignId, ProcessorMessage};
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::collections::HashMap;
|
||||
|
||||
use frost::{Participant, sign::PreprocessMachine};
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
|
||||
use serai_db::{DbTxn, Db};
|
||||
use messages::sign::{VariantSignId, ProcessorMessage, CoordinatorMessage};
|
||||
|
||||
@@ -14,6 +14,9 @@ rust-version = "1.89"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["scale"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -34,9 +37,10 @@ dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features =
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false, features = ["ristretto"] }
|
||||
|
||||
# Substrate
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-validator-sets-primitives = { path = "../../substrate/validator-sets/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
|
||||
|
||||
@@ -6,7 +6,7 @@ use zeroize::Zeroizing;
|
||||
use ciphersuite::{group::GroupEncoding, *};
|
||||
use dkg::*;
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serai_db::{Get, DbTxn};
|
||||
@@ -44,7 +44,7 @@ pub(crate) struct Participations {
|
||||
}
|
||||
|
||||
mod _db {
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
|
||||
use serai_db::{Get, DbTxn, create_db};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{
|
||||
|
||||
use dkg::*;
|
||||
|
||||
use serai_primitives::validator_sets::KeyShares;
|
||||
use serai_validator_sets_primitives::MAX_KEY_SHARES_PER_SET;
|
||||
|
||||
/// A cache of the generators used by the eVRF DKG.
|
||||
///
|
||||
@@ -29,8 +29,8 @@ pub(crate) fn generators<C: 'static + Curves>() -> &'static Generators<C> {
|
||||
.or_insert_with(|| {
|
||||
// If we haven't prior needed generators for this Ciphersuite, generate new ones
|
||||
Box::leak(Box::new(Generators::<C>::new(
|
||||
(KeyShares::MAX_PER_SET * 2 / 3) + 1,
|
||||
KeyShares::MAX_PER_SET,
|
||||
(MAX_KEY_SHARES_PER_SET * 2 / 3) + 1,
|
||||
MAX_KEY_SHARES_PER_SET,
|
||||
)))
|
||||
})
|
||||
.downcast_ref()
|
||||
|
||||
@@ -17,7 +17,7 @@ use ciphersuite::{
|
||||
};
|
||||
use dkg::*;
|
||||
|
||||
use serai_primitives::validator_sets::Session;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
use messages::key_gen::*;
|
||||
|
||||
use serai_db::{Get, DbTxn};
|
||||
|
||||
@@ -19,9 +19,15 @@ 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"] }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-cosign = { package = "serai-cosign-types", path = "../../coordinator/cosign/types" }
|
||||
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-cosign = { path = "../../coordinator/cosign", default-features = false }
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
#![expect(clippy::cast_possible_truncation)]
|
||||
|
||||
use core::fmt;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use dkg::Participant;
|
||||
|
||||
use serai_primitives::{
|
||||
BlockHash,
|
||||
crypto::KeyPair,
|
||||
validator_sets::{Session, SlashReport},
|
||||
instructions::{SignedBatch, OutInstructionWithBalance},
|
||||
};
|
||||
use serai_primitives::BlockHash;
|
||||
use validator_sets_primitives::{Session, KeyPair, SlashReport};
|
||||
use coins_primitives::OutInstructionWithBalance;
|
||||
use in_instructions_primitives::SignedBatch;
|
||||
|
||||
use serai_cosign::{Cosign, SignedCosign};
|
||||
|
||||
@@ -148,7 +149,7 @@ pub mod key_gen {
|
||||
pub mod sign {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, BorshSerialize, BorshDeserialize)]
|
||||
pub enum VariantSignId {
|
||||
Cosign(u64),
|
||||
Batch([u8; 32]),
|
||||
@@ -172,7 +173,9 @@ pub mod sign {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub struct SignId {
|
||||
pub session: Session,
|
||||
pub id: VariantSignId,
|
||||
@@ -381,7 +384,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, .. } => {
|
||||
@@ -402,19 +405,17 @@ impl CoordinatorMessage {
|
||||
};
|
||||
|
||||
let mut res = vec![COORDINATOR_UID, TYPE_SIGN_UID, sub];
|
||||
res.extend(borsh::to_vec(&id).unwrap());
|
||||
res.extend(id.encode());
|
||||
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, borsh::to_vec(&cosign.block_number).unwrap())
|
||||
(0, cosign.block_number.encode())
|
||||
}
|
||||
// We only sign one slash report, and Reattempt is a separate message
|
||||
coordinator::CoordinatorMessage::SignSlashReport { session, .. } => {
|
||||
(1, borsh::to_vec(&session).unwrap())
|
||||
}
|
||||
coordinator::CoordinatorMessage::SignSlashReport { session, .. } => (1, session.encode()),
|
||||
};
|
||||
|
||||
let mut res = vec![COORDINATOR_UID, TYPE_COORDINATOR_UID, sub];
|
||||
@@ -423,14 +424,10 @@ impl CoordinatorMessage {
|
||||
}
|
||||
CoordinatorMessage::Substrate(msg) => {
|
||||
let (sub, id) = match msg {
|
||||
substrate::CoordinatorMessage::SetKeys { session, .. } => {
|
||||
(0, borsh::to_vec(&session).unwrap())
|
||||
}
|
||||
substrate::CoordinatorMessage::SlashesReported { session } => {
|
||||
(1, borsh::to_vec(&session).unwrap())
|
||||
}
|
||||
substrate::CoordinatorMessage::SetKeys { session, .. } => (0, session.encode()),
|
||||
substrate::CoordinatorMessage::SlashesReported { session } => (1, session.encode()),
|
||||
substrate::CoordinatorMessage::Block { serai_block_number, .. } => {
|
||||
(2, borsh::to_vec(&serai_block_number).unwrap())
|
||||
(2, serai_block_number.encode())
|
||||
}
|
||||
};
|
||||
|
||||
@@ -455,10 +452,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 } => {
|
||||
@@ -474,11 +471,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, borsh::to_vec(&(session, u16::from(*participant))).unwrap())
|
||||
(0, (session, u16::from(*participant)).encode())
|
||||
}
|
||||
// Unique since SignId
|
||||
sign::ProcessorMessage::Preprocesses { id, .. } => (1, borsh::to_vec(&id).unwrap()),
|
||||
sign::ProcessorMessage::Shares { id, .. } => (2, borsh::to_vec(&id).unwrap()),
|
||||
sign::ProcessorMessage::Preprocesses { id, .. } => (1, id.encode()),
|
||||
sign::ProcessorMessage::Shares { id, .. } => (2, id.encode()),
|
||||
};
|
||||
|
||||
let mut res = vec![PROCESSOR_UID, TYPE_SIGN_UID, sub];
|
||||
@@ -488,14 +485,10 @@ impl ProcessorMessage {
|
||||
ProcessorMessage::Coordinator(msg) => {
|
||||
let (sub, id) = match msg {
|
||||
coordinator::ProcessorMessage::CosignedBlock { cosign } => {
|
||||
(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())
|
||||
(0, cosign.cosign.block_hash.encode())
|
||||
}
|
||||
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];
|
||||
@@ -504,9 +497,7 @@ impl ProcessorMessage {
|
||||
}
|
||||
ProcessorMessage::Substrate(msg) => {
|
||||
let (sub, id) = match msg {
|
||||
substrate::ProcessorMessage::SubstrateBlockAck { block, .. } => {
|
||||
(0, borsh::to_vec(&block).unwrap())
|
||||
}
|
||||
substrate::ProcessorMessage::SubstrateBlockAck { block, .. } => (0, block.encode()),
|
||||
};
|
||||
|
||||
let mut res = vec![PROCESSOR_UID, TYPE_SUBSTRATE_UID, sub];
|
||||
|
||||
@@ -21,6 +21,7 @@ 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"] }
|
||||
@@ -28,11 +29,10 @@ dalek-ff-group = { path = "../../crypto/dalek-ff-group", default-features = fals
|
||||
dkg = { package = "dkg-evrf", path = "../../crypto/dkg/evrf", default-features = false, features = ["std", "ed25519"] }
|
||||
frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
|
||||
|
||||
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "030c60974f0f0306849c1795bca854a3bbb757b4", default-features = false, features = ["std", "multisig"] }
|
||||
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "030c60974f0f0306849c1795bca854a3bbb757b4", default-features = false }
|
||||
monero-wallet = { git = "https://github.com/monero-oxide/monero-oxide", rev = "4b7191e3da20e42f839bfd6d706f75364004a4b8", default-features = false, features = ["std", "multisig"] }
|
||||
monero-simple-request-rpc = { git = "https://github.com/monero-oxide/monero-oxide", rev = "4b7191e3da20e42f839bfd6d706f75364004a4b8", default-features = false }
|
||||
|
||||
serai-primitives = { path = "../../substrate/primitives", default-features = false, features = ["std"] }
|
||||
serai-client-monero = { path = "../../substrate/client/monero", default-features = false }
|
||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["monero"] }
|
||||
|
||||
zalloc = { path = "../../common/zalloc" }
|
||||
log = { version = "0.4", default-features = false, features = ["std"] }
|
||||
|
||||
@@ -8,7 +8,7 @@ use monero_wallet::{
|
||||
GuaranteedScanner,
|
||||
};
|
||||
|
||||
use serai_client_monero::Address;
|
||||
use serai_client::networks::monero::Address;
|
||||
|
||||
use primitives::{ReceivedOutput, EventualityTracker};
|
||||
use crate::{
|
||||
|
||||
@@ -5,19 +5,22 @@ use dalek_ff_group::Ed25519;
|
||||
|
||||
use monero_wallet::WalletOutput;
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{
|
||||
coin::ExternalCoin,
|
||||
balance::{Amount, ExternalBalance},
|
||||
use serai_client::{
|
||||
primitives::{ExternalCoin, Amount, ExternalBalance},
|
||||
networks::monero::Address,
|
||||
};
|
||||
use serai_client_monero::Address;
|
||||
|
||||
use primitives::{OutputType, ReceivedOutput};
|
||||
|
||||
use crate::{EXTERNAL_SUBADDRESS, BRANCH_SUBADDRESS, CHANGE_SUBADDRESS, FORWARDED_SUBADDRESS};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[rustfmt::skip]
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Default, Hash, Debug, Encode, Decode, BorshSerialize, BorshDeserialize,
|
||||
)]
|
||||
pub(crate) struct OutputId(pub(crate) [u8; 32]);
|
||||
impl AsRef<[u8]> for OutputId {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::future::Future;
|
||||
|
||||
use monero_simple_request_rpc::{prelude::*, SimpleRequestTransport};
|
||||
|
||||
use serai_primitives::{network_id::ExternalNetworkId, coin::ExternalCoin, balance::Amount};
|
||||
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
|
||||
|
||||
use scanner::ScannerFeed;
|
||||
use signers::TransactionPublisher;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user