mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Redo primitives, abi
Consolidates all primitives into a single crate. We didn't benefit from its fragmentation. I'm hesitant to say the new internal-organization is better (it may be just as clunky), but it's at least in a single crate (not spread out over micro-crates). The ABI is the most distinct. We now entirely own it. Block header hashes don't directly commit to any BABE data (avoiding potentially ~4 KB headers upon session changes), and are hashed as borsh (a more widely used codec than SCALE). There are still Substrate variants, using SCALE and with the BABE data, but they're prunable from a protocol design perspective. Defines a transaction as a Vec of Calls, allowing atomic operations.
This commit is contained in:
@@ -12,76 +12,38 @@ rust-version = "1.85"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = ["serde"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc", "serde"] }
|
||||
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "bit-vec"] }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive", "bit-vec"] }
|
||||
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"] }
|
||||
|
||||
bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
||||
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
||||
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||
serde = { version = "1", default-features = false, features = ["derive"], optional = true }
|
||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"], optional = true }
|
||||
scale-info = { version = "2", default-features = false, features = ["derive"], optional = true }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["serde"], optional = true }
|
||||
|
||||
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
||||
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1", default-features = false }
|
||||
serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1", default-features = false }
|
||||
serai-emissions-primitives = { path = "../emissions/primitives", version = "0.1", default-features = false }
|
||||
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1", default-features = false }
|
||||
serai-signals-primitives = { path = "../signals/primitives", version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
std = [
|
||||
"borsh/std",
|
||||
|
||||
"bitvec/std",
|
||||
|
||||
"scale/std",
|
||||
"scale-info/std",
|
||||
|
||||
"borsh?/std",
|
||||
"serde?/std",
|
||||
|
||||
"sp-core/std",
|
||||
"sp-runtime/std",
|
||||
|
||||
"sp-consensus-babe/std",
|
||||
"sp-consensus-grandpa/std",
|
||||
|
||||
"frame-support/std",
|
||||
"serde?/std",
|
||||
"scale?/std",
|
||||
"scale-info?/std",
|
||||
"sp-runtime?/std",
|
||||
|
||||
"serai-primitives/std",
|
||||
"serai-coins-primitives/std",
|
||||
"serai-validator-sets-primitives/std",
|
||||
"serai-genesis-liquidity-primitives/std",
|
||||
"serai-emissions-primitives/std",
|
||||
"serai-in-instructions-primitives/std",
|
||||
"serai-signals-primitives/std",
|
||||
]
|
||||
borsh = [
|
||||
"dep:borsh",
|
||||
"serai-primitives/borsh",
|
||||
"serai-coins-primitives/borsh",
|
||||
"serai-validator-sets-primitives/borsh",
|
||||
"serai-genesis-liquidity-primitives/borsh",
|
||||
"serai-in-instructions-primitives/borsh",
|
||||
"serai-signals-primitives/borsh",
|
||||
]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
"serai-primitives/serde",
|
||||
"serai-coins-primitives/serde",
|
||||
"serai-validator-sets-primitives/serde",
|
||||
"serai-genesis-liquidity-primitives/serde",
|
||||
"serai-in-instructions-primitives/serde",
|
||||
"serai-signals-primitives/serde",
|
||||
]
|
||||
substrate = ["serde", "scale", "scale-info", "sp-runtime"]
|
||||
default = ["std"]
|
||||
|
||||
4
substrate/abi/README.md
Normal file
4
substrate/abi/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# serai-abi
|
||||
|
||||
Serai's ABI, inclusive to the transaction, event, and block types. MIT-licensed to ensure usability
|
||||
in a variety of contexts.
|
||||
@@ -1,17 +0,0 @@
|
||||
use sp_consensus_babe::EquivocationProof;
|
||||
|
||||
use serai_primitives::{Header, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<Header>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
// We could define a Babe Config here and use the literal pallet_babe::Call
|
||||
// The disadvantage to this would be the complexity and presence of junk fields such as `__Ignore`
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum Call {
|
||||
report_equivocation(ReportEquivocation),
|
||||
report_equivocation_unsigned(ReportEquivocation),
|
||||
}
|
||||
250
substrate/abi/src/block.rs
Normal file
250
substrate/abi/src/block.rs
Normal file
@@ -0,0 +1,250 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use crate::{primitives::BlockHash, Transaction};
|
||||
|
||||
/// A V1 header for a block.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct HeaderV1 {
|
||||
/// The index of this block on the blockchain.
|
||||
///
|
||||
/// The genesis block has number 0.
|
||||
pub number: u64,
|
||||
/// The block this header builds upon.
|
||||
pub parent_hash: BlockHash,
|
||||
/// The root of a Merkle tree commiting to the transactions within this block.
|
||||
// TODO: Review the format of this defined by Substrate
|
||||
pub transactions_root: [u8; 32],
|
||||
/// A commitment to the consensus data used to justify adding this block to the blockchain.
|
||||
pub consensus_commitment: [u8; 32],
|
||||
}
|
||||
|
||||
/// A header for a block.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Header {
|
||||
/// A version 1 header.
|
||||
V1(HeaderV1),
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Get the hash of the header.
|
||||
pub fn number(&self) -> u64 {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { number, .. }) => *number,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn parent_hash(&self) -> BlockHash {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { parent_hash, .. }) => *parent_hash,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn transactions_root(&self) -> [u8; 32] {
|
||||
match self {
|
||||
Header::V1(HeaderV1 { transactions_root, .. }) => *transactions_root,
|
||||
}
|
||||
}
|
||||
/// Get the hash of the header.
|
||||
pub fn hash(&self) -> BlockHash {
|
||||
BlockHash(sp_core::blake2_256(&borsh::to_vec(self).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A block.
|
||||
///
|
||||
/// This does not guarantee consistency. The header's `transactions_root` may not match the
|
||||
/// contained transactions.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct Block {
|
||||
/// The block's header.
|
||||
pub header: Header,
|
||||
/// The block's transactions.
|
||||
pub transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "substrate")]
|
||||
mod substrate {
|
||||
use scale::{Encode, Decode};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
generic::Digest,
|
||||
traits::{Header as HeaderTrait, HeaderProvider, Block as BlockTrait},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// The consensus data for a V1 header.
|
||||
///
|
||||
/// This is not considered part of the protocol proper and may be pruned in the future. It's
|
||||
/// solely considered used for consensus now.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub struct ConsensusV1 {
|
||||
/// The state root.
|
||||
state_root: H256,
|
||||
/// The consensus digests.
|
||||
digest: Digest,
|
||||
}
|
||||
|
||||
/// A V1 header for a block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub struct SubstrateHeaderV1 {
|
||||
number: u64,
|
||||
parent_hash: H256,
|
||||
transactions_root: H256,
|
||||
consensus: ConsensusV1,
|
||||
}
|
||||
|
||||
/// A header for a block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, sp_runtime::Serialize)]
|
||||
pub enum SubstrateHeader {
|
||||
/// A version 1 header.
|
||||
V1(SubstrateHeaderV1),
|
||||
}
|
||||
|
||||
impl From<&SubstrateHeader> for Header {
|
||||
fn from(header: &SubstrateHeader) -> Header {
|
||||
match header {
|
||||
SubstrateHeader::V1(header) => Header::V1(HeaderV1 {
|
||||
number: header.number,
|
||||
parent_hash: BlockHash(header.parent_hash.0),
|
||||
transactions_root: header.transactions_root.0,
|
||||
consensus_commitment: sp_core::blake2_256(&header.consensus.encode()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A block, as needed by Substrate.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, sp_runtime::Serialize)]
|
||||
pub struct SubstrateBlock {
|
||||
header: SubstrateHeader,
|
||||
#[serde(skip)] // This makes this unsafe to deserialize, but we don't impl `Deserialize`
|
||||
transactions: Vec<Transaction>,
|
||||
}
|
||||
|
||||
impl HeaderTrait for SubstrateHeader {
|
||||
type Number = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
|
||||
fn new(
|
||||
number: Self::Number,
|
||||
extrinsics_root: Self::Hash,
|
||||
state_root: Self::Hash,
|
||||
parent_hash: Self::Hash,
|
||||
digest: Digest,
|
||||
) -> Self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 {
|
||||
number,
|
||||
parent_hash,
|
||||
transactions_root: extrinsics_root,
|
||||
consensus: ConsensusV1 { state_root, digest },
|
||||
})
|
||||
}
|
||||
|
||||
fn number(&self) -> &Self::Number {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { number, .. }) => number,
|
||||
}
|
||||
}
|
||||
fn set_number(&mut self, number: Self::Number) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { number: existing, .. }) => {
|
||||
*existing = number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extrinsics_root(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => transactions_root,
|
||||
}
|
||||
}
|
||||
fn set_extrinsics_root(&mut self, extrinsics_root: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { transactions_root, .. }) => {
|
||||
*transactions_root = extrinsics_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn state_root(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.state_root,
|
||||
}
|
||||
}
|
||||
fn set_state_root(&mut self, state_root: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => {
|
||||
consensus.state_root = state_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_hash(&self) -> &Self::Hash {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash, .. }) => parent_hash,
|
||||
}
|
||||
}
|
||||
fn set_parent_hash(&mut self, parent_hash: Self::Hash) {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { parent_hash: existing, .. }) => {
|
||||
*existing = parent_hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn digest(&self) -> &Digest {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &consensus.digest,
|
||||
}
|
||||
}
|
||||
fn digest_mut(&mut self) -> &mut Digest {
|
||||
match self {
|
||||
SubstrateHeader::V1(SubstrateHeaderV1 { consensus, .. }) => &mut consensus.digest,
|
||||
}
|
||||
}
|
||||
|
||||
fn hash(&self) -> H256 {
|
||||
H256::from(Header::from(self).hash().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl HeaderProvider for SubstrateBlock {
|
||||
type HeaderT = SubstrateHeader;
|
||||
}
|
||||
|
||||
impl BlockTrait for SubstrateBlock {
|
||||
type Extrinsic = Transaction;
|
||||
type Header = SubstrateHeader;
|
||||
type Hash = H256;
|
||||
fn header(&self) -> &Self::Header {
|
||||
&self.header
|
||||
}
|
||||
fn extrinsics(&self) -> &[Self::Extrinsic] {
|
||||
&self.transactions
|
||||
}
|
||||
fn deconstruct(self) -> (Self::Header, Vec<Self::Extrinsic>) {
|
||||
(self.header, self.transactions)
|
||||
}
|
||||
fn new(header: Self::Header, transactions: Vec<Self::Extrinsic>) -> Self {
|
||||
Self { header, transactions }
|
||||
}
|
||||
fn encode_from(header: &Self::Header, transactions: &[Self::Extrinsic]) -> Vec<u8> {
|
||||
let header = header.encode();
|
||||
let transactions = transactions.encode();
|
||||
let mut block = header;
|
||||
block.extend(transactions);
|
||||
block
|
||||
}
|
||||
fn hash(&self) -> Self::Hash {
|
||||
self.header.hash()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "substrate")]
|
||||
pub use substrate::*;
|
||||
@@ -1,25 +1,70 @@
|
||||
use serai_primitives::{Balance, SeraiAddress};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_coins_primitives as primitives;
|
||||
use primitives::OutInstructionWithBalance;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress, balance::Balance, instructions::OutInstructionWithBalance,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to coins.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
burn { balance: Balance },
|
||||
burn_with_instruction { instruction: OutInstructionWithBalance },
|
||||
/// Transfer these coins to the specified address.
|
||||
transfer {
|
||||
/// The address to transfer to.
|
||||
to: SeraiAddress,
|
||||
/// The coins to transfer.
|
||||
coins: Balance,
|
||||
},
|
||||
/// Burn these coins.
|
||||
burn {
|
||||
/// The coins to burn.
|
||||
coins: Balance,
|
||||
},
|
||||
/// Burn these coins with an `OutInstruction` specified.
|
||||
burn_with_instruction {
|
||||
/// The `OutInstruction`, with the coins to burn.
|
||||
instruction: OutInstructionWithBalance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Mint { to: SeraiAddress, balance: Balance },
|
||||
Burn { from: SeraiAddress, balance: Balance },
|
||||
BurnWithInstruction { from: SeraiAddress, instruction: OutInstructionWithBalance },
|
||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::transfer { .. } | Call::burn { .. } | Call::burn_with_instruction { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the system.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// The specified coins were minted.
|
||||
Mint {
|
||||
/// The address minted to.
|
||||
to: SeraiAddress,
|
||||
/// The coins minted.
|
||||
coins: Balance,
|
||||
},
|
||||
/// The specified coins were burnt.
|
||||
Burn {
|
||||
/// The address burnt from.
|
||||
from: SeraiAddress,
|
||||
/// The coins burnt.
|
||||
coins: Balance,
|
||||
},
|
||||
/// The specified coins were burnt with an `OutInstruction` specified.
|
||||
BurnWithInstruction {
|
||||
/// The address burnt from.
|
||||
from: SeraiAddress,
|
||||
/// The `OutInstruction` specified, and the coins burnt.
|
||||
instruction: OutInstructionWithBalance,
|
||||
},
|
||||
/// The specified coins were transferred.
|
||||
Transfer {
|
||||
/// The address transferred from.
|
||||
from: SeraiAddress,
|
||||
/// The address transferred to.
|
||||
to: SeraiAddress,
|
||||
/// The coins transferred.
|
||||
coins: Balance,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,75 +1,121 @@
|
||||
use sp_runtime::BoundedVec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use serai_primitives::*;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
type PoolId = ExternalCoin;
|
||||
type MaxSwapPathLength = sp_core::ConstU32<3>;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress,
|
||||
coin::ExternalCoin,
|
||||
balance::{Amount, ExternalBalance, Balance},
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to the DEX.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
/// Add liquidity.
|
||||
add_liquidity {
|
||||
/// The coin to add liquidity for.
|
||||
coin: ExternalCoin,
|
||||
coin_desired: SubstrateAmount,
|
||||
sri_desired: SubstrateAmount,
|
||||
coin_min: SubstrateAmount,
|
||||
sri_min: SubstrateAmount,
|
||||
mint_to: SeraiAddress,
|
||||
/// The intended amount of SRI to add as liquidity.
|
||||
sri_intended: Amount,
|
||||
/// The intended amount of the coin to add as liquidity.
|
||||
coin_intended: Amount,
|
||||
/// The minimum amount of SRI to add as liquidity.
|
||||
sri_minimum: Amount,
|
||||
/// The minimum amount of the coin to add as liquidity.
|
||||
coin_minimum: Amount,
|
||||
},
|
||||
/// Transfer these liquidity tokens to the specified address.
|
||||
transfer_liquidity {
|
||||
/// The address to transfer to.
|
||||
to: SeraiAddress,
|
||||
/// The liquidity tokens to transfer.
|
||||
liquidity_tokens: ExternalBalance,
|
||||
},
|
||||
/// Remove liquidity.
|
||||
remove_liquidity {
|
||||
coin: ExternalCoin,
|
||||
lp_token_burn: SubstrateAmount,
|
||||
coin_min_receive: SubstrateAmount,
|
||||
sri_min_receive: SubstrateAmount,
|
||||
withdraw_to: SeraiAddress,
|
||||
/// The liquidity tokens to burn, removing the underlying liquidity from the pool.
|
||||
///
|
||||
/// The `coin` within the balance is the coin to remove liquidity for.
|
||||
liquidity_tokens: ExternalBalance,
|
||||
/// The minimum amount of SRI to receive.
|
||||
sri_minimum: Amount,
|
||||
/// The minimum amount of the coin to receive.
|
||||
coin_minimum: Amount,
|
||||
},
|
||||
swap_exact_tokens_for_tokens {
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_in: SubstrateAmount,
|
||||
amount_out_min: SubstrateAmount,
|
||||
send_to: SeraiAddress,
|
||||
/// Swap an exact amount of coins.
|
||||
swap_exact {
|
||||
/// The coins to swap.
|
||||
coins_to_swap: Balance,
|
||||
/// The minimum balance to receive.
|
||||
minimum_to_receive: Balance,
|
||||
},
|
||||
swap_tokens_for_exact_tokens {
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_out: SubstrateAmount,
|
||||
amount_in_max: SubstrateAmount,
|
||||
send_to: SeraiAddress,
|
||||
/// Swap for an exact amount of coins.
|
||||
swap_for_exact {
|
||||
/// The coins to receive.
|
||||
coins_to_receive: Balance,
|
||||
/// The maximum amount to swap.
|
||||
maximum_to_swap: Balance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::add_liquidity { .. } |
|
||||
Call::transfer_liquidity { .. } |
|
||||
Call::remove_liquidity { .. } |
|
||||
Call::swap_exact { .. } |
|
||||
Call::swap_for_exact { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the DEX.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
PoolCreated {
|
||||
pool_id: PoolId,
|
||||
pool_account: SeraiAddress,
|
||||
},
|
||||
|
||||
/// Liquidity was added to a pool.
|
||||
LiquidityAdded {
|
||||
who: SeraiAddress,
|
||||
mint_to: SeraiAddress,
|
||||
pool_id: PoolId,
|
||||
coin_amount: SubstrateAmount,
|
||||
sri_amount: SubstrateAmount,
|
||||
lp_token_minted: SubstrateAmount,
|
||||
/// The account which added the liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The account which received the liquidity tokens.
|
||||
recipient: SeraiAddress,
|
||||
/// The pool liquidity was added to.
|
||||
pool: ExternalCoin,
|
||||
/// The amount of liquidity tokens which were minted.
|
||||
liquidity_tokens_minted: Amount,
|
||||
/// The amount of the coin which was added to the pool's liquidity.
|
||||
coin_amount: Amount,
|
||||
/// The amount of SRI which was added to the pool's liquidity.
|
||||
sri_amount: Amount,
|
||||
},
|
||||
|
||||
/// Liquidity was removed from a pool.
|
||||
LiquidityRemoved {
|
||||
who: SeraiAddress,
|
||||
withdraw_to: SeraiAddress,
|
||||
pool_id: PoolId,
|
||||
coin_amount: SubstrateAmount,
|
||||
sri_amount: SubstrateAmount,
|
||||
lp_token_burned: SubstrateAmount,
|
||||
/// The account which removed the liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The pool liquidity was removed from.
|
||||
pool: ExternalCoin,
|
||||
/// The mount of liquidity tokens which were burnt.
|
||||
liquidity_tokens_burnt: Amount,
|
||||
/// The amount of the coin which was removed from the pool's liquidity.
|
||||
coin_amount: Amount,
|
||||
/// The amount of SRI which was removed from the pool's liquidity.
|
||||
sri_amount: Amount,
|
||||
},
|
||||
|
||||
SwapExecuted {
|
||||
who: SeraiAddress,
|
||||
send_to: SeraiAddress,
|
||||
path: BoundedVec<Coin, MaxSwapPathLength>,
|
||||
amount_in: SubstrateAmount,
|
||||
amount_out: SubstrateAmount,
|
||||
/// A swap through the liquidity pools occurred.
|
||||
Swap {
|
||||
/// The account which made the swap.
|
||||
origin: SeraiAddress,
|
||||
/// The recipient for the output of the swap.
|
||||
recipient: SeraiAddress,
|
||||
/// The deltas incurred by the pools.
|
||||
///
|
||||
/// For a swap of sriABC to sriDEF, this would be
|
||||
/// `[Balance { sriABC, 1 }, Balance { SRI, 2 }, Balance { sriDEF, 3 }]`, where
|
||||
/// `Balance { sriABC, 1 }` was added to the `sriABC-SRI` pool, `Balance { SRI, 2 }` was
|
||||
/// removed from the `sriABC-SRI` pool and added to the `sriDEF-SRI` pool, and
|
||||
/// `Balance { sriDEF, 3 }` was removed from the `sriDEF-SRI` pool.
|
||||
deltas: Vec<Balance>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
use serai_primitives::network_id::ExternalNetworkId;
|
||||
|
||||
/// An event from economic security.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
EconomicSecurityReached { network: ExternalNetworkId },
|
||||
/// Economic security was achieved for a network's validator set.
|
||||
EconomicSecurityAchieved {
|
||||
/// The network whose validator set achieved economic security.
|
||||
network: ExternalNetworkId,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub use serai_emissions_primitives as primitives;
|
||||
@@ -1,20 +1,50 @@
|
||||
pub use serai_genesis_liquidity_primitives as primitives;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::*;
|
||||
use primitives::*;
|
||||
use serai_primitives::{
|
||||
crypto::Signature, address::SeraiAddress, balance::ExternalBalance, genesis::GenesisValues,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
/// A call to the genesis liquidity.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
remove_coin_liquidity { balance: ExternalBalance },
|
||||
oraclize_values { values: Values, signature: Signature },
|
||||
/// Oraclize the value of non-Bitcoin external coins relative to Bitcoin.
|
||||
oraclize_values {
|
||||
/// The values of the non-Bitcoin external coins.
|
||||
values: GenesisValues,
|
||||
/// The signature by the genesis validators for these values.
|
||||
signature: Signature,
|
||||
},
|
||||
/// Remove liquidity.
|
||||
remove_liquidity {
|
||||
/// The genesis liquidity to remove.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Event {
|
||||
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::oraclize_values { .. } => false,
|
||||
Call::remove_liquidity { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the genesis liquidity.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// Genesis liquidity added.
|
||||
GenesisLiquidityAdded {
|
||||
/// The recipient of the genesis liquidity.
|
||||
recipient: SeraiAddress,
|
||||
/// The coins added as genesis liquidity.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
/// Genesis liquidity removed.
|
||||
GenesisLiquidityRemoved {
|
||||
/// The account which removed the genesis liquidity.
|
||||
origin: SeraiAddress,
|
||||
/// The amount of genesis liquidity removed.
|
||||
balance: ExternalBalance,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
use sp_consensus_grandpa::EquivocationProof;
|
||||
|
||||
use serai_primitives::{BlockNumber, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub struct ReportEquivocation {
|
||||
pub equivocation_proof: alloc::boxed::Box<EquivocationProof<[u8; 32], BlockNumber>>,
|
||||
pub key_owner_proof: SeraiAddress,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum Call {
|
||||
report_equivocation(ReportEquivocation),
|
||||
report_equivocation_unsigned(ReportEquivocation),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
NewAuthorities { authority_set: alloc::vec::Vec<(SeraiAddress, u64)> },
|
||||
// TODO: Remove these
|
||||
Paused,
|
||||
Resumed,
|
||||
}
|
||||
@@ -1,30 +1,47 @@
|
||||
use serai_primitives::*;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_in_instructions_primitives as primitives;
|
||||
use primitives::SignedBatch;
|
||||
use serai_validator_sets_primitives::Session;
|
||||
use serai_primitives::{
|
||||
BlockHash, network_id::ExternalNetworkId, validator_sets::Session, instructions::SignedBatch,
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to `InInstruction`s.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
execute_batch { batch: SignedBatch },
|
||||
/// Execute a batch of `InInstruction`s.
|
||||
execute_batch {
|
||||
/// The batch to execute.
|
||||
batch: SignedBatch,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::execute_batch { .. } => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from `InInstruction`s.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A batch of `InInstruction`s was executed.
|
||||
Batch {
|
||||
/// The network for which a batch was executed.
|
||||
network: ExternalNetworkId,
|
||||
/// The session which published the batch.
|
||||
publishing_session: Session,
|
||||
/// The ID of the batch.
|
||||
id: u32,
|
||||
/// The hash of the block on the external network which caused this batch's creation.
|
||||
external_network_block_hash: BlockHash,
|
||||
/// The hash of the `InInstruction`s within this batch.
|
||||
in_instructions_hash: [u8; 32],
|
||||
/// The results of each `InInstruction` within the batch.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||
)]
|
||||
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
},
|
||||
Halt {
|
||||
network: ExternalNetworkId,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,94 +1,98 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
pub use serai_primitives as primitives;
|
||||
|
||||
/// Call/Event for the system.
|
||||
pub mod system;
|
||||
|
||||
pub mod timestamp;
|
||||
|
||||
/// Call/Event for coins.
|
||||
pub mod coins;
|
||||
pub mod liquidity_tokens;
|
||||
pub mod dex;
|
||||
|
||||
/// Call/Event for validator sets.
|
||||
pub mod validator_sets;
|
||||
|
||||
pub mod genesis_liquidity;
|
||||
pub mod emissions;
|
||||
|
||||
pub mod economic_security;
|
||||
|
||||
pub mod in_instructions;
|
||||
|
||||
/// Call/Event for signals.
|
||||
pub mod signals;
|
||||
|
||||
pub mod babe;
|
||||
pub mod grandpa;
|
||||
/// Call/Event for the DEX.
|
||||
pub mod dex;
|
||||
|
||||
pub mod tx;
|
||||
/// Call/Event for genesis liquidity.
|
||||
pub mod genesis_liquidity;
|
||||
/// Event for economic security.
|
||||
pub mod economic_security;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// Call/Event for `InInstruction`s.
|
||||
pub mod in_instructions;
|
||||
|
||||
mod transaction;
|
||||
pub use transaction::*;
|
||||
|
||||
mod block;
|
||||
pub use block::*;
|
||||
|
||||
/// All calls.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[repr(u8)]
|
||||
pub enum Call {
|
||||
Timestamp(timestamp::Call),
|
||||
Coins(coins::Call),
|
||||
LiquidityTokens(liquidity_tokens::Call),
|
||||
Dex(dex::Call),
|
||||
ValidatorSets(validator_sets::Call),
|
||||
GenesisLiquidity(genesis_liquidity::Call),
|
||||
InInstructions(in_instructions::Call),
|
||||
Signals(signals::Call),
|
||||
Babe(babe::Call),
|
||||
Grandpa(grandpa::Call),
|
||||
// The call for the system.
|
||||
// System(system::Call) = 0,
|
||||
/// The call for coins.
|
||||
Coins(coins::Call) = 1,
|
||||
/// The call for validator sets.
|
||||
ValidatorSets(validator_sets::Call) = 2,
|
||||
/// The call for signals.
|
||||
Signals(signals::Call) = 3,
|
||||
/// The call for the DEX.
|
||||
Dex(dex::Call) = 4,
|
||||
/// The call for genesis liquidity.
|
||||
GenesisLiquidity(genesis_liquidity::Call) = 5,
|
||||
// The call for economic security.
|
||||
// EconomicSecurity = 6,
|
||||
/// The call for `InInstruction`s.
|
||||
InInstructions(in_instructions::Call) = 7,
|
||||
}
|
||||
|
||||
// TODO: Remove this
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
pub enum TransactionPaymentEvent {
|
||||
TransactionFeePaid { who: serai_primitives::SeraiAddress, actual_fee: u64, tip: u64 },
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::Coins(call) => call.is_signed(),
|
||||
Call::ValidatorSets(call) => call.is_signed(),
|
||||
Call::Signals(call) => call.is_signed(),
|
||||
Call::Dex(call) => call.is_signed(),
|
||||
Call::GenesisLiquidity(call) => call.is_signed(),
|
||||
Call::InInstructions(call) => call.is_signed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// All events.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
#[borsh(use_discriminant = true)]
|
||||
#[repr(u8)]
|
||||
pub enum Event {
|
||||
System(system::Event),
|
||||
Timestamp,
|
||||
TransactionPayment(TransactionPaymentEvent),
|
||||
Coins(coins::Event),
|
||||
LiquidityTokens(liquidity_tokens::Event),
|
||||
Dex(dex::Event),
|
||||
ValidatorSets(validator_sets::Event),
|
||||
GenesisLiquidity(genesis_liquidity::Event),
|
||||
Emissions,
|
||||
EconomicSecurity(economic_security::Event),
|
||||
InInstructions(in_instructions::Event),
|
||||
Signals(signals::Event),
|
||||
Babe,
|
||||
Grandpa(grandpa::Event),
|
||||
/// The event for the system.
|
||||
System(system::Event) = 0,
|
||||
/// The event for coins.
|
||||
Coins(coins::Event) = 1,
|
||||
/// The event for validator sets.
|
||||
ValidatorSets(validator_sets::Event) = 2,
|
||||
/// The event for signals.
|
||||
Signals(signals::Event) = 3,
|
||||
/// The event for the DEX.
|
||||
Dex(dex::Event) = 4,
|
||||
/// The event for genesis liquidity.
|
||||
GenesisLiquidity(genesis_liquidity::Event) = 5,
|
||||
/// The event for economic security.
|
||||
EconomicSecurity(economic_security::Event) = 6,
|
||||
/// The event for `InInstruction`s.
|
||||
InInstructions(in_instructions::Event) = 7,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct Extra {
|
||||
pub era: sp_runtime::generic::Era,
|
||||
#[codec(compact)]
|
||||
pub nonce: u32,
|
||||
#[codec(compact)]
|
||||
pub tip: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub struct SignedPayloadExtra {
|
||||
pub spec_version: u32,
|
||||
pub tx_version: u32,
|
||||
pub genesis: [u8; 32],
|
||||
pub mortality_checkpoint: [u8; 32],
|
||||
}
|
||||
|
||||
pub type Transaction = tx::Transaction<Call, Extra>;
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
use serai_primitives::{Balance, SeraiAddress};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Call {
|
||||
burn { balance: Balance },
|
||||
transfer { to: SeraiAddress, balance: Balance },
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Mint { to: SeraiAddress, balance: Balance },
|
||||
Burn { from: SeraiAddress, balance: Balance },
|
||||
Transfer { from: SeraiAddress, to: SeraiAddress, balance: Balance },
|
||||
}
|
||||
@@ -1,59 +1,132 @@
|
||||
use serai_primitives::{NetworkId, SeraiAddress};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_validator_sets_primitives::ValidatorSet;
|
||||
use serai_primitives::{
|
||||
address::SeraiAddress, network_id::NetworkId, validator_sets::ValidatorSet, signals::Signal,
|
||||
};
|
||||
|
||||
pub use serai_signals_primitives as primitives;
|
||||
use primitives::SignalId;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to signals.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
register_retirement_signal { in_favor_of: [u8; 32] },
|
||||
revoke_retirement_signal { retirement_signal_id: [u8; 32] },
|
||||
favor { signal_id: SignalId, for_network: NetworkId },
|
||||
revoke_favor { signal_id: SignalId, for_network: NetworkId },
|
||||
stand_against { signal_id: SignalId, for_network: NetworkId },
|
||||
/// Register a retirement signal.
|
||||
register_retirement_signal {
|
||||
/// The protocol favored over the current protocol.
|
||||
in_favor_of: [u8; 32],
|
||||
},
|
||||
/// Revoke a retirement signal.
|
||||
revoke_retirement_signal {
|
||||
/// The protocol which was favored over the current protocol
|
||||
was_in_favor_of: [u8; 32],
|
||||
},
|
||||
/// Favor a signal.
|
||||
favor {
|
||||
/// The signal to favor.
|
||||
signal: Signal,
|
||||
/// The network this validator is expressing favor with.
|
||||
///
|
||||
/// A validator may be an active validator for multiple networks. The validator must specify
|
||||
/// which network they're expressing favor with in this call.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Revoke favor for a signal.
|
||||
revoke_favor {
|
||||
/// The signal to revoke favor for.
|
||||
signal: Signal,
|
||||
/// The network this validator is revoking favor with.
|
||||
///
|
||||
/// A validator may have expressed favor with multiple networks. The validator must specify
|
||||
/// which network they're revoking favor with in this call.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Stand against a signal.
|
||||
///
|
||||
/// This has no effects other than emitting an event that this signal is stood against. If the
|
||||
/// origin has prior expressed favor, they must still call `revoke_favor` for each network they
|
||||
/// expressed favor with.
|
||||
stand_against {
|
||||
/// The signal to stand against.
|
||||
signal: Signal,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::register_retirement_signal { .. } |
|
||||
Call::revoke_retirement_signal { .. } |
|
||||
Call::favor { .. } |
|
||||
Call::revoke_favor { .. } |
|
||||
Call::stand_against { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from signals.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A retirement signal has been registered.
|
||||
RetirementSignalRegistered {
|
||||
signal_id: [u8; 32],
|
||||
/// The retirement signal's ID.
|
||||
signal: [u8; 32],
|
||||
/// The protocol retirement is proposed in favor of.
|
||||
in_favor_of: [u8; 32],
|
||||
/// The address which registered this signal.
|
||||
registrant: SeraiAddress,
|
||||
},
|
||||
/// A retirement signal was revoked.
|
||||
RetirementSignalRevoked {
|
||||
signal_id: [u8; 32],
|
||||
/// The retirement signal's ID.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// A signal was favored.
|
||||
SignalFavored {
|
||||
signal_id: SignalId,
|
||||
/// The signal favored.
|
||||
signal: Signal,
|
||||
/// The validator the signal was favored by.
|
||||
by: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
},
|
||||
SetInFavor {
|
||||
signal_id: SignalId,
|
||||
set: ValidatorSet,
|
||||
},
|
||||
RetirementSignalLockedIn {
|
||||
signal_id: [u8; 32],
|
||||
},
|
||||
SetNoLongerInFavor {
|
||||
signal_id: SignalId,
|
||||
set: ValidatorSet,
|
||||
/// The network with which the signal was favored.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// Favor for a signal was revoked.
|
||||
FavorRevoked {
|
||||
signal_id: SignalId,
|
||||
/// The signal whose favor was revoked.
|
||||
signal: Signal,
|
||||
/// The validator who revoked their favor for the signal.
|
||||
by: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
/// The network with which favor for the signal was revoked.
|
||||
with_network: NetworkId,
|
||||
},
|
||||
/// A supermajority of a validator set now favor a signal.
|
||||
SetInFavor {
|
||||
/// The signal which now has a supermajority of a validator set favoring it.
|
||||
signal: Signal,
|
||||
/// The validator set which is now considered to favor the signal.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A validator set is no longer considered to favor a signal.
|
||||
SetNoLongerInFavor {
|
||||
/// The signal which no longer has the validator set considered in favor of it.
|
||||
signal: Signal,
|
||||
/// The validator set which is no longer considered to be in favor of the signal.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A retirement signal has been locked in.
|
||||
RetirementSignalLockedIn {
|
||||
/// The signal which has been locked in.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// A validator set's ability to publish batches was halted.
|
||||
///
|
||||
/// This also halts set rotation in effect, as handovers are via new sets starting to publish
|
||||
/// batches.
|
||||
SetHalted {
|
||||
/// The signal which has been locked in.
|
||||
signal: [u8; 32],
|
||||
},
|
||||
/// An account has stood against a signal.
|
||||
AgainstSignal {
|
||||
signal_id: SignalId,
|
||||
/// The signal stood against.
|
||||
signal: Signal,
|
||||
/// The account which stood against the signal.
|
||||
who: SeraiAddress,
|
||||
for_network: NetworkId,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use frame_support::dispatch::{DispatchInfo, DispatchError};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
/// An event from the system.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
ExtrinsicSuccess { dispatch_info: DispatchInfo },
|
||||
ExtrinsicFailed { dispatch_error: DispatchError, dispatch_info: DispatchInfo },
|
||||
CodeUpdated,
|
||||
NewAccount { account: SeraiAddress },
|
||||
KilledAccount { account: SeraiAddress },
|
||||
Remarked { sender: SeraiAddress, hash: [u8; 32] },
|
||||
/// The transaction successfully executed.
|
||||
TransactionSuccess,
|
||||
/// The transaction failed to execute.
|
||||
// TODO: Add an error to this
|
||||
TransactionFailed,
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
set {
|
||||
#[codec(compact)]
|
||||
now: u64,
|
||||
},
|
||||
}
|
||||
319
substrate/abi/src/transaction.rs
Normal file
319
substrate/abi/src/transaction.rs
Normal file
@@ -0,0 +1,319 @@
|
||||
use core::num::NonZero;
|
||||
use alloc::{vec, vec::Vec};
|
||||
|
||||
use borsh::{io, BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::{address::SeraiAddress, crypto::Signature};
|
||||
use crate::Call;
|
||||
|
||||
// use frame_support::dispatch::GetDispatchInfo;
|
||||
|
||||
/// An error regarding `SignedCalls`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum SignedCallsError {
|
||||
/// No calls were included.
|
||||
NoCalls,
|
||||
/// An unsigned call was included.
|
||||
IncludedUnsignedCall,
|
||||
}
|
||||
|
||||
/// A `Vec` of signed calls.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct SignedCalls(Vec<Call>);
|
||||
impl TryFrom<Vec<Call>> for SignedCalls {
|
||||
type Error = SignedCallsError;
|
||||
fn try_from(calls: Vec<Call>) -> Result<Self, Self::Error> {
|
||||
if calls.is_empty() {
|
||||
Err(SignedCallsError::NoCalls)?;
|
||||
}
|
||||
for call in &calls {
|
||||
if !call.is_signed() {
|
||||
Err(SignedCallsError::IncludedUnsignedCall)?;
|
||||
}
|
||||
}
|
||||
Ok(SignedCalls(calls))
|
||||
}
|
||||
}
|
||||
|
||||
/// An error regarding `UnsignedCall`.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum UnsignedCallError {
|
||||
/// A signed call was specified.
|
||||
SignedCall,
|
||||
}
|
||||
|
||||
/// An unsigned call.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct UnsignedCall(Call);
|
||||
impl TryFrom<Call> for UnsignedCall {
|
||||
type Error = UnsignedCallError;
|
||||
fn try_from(call: Call) -> Result<Self, Self::Error> {
|
||||
if call.is_signed() {
|
||||
Err(UnsignedCallError::SignedCall)?;
|
||||
}
|
||||
Ok(UnsignedCall(call))
|
||||
}
|
||||
}
|
||||
|
||||
/// Part of the context used to sign with, from the protocol.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ImplicitContext {
|
||||
/// The ID of the the protocol.
|
||||
pub protocol_id: [u8; 32],
|
||||
/// The genesis hash of the blockchain.
|
||||
pub genesis: [u8; 32],
|
||||
}
|
||||
|
||||
/// Part of the context used to sign with, specified within the transaction itself.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ExplicitContext {
|
||||
/// The historic block this transaction builds upon.
|
||||
///
|
||||
/// This transaction can not be included in a blockchain which does not include this block.
|
||||
pub historic_block: [u8; 32],
|
||||
|
||||
/// The block this transaction expires at.
|
||||
///
|
||||
/// This transaction can not be included in a block whose number is equal or greater to this
|
||||
/// value.
|
||||
pub expires_at: Option<NonZero<u64>>,
|
||||
|
||||
/// The signer.
|
||||
pub signer: SeraiAddress,
|
||||
|
||||
/// The signer's nonce.
|
||||
pub nonce: u32,
|
||||
|
||||
/// The fee paid to the network for inclusion.
|
||||
///
|
||||
/// This fee is paid regardless of the success of any of the calls.
|
||||
pub fee: u64,
|
||||
}
|
||||
|
||||
/// A signature, with context.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct ContextualizedSignature {
|
||||
/// The explicit context.
|
||||
explicit_context: ExplicitContext,
|
||||
/// The signature.
|
||||
signature: Signature,
|
||||
}
|
||||
|
||||
/// The Serai transaction type.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction<RuntimeCall: 'static + From<Call> = Call> {
|
||||
/// The calls, as defined in Serai's ABI.
|
||||
///
|
||||
/// These calls are executed atomically. Either all successfully execute or none do. The
|
||||
/// transaction's fee is paid regardless.
|
||||
// TODO: Bound
|
||||
calls: Vec<Call>,
|
||||
/// The calls, as defined by Substrate.
|
||||
runtime_calls: Vec<RuntimeCall>,
|
||||
/// The signature, if present.
|
||||
contextualized_signature: Option<ContextualizedSignature>,
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> BorshSerialize for Transaction<RuntimeCall> {
|
||||
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||
// Write the calls
|
||||
self.calls.serialize(writer)?;
|
||||
// Write the signature, if present. Presence is deterministic to the calls
|
||||
if let Some(contextualized_signature) = self.contextualized_signature.as_ref() {
|
||||
contextualized_signature.serialize(writer)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> BorshDeserialize for Transaction<RuntimeCall> {
|
||||
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||
// Read the calls
|
||||
let calls = Vec::<Call>::deserialize_reader(reader)?;
|
||||
// Populate the runtime calls
|
||||
let mut runtime_calls = Vec::with_capacity(calls.len());
|
||||
for call in calls.iter().cloned() {
|
||||
runtime_calls.push(RuntimeCall::from(call));
|
||||
}
|
||||
|
||||
// Determine if this is signed or unsigned
|
||||
let mut signed = None;
|
||||
for call in &calls {
|
||||
let call_is_signed = call.is_signed();
|
||||
if signed.is_none() {
|
||||
signed = Some(call_is_signed)
|
||||
};
|
||||
if signed != Some(call_is_signed) {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "calls were a mixture of signed and unsigned"))?;
|
||||
}
|
||||
}
|
||||
let Some(signed) = signed else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "transaction had no calls"))?
|
||||
};
|
||||
|
||||
// Read the signature, if these calls are signed
|
||||
let contextualized_signature =
|
||||
if signed { Some(<ContextualizedSignature>::deserialize_reader(reader)?) } else { None };
|
||||
|
||||
Ok(Transaction { calls, runtime_calls, contextualized_signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<RuntimeCall: 'static + From<Call>> Transaction<RuntimeCall> {
|
||||
/// The message to sign to produce a signature.
|
||||
pub fn signature_message(
|
||||
calls: &SignedCalls,
|
||||
implicit_context: &ImplicitContext,
|
||||
explicit_context: &ExplicitContext,
|
||||
) -> Vec<u8> {
|
||||
let mut message = Vec::with_capacity(
|
||||
(calls.0.len() * 64) +
|
||||
core::mem::size_of::<ImplicitContext>() +
|
||||
core::mem::size_of::<ExplicitContext>(),
|
||||
);
|
||||
calls.0.serialize(&mut message).unwrap();
|
||||
implicit_context.serialize(&mut message).unwrap();
|
||||
explicit_context.serialize(&mut message).unwrap();
|
||||
message
|
||||
}
|
||||
|
||||
/// A transaction with signed calls.
|
||||
pub fn is_signed(
|
||||
calls: SignedCalls,
|
||||
explicit_context: ExplicitContext,
|
||||
signature: Signature,
|
||||
) -> Self {
|
||||
let calls = calls.0;
|
||||
let mut runtime_calls = Vec::with_capacity(calls.len());
|
||||
for call in calls.iter().cloned() {
|
||||
runtime_calls.push(call.into());
|
||||
}
|
||||
Self {
|
||||
calls,
|
||||
runtime_calls,
|
||||
contextualized_signature: Some(ContextualizedSignature { explicit_context, signature }),
|
||||
}
|
||||
}
|
||||
|
||||
/// A transaction with an unsigned call.
|
||||
pub fn unsigned(call: UnsignedCall) -> Self {
|
||||
let call = call.0;
|
||||
Self {
|
||||
calls: vec![call.clone()],
|
||||
runtime_calls: vec![call.into()],
|
||||
contextualized_signature: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "substrate")]
|
||||
mod substrate {
|
||||
use super::*;
|
||||
|
||||
impl scale::Encode for Transaction {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
borsh::to_vec(self).unwrap()
|
||||
}
|
||||
}
|
||||
impl scale::Decode for Transaction {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
struct ScaleRead<'a, I: scale::Input>(&'a mut I);
|
||||
impl<I: scale::Input> borsh::io::Read for ScaleRead<'_, I> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> borsh::io::Result<usize> {
|
||||
let remaining_len = self
|
||||
.0
|
||||
.remaining_len()
|
||||
.map_err(|err| borsh::io::Error::new(borsh::io::ErrorKind::Other, err))?;
|
||||
// If we're still calling `read`, we try to read at least one more byte
|
||||
let to_read = buf.len().min(remaining_len.unwrap_or(1));
|
||||
self
|
||||
.0
|
||||
.read(&mut buf[.. to_read])
|
||||
.map_err(|err| borsh::io::Error::new(borsh::io::ErrorKind::Other, err))?;
|
||||
Ok(to_read)
|
||||
}
|
||||
}
|
||||
Self::deserialize_reader(&mut ScaleRead(input)).map_err(|err| err.downcast().unwrap())
|
||||
}
|
||||
}
|
||||
impl<RuntimeCall: 'static + From<Call>> sp_runtime::traits::ExtrinsicLike
|
||||
for Transaction<RuntimeCall>
|
||||
{
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.calls[0].is_signed())
|
||||
}
|
||||
fn is_bare(&self) -> bool {
|
||||
!self.calls[0].is_signed()
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<Call> + TryInto<Call>,
|
||||
> sp_runtime::traits::Extrinsic for Transaction<Call>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
||||
{
|
||||
fn call(&self) -> &Call {
|
||||
&self.mapped_call
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
|
||||
const VERSION: u8 = 0;
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
||||
> GetDispatchInfo for Transaction<Call, Extra>
|
||||
{
|
||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
||||
self.mapped_call.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
||||
{
|
||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
||||
|
||||
fn check(
|
||||
self,
|
||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signer, signature, extra)) => {
|
||||
if !signature.verify(
|
||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
||||
&signer.into(),
|
||||
) {
|
||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
||||
}
|
||||
|
||||
sp_runtime::generic::CheckedExtrinsic {
|
||||
signed: Some((signer.into(), extra)),
|
||||
function: self.mapped_call,
|
||||
}
|
||||
}
|
||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
use scale::Encode;
|
||||
|
||||
use sp_core::sr25519::{Public, Signature};
|
||||
use sp_runtime::traits::Verify;
|
||||
|
||||
use serai_primitives::SeraiAddress;
|
||||
|
||||
use frame_support::dispatch::GetDispatchInfo;
|
||||
|
||||
pub trait TransactionMember:
|
||||
Clone + PartialEq + Eq + core::fmt::Debug + scale::Encode + scale::Decode + scale_info::TypeInfo
|
||||
{
|
||||
}
|
||||
impl<
|
||||
T: Clone
|
||||
+ PartialEq
|
||||
+ Eq
|
||||
+ core::fmt::Debug
|
||||
+ scale::Encode
|
||||
+ scale::Decode
|
||||
+ scale_info::TypeInfo,
|
||||
> TransactionMember for T
|
||||
{
|
||||
}
|
||||
|
||||
type TransactionEncodeAs<'a, Extra> =
|
||||
(&'a crate::Call, &'a Option<(SeraiAddress, Signature, Extra)>);
|
||||
type TransactionDecodeAs<Extra> = (crate::Call, Option<(SeraiAddress, Signature, Extra)>);
|
||||
|
||||
// We use our own Transaction struct, over UncheckedExtrinsic, for more control, a bit more
|
||||
// simplicity, and in order to be immune to https://github.com/paritytech/polkadot-sdk/issues/2947
|
||||
#[allow(private_bounds)]
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Transaction<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> {
|
||||
call: crate::Call,
|
||||
mapped_call: Call,
|
||||
signature: Option<(SeraiAddress, Signature, Extra)>,
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Transaction<Call, Extra>
|
||||
{
|
||||
pub fn new(call: crate::Call, signature: Option<(SeraiAddress, Signature, Extra)>) -> Self {
|
||||
Self { call: call.clone(), mapped_call: call.into(), signature }
|
||||
}
|
||||
|
||||
pub fn call(&self) -> &crate::Call {
|
||||
&self.call
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Encode for Transaction<Call, Extra>
|
||||
{
|
||||
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
|
||||
let tx: TransactionEncodeAs<Extra> = (&self.call, &self.signature);
|
||||
tx.using_encoded(f)
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale::Decode for Transaction<Call, Extra>
|
||||
{
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let (call, signature) = TransactionDecodeAs::decode(input)?;
|
||||
let mapped_call = Call::from(call.clone());
|
||||
Ok(Self { call, mapped_call, signature })
|
||||
}
|
||||
}
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
scale_info::TypeInfo for Transaction<Call, Extra>
|
||||
{
|
||||
type Identity = TransactionDecodeAs<Extra>;
|
||||
|
||||
// Define the type info as the info of the type equivalent to what we encode as
|
||||
fn type_info() -> scale_info::Type {
|
||||
TransactionDecodeAs::<Extra>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod _serde {
|
||||
use scale::Encode;
|
||||
use serde::ser::*;
|
||||
use super::*;
|
||||
impl<Call: 'static + TransactionMember + From<crate::Call>, Extra: 'static + TransactionMember>
|
||||
Serialize for Transaction<Call, Extra>
|
||||
{
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let encoded = self.encode();
|
||||
serializer.serialize_bytes(&encoded)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::de::*;
|
||||
#[cfg(feature = "std")]
|
||||
impl<
|
||||
'a,
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> Deserialize<'a> for Transaction<Call, Extra>
|
||||
{
|
||||
fn deserialize<D: Deserializer<'a>>(de: D) -> Result<Self, D::Error> {
|
||||
let bytes = sp_core::bytes::deserialize(de)?;
|
||||
<Self as scale::Decode>::decode(&mut &bytes[..])
|
||||
.map_err(|e| serde::de::Error::custom(format!("invalid transaction: {e}")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> sp_runtime::traits::Extrinsic for Transaction<Call, Extra>
|
||||
{
|
||||
type Call = Call;
|
||||
type SignaturePayload = (SeraiAddress, Signature, Extra);
|
||||
fn is_signed(&self) -> Option<bool> {
|
||||
Some(self.signature.is_some())
|
||||
}
|
||||
fn new(call: Call, signature: Option<Self::SignaturePayload>) -> Option<Self> {
|
||||
Some(Self { call: call.clone().try_into().ok()?, mapped_call: call, signature })
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + TryInto<crate::Call>,
|
||||
Extra: 'static + TransactionMember,
|
||||
> frame_support::traits::ExtrinsicCall for Transaction<Call, Extra>
|
||||
{
|
||||
fn call(&self) -> &Call {
|
||||
&self.mapped_call
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::ExtrinsicMetadata for Transaction<Call, Extra>
|
||||
{
|
||||
type SignedExtensions = Extra;
|
||||
|
||||
const VERSION: u8 = 0;
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call> + GetDispatchInfo,
|
||||
Extra: 'static + TransactionMember,
|
||||
> GetDispatchInfo for Transaction<Call, Extra>
|
||||
{
|
||||
fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
|
||||
self.mapped_call.get_dispatch_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
Call: 'static + TransactionMember + From<crate::Call>,
|
||||
Extra: 'static + TransactionMember + sp_runtime::traits::SignedExtension,
|
||||
> sp_runtime::traits::BlindCheckable for Transaction<Call, Extra>
|
||||
{
|
||||
type Checked = sp_runtime::generic::CheckedExtrinsic<Public, Call, Extra>;
|
||||
|
||||
fn check(
|
||||
self,
|
||||
) -> Result<Self::Checked, sp_runtime::transaction_validity::TransactionValidityError> {
|
||||
Ok(match self.signature {
|
||||
Some((signer, signature, extra)) => {
|
||||
if !signature.verify(
|
||||
(&self.call, &extra, extra.additional_signed()?).encode().as_slice(),
|
||||
&signer.into(),
|
||||
) {
|
||||
Err(sp_runtime::transaction_validity::InvalidTransaction::BadProof)?
|
||||
}
|
||||
|
||||
sp_runtime::generic::CheckedExtrinsic {
|
||||
signed: Some((signer.into(), extra)),
|
||||
function: self.mapped_call,
|
||||
}
|
||||
}
|
||||
None => sp_runtime::generic::CheckedExtrinsic { signed: None, function: self.mapped_call },
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,144 @@
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
|
||||
pub use serai_validator_sets_primitives as primitives;
|
||||
use serai_primitives::{
|
||||
crypto::{ExternalKey, KeyPair, Signature},
|
||||
address::SeraiAddress,
|
||||
balance::Amount,
|
||||
network_id::*,
|
||||
validator_sets::*,
|
||||
};
|
||||
|
||||
use serai_primitives::*;
|
||||
use serai_validator_sets_primitives::*;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
/// A call to the validator sets.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Call {
|
||||
/// Set the keys for a validator set.
|
||||
set_keys {
|
||||
/// The network whose latest validator set is setting their keys.
|
||||
network: ExternalNetworkId,
|
||||
/// The keys being set.
|
||||
key_pair: KeyPair,
|
||||
/// The participants in the validator set who signed off on these keys.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bitvec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bitvec"
|
||||
)]
|
||||
signature_participants: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||
/// The signature confirming these keys are valid.
|
||||
signature: Signature,
|
||||
},
|
||||
set_embedded_elliptic_curve_key {
|
||||
embedded_elliptic_curve: EmbeddedEllipticCurve,
|
||||
key: BoundedVec<u8, ConstU32<{ MAX_KEY_LEN }>>,
|
||||
},
|
||||
/// Report a validator set's slashes onto Serai.
|
||||
report_slashes {
|
||||
/// The network whose retiring validator set is setting their keys.
|
||||
network: ExternalNetworkId,
|
||||
/// The slashes they're reporting.
|
||||
slashes: SlashReport,
|
||||
/// The signature confirming the validity of this slash report.
|
||||
signature: Signature,
|
||||
},
|
||||
/// Set a validator's keys on embedded elliptic curves for a specific network.
|
||||
set_embedded_elliptic_curve_keys {
|
||||
/// The network the origin is setting their embedded elliptic curve keys for.
|
||||
network: ExternalNetworkId,
|
||||
/// The keys on the embedded elliptic curves.
|
||||
///
|
||||
/// This may be a single key if the external network uses the same embedded elliptic curve as
|
||||
/// used for the key to oraclize onto Serai.
|
||||
#[borsh(
|
||||
serialize_with = "serai_primitives::sp_borsh::borsh_serialize_bounded_vec",
|
||||
deserialize_with = "serai_primitives::sp_borsh::borsh_deserialize_bounded_vec"
|
||||
)]
|
||||
keys: BoundedVec<u8, ConstU32<{ 2 * ExternalKey::MAX_LEN }>>,
|
||||
},
|
||||
/// Allocate stake to a network.
|
||||
allocate {
|
||||
/// The network to allocate stake to.
|
||||
network: NetworkId,
|
||||
/// The amount of stake to allocate.
|
||||
amount: Amount,
|
||||
},
|
||||
/// Deallocate stake from a network.
|
||||
///
|
||||
/// This deallocation may be immediate or may be delayed depending on if the origin is an
|
||||
/// active, or even recent, validator. If delayed, it will have to be claimed at a later time.
|
||||
deallocate {
|
||||
/// The network to deallocate stake from.
|
||||
network: NetworkId,
|
||||
/// The amount of stake to deallocate.
|
||||
amount: Amount,
|
||||
},
|
||||
/// Claim a now-unlocked deallocation.
|
||||
claim_deallocation {
|
||||
network: NetworkId,
|
||||
session: Session,
|
||||
/// The validator set which claiming the deallocation was delayed until.
|
||||
deallocation: ValidatorSet,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
impl Call {
|
||||
pub(crate) fn is_signed(&self) -> bool {
|
||||
match self {
|
||||
Call::set_keys { .. } | Call::report_slashes { .. } => false,
|
||||
Call::set_embedded_elliptic_curve_keys { .. } |
|
||||
Call::allocate { .. } |
|
||||
Call::deallocate { .. } |
|
||||
Call::claim_deallocation { .. } => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An event from the validator sets.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Event {
|
||||
/// A new validator set was declared.
|
||||
NewSet {
|
||||
/// The set declared.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
ParticipantRemoved {
|
||||
set: ValidatorSet,
|
||||
removed: SeraiAddress,
|
||||
},
|
||||
KeyGen {
|
||||
/// A validator set has set their keys.
|
||||
SetKeys {
|
||||
/// The set which set their keys.
|
||||
set: ExternalValidatorSet,
|
||||
/// The keys sets.
|
||||
key_pair: KeyPair,
|
||||
},
|
||||
/// A validator set has accepted responsibility from the prior validator set.
|
||||
AcceptedHandover {
|
||||
/// The set which accepted responsibility from the prior set.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
/// A validator set has retired.
|
||||
SetRetired {
|
||||
/// The set retired.
|
||||
set: ValidatorSet,
|
||||
},
|
||||
AllocationIncreased {
|
||||
/// A validator's allocation to a network has increased.
|
||||
Allocation {
|
||||
/// The validator who increased their allocation.
|
||||
validator: SeraiAddress,
|
||||
/// The network the stake was allocated to.
|
||||
network: NetworkId,
|
||||
/// The amount of stake allocated.
|
||||
amount: Amount,
|
||||
},
|
||||
AllocationDecreased {
|
||||
/// A validator's allocation to a network has decreased.
|
||||
Deallocation {
|
||||
/// The validator who decreased their allocation.
|
||||
validator: SeraiAddress,
|
||||
/// The network the stake was deallocated from.
|
||||
network: NetworkId,
|
||||
/// The amount of stake deallocated.
|
||||
amount: Amount,
|
||||
/// The session which claiming the deallocation was delayed until.
|
||||
delayed_until: Option<Session>,
|
||||
},
|
||||
/// A validator's deallocation from a network has been claimed.
|
||||
///
|
||||
/// This is only emited for deallocations which were delayed and has to be explicitly claimed.
|
||||
DeallocationClaimed {
|
||||
/// The validator who claimed their deallocation.
|
||||
validator: SeraiAddress,
|
||||
network: NetworkId,
|
||||
session: Session,
|
||||
/// The validator set the deallocation was delayed until.
|
||||
deallocation: ValidatorSet,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user