mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-22 03:29:34 +00:00
add specific network/coin/balance types (#619)
Some checks failed
Full Stack Tests / build (push) Has been cancelled
Message Queue Tests / build (push) Has been cancelled
Coordinator Tests / build (push) Has been cancelled
Lint / clippy (macos-13) (push) Has been cancelled
Lint / machete (push) Has been cancelled
Lint / clippy (macos-14) (push) Has been cancelled
Lint / clippy (ubuntu-latest) (push) Has been cancelled
Lint / clippy (windows-latest) (push) Has been cancelled
Lint / deny (push) Has been cancelled
Lint / fmt (push) Has been cancelled
Processor Tests / build (push) Has been cancelled
Reproducible Runtime / build (push) Has been cancelled
Tests / test-infra (push) Has been cancelled
Tests / test-serai-client (push) Has been cancelled
Monero Tests / unit-tests (push) Has been cancelled
Monero Tests / integration-tests (v0.17.3.2) (push) Has been cancelled
Monero Tests / integration-tests (v0.18.3.4) (push) Has been cancelled
Tests / test-substrate (push) Has been cancelled
Some checks failed
Full Stack Tests / build (push) Has been cancelled
Message Queue Tests / build (push) Has been cancelled
Coordinator Tests / build (push) Has been cancelled
Lint / clippy (macos-13) (push) Has been cancelled
Lint / machete (push) Has been cancelled
Lint / clippy (macos-14) (push) Has been cancelled
Lint / clippy (ubuntu-latest) (push) Has been cancelled
Lint / clippy (windows-latest) (push) Has been cancelled
Lint / deny (push) Has been cancelled
Lint / fmt (push) Has been cancelled
Processor Tests / build (push) Has been cancelled
Reproducible Runtime / build (push) Has been cancelled
Tests / test-infra (push) Has been cancelled
Tests / test-serai-client (push) Has been cancelled
Monero Tests / unit-tests (push) Has been cancelled
Monero Tests / integration-tests (v0.17.3.2) (push) Has been cancelled
Monero Tests / integration-tests (v0.18.3.4) (push) Has been cancelled
Tests / test-substrate (push) Has been cancelled
* add specific network/coin/balance types * misc fixes * fix clippy * misc fixes * fix pr comments * Make halting for external networks * fix encode/decode
This commit is contained in:
parent
d7ecab605e
commit
435f1d9ae1
91 changed files with 1536 additions and 1055 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -8393,6 +8393,7 @@ dependencies = [
|
|||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ use tokio::{
|
|||
use borsh::BorshSerialize;
|
||||
use sp_application_crypto::RuntimePublic;
|
||||
use serai_client::{
|
||||
primitives::{NETWORKS, NetworkId, Signature},
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
SeraiError, TemporalSerai, Serai,
|
||||
primitives::{ExternalNetworkId, Signature, EXTERNAL_NETWORKS},
|
||||
validator_sets::primitives::{ExternalValidatorSet, Session},
|
||||
Serai, SeraiError, TemporalSerai,
|
||||
};
|
||||
|
||||
use serai_db::{Get, DbTxn, Db, create_db};
|
||||
|
@ -28,17 +28,17 @@ use crate::{
|
|||
|
||||
create_db! {
|
||||
CosignDb {
|
||||
ReceivedCosign: (set: ValidatorSet, block: [u8; 32]) -> CosignedBlock,
|
||||
LatestCosign: (network: NetworkId) -> CosignedBlock,
|
||||
DistinctChain: (set: ValidatorSet) -> (),
|
||||
ReceivedCosign: (set: ExternalValidatorSet, block: [u8; 32]) -> CosignedBlock,
|
||||
LatestCosign: (network: ExternalNetworkId) -> CosignedBlock,
|
||||
DistinctChain: (set: ExternalValidatorSet) -> (),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CosignEvaluator<D: Db> {
|
||||
db: Mutex<D>,
|
||||
serai: Arc<Serai>,
|
||||
stakes: RwLock<Option<HashMap<NetworkId, u64>>>,
|
||||
latest_cosigns: RwLock<HashMap<NetworkId, CosignedBlock>>,
|
||||
stakes: RwLock<Option<HashMap<ExternalNetworkId, u64>>>,
|
||||
latest_cosigns: RwLock<HashMap<ExternalNetworkId, CosignedBlock>>,
|
||||
}
|
||||
|
||||
impl<D: Db> CosignEvaluator<D> {
|
||||
|
@ -79,7 +79,7 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
let serai = self.serai.as_of_latest_finalized_block().await?;
|
||||
|
||||
let mut stakes = HashMap::new();
|
||||
for network in NETWORKS {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
// Use if this network has published a Batch for a short-circuit of if they've ever set a key
|
||||
let set_key = serai.in_instructions().last_batch_for_network(network).await?.is_some();
|
||||
if set_key {
|
||||
|
@ -87,7 +87,7 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
network,
|
||||
serai
|
||||
.validator_sets()
|
||||
.total_allocated_stake(network)
|
||||
.total_allocated_stake(network.into())
|
||||
.await?
|
||||
.expect("network which published a batch didn't have a stake set")
|
||||
.0,
|
||||
|
@ -126,9 +126,9 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
|
||||
async fn set_with_keys_fn(
|
||||
serai: &TemporalSerai<'_>,
|
||||
network: NetworkId,
|
||||
) -> Result<Option<ValidatorSet>, SeraiError> {
|
||||
let Some(latest_session) = serai.validator_sets().session(network).await? else {
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<Option<ExternalValidatorSet>, SeraiError> {
|
||||
let Some(latest_session) = serai.validator_sets().session(network.into()).await? else {
|
||||
log::warn!("received cosign from {:?}, which doesn't yet have a session", network);
|
||||
return Ok(None);
|
||||
};
|
||||
|
@ -136,13 +136,13 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
Ok(Some(
|
||||
if serai
|
||||
.validator_sets()
|
||||
.keys(ValidatorSet { network, session: prior_session })
|
||||
.keys(ExternalValidatorSet { network, session: prior_session })
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
ValidatorSet { network, session: prior_session }
|
||||
ExternalValidatorSet { network, session: prior_session }
|
||||
} else {
|
||||
ValidatorSet { network, session: latest_session }
|
||||
ExternalValidatorSet { network, session: latest_session }
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -204,16 +204,12 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
|
||||
let mut total_stake = 0;
|
||||
let mut total_on_distinct_chain = 0;
|
||||
for network in NETWORKS {
|
||||
if network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
// Get the current set for this network
|
||||
let set_with_keys = {
|
||||
let mut res;
|
||||
while {
|
||||
res = set_with_keys_fn(&serai, cosign.network).await;
|
||||
res = set_with_keys_fn(&serai, network).await;
|
||||
res.is_err()
|
||||
} {
|
||||
log::error!(
|
||||
|
@ -231,7 +227,8 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
let stake = {
|
||||
let mut res;
|
||||
while {
|
||||
res = serai.validator_sets().total_allocated_stake(set_with_keys.network).await;
|
||||
res =
|
||||
serai.validator_sets().total_allocated_stake(set_with_keys.network.into()).await;
|
||||
res.is_err()
|
||||
} {
|
||||
log::error!(
|
||||
|
@ -271,7 +268,7 @@ impl<D: Db> CosignEvaluator<D> {
|
|||
#[allow(clippy::new_ret_no_self)]
|
||||
pub fn new<P: P2p>(db: D, p2p: P, serai: Arc<Serai>) -> mpsc::UnboundedSender<CosignedBlock> {
|
||||
let mut latest_cosigns = HashMap::new();
|
||||
for network in NETWORKS {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
if let Some(cosign) = LatestCosign::get(&db, network) {
|
||||
latest_cosigns.insert(network, cosign);
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ use blake2::{
|
|||
use scale::Encode;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serai_client::{
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
in_instructions::primitives::{Batch, SignedBatch},
|
||||
primitives::ExternalNetworkId,
|
||||
validator_sets::primitives::{ExternalValidatorSet, Session},
|
||||
};
|
||||
|
||||
pub use serai_db::*;
|
||||
|
@ -18,21 +18,21 @@ use crate::tributary::{TributarySpec, Transaction, scanner::RecognizedIdType};
|
|||
|
||||
create_db!(
|
||||
MainDb {
|
||||
HandledMessageDb: (network: NetworkId) -> u64,
|
||||
HandledMessageDb: (network: ExternalNetworkId) -> u64,
|
||||
ActiveTributaryDb: () -> Vec<u8>,
|
||||
RetiredTributaryDb: (set: ValidatorSet) -> (),
|
||||
RetiredTributaryDb: (set: ExternalValidatorSet) -> (),
|
||||
FirstPreprocessDb: (
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
id_type: RecognizedIdType,
|
||||
id: &[u8]
|
||||
) -> Vec<Vec<u8>>,
|
||||
LastReceivedBatchDb: (network: NetworkId) -> u32,
|
||||
ExpectedBatchDb: (network: NetworkId, id: u32) -> [u8; 32],
|
||||
BatchDb: (network: NetworkId, id: u32) -> SignedBatch,
|
||||
LastVerifiedBatchDb: (network: NetworkId) -> u32,
|
||||
HandoverBatchDb: (set: ValidatorSet) -> u32,
|
||||
LookupHandoverBatchDb: (network: NetworkId, batch: u32) -> Session,
|
||||
QueuedBatchesDb: (set: ValidatorSet) -> Vec<u8>
|
||||
LastReceivedBatchDb: (network: ExternalNetworkId) -> u32,
|
||||
ExpectedBatchDb: (network: ExternalNetworkId, id: u32) -> [u8; 32],
|
||||
BatchDb: (network: ExternalNetworkId, id: u32) -> SignedBatch,
|
||||
LastVerifiedBatchDb: (network: ExternalNetworkId) -> u32,
|
||||
HandoverBatchDb: (set: ExternalValidatorSet) -> u32,
|
||||
LookupHandoverBatchDb: (network: ExternalNetworkId, batch: u32) -> Session,
|
||||
QueuedBatchesDb: (set: ExternalValidatorSet) -> Vec<u8>
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -61,7 +61,7 @@ impl ActiveTributaryDb {
|
|||
ActiveTributaryDb::set(txn, &existing_bytes);
|
||||
}
|
||||
|
||||
pub fn retire_tributary(txn: &mut impl DbTxn, set: ValidatorSet) {
|
||||
pub fn retire_tributary(txn: &mut impl DbTxn, set: ExternalValidatorSet) {
|
||||
let mut active = Self::active_tributaries(txn).1;
|
||||
for i in 0 .. active.len() {
|
||||
if active[i].set() == set {
|
||||
|
@ -82,7 +82,7 @@ impl ActiveTributaryDb {
|
|||
impl FirstPreprocessDb {
|
||||
pub fn save_first_preprocess(
|
||||
txn: &mut impl DbTxn,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
id_type: RecognizedIdType,
|
||||
id: &[u8],
|
||||
preprocess: &Vec<Vec<u8>>,
|
||||
|
@ -108,19 +108,19 @@ impl ExpectedBatchDb {
|
|||
}
|
||||
|
||||
impl HandoverBatchDb {
|
||||
pub fn set_handover_batch(txn: &mut impl DbTxn, set: ValidatorSet, batch: u32) {
|
||||
pub fn set_handover_batch(txn: &mut impl DbTxn, set: ExternalValidatorSet, batch: u32) {
|
||||
Self::set(txn, set, &batch);
|
||||
LookupHandoverBatchDb::set(txn, set.network, batch, &set.session);
|
||||
}
|
||||
}
|
||||
impl QueuedBatchesDb {
|
||||
pub fn queue(txn: &mut impl DbTxn, set: ValidatorSet, batch: &Transaction) {
|
||||
pub fn queue(txn: &mut impl DbTxn, set: ExternalValidatorSet, batch: &Transaction) {
|
||||
let mut batches = Self::get(txn, set).unwrap_or_default();
|
||||
batch.write(&mut batches).unwrap();
|
||||
Self::set(txn, set, &batches);
|
||||
}
|
||||
|
||||
pub fn take(txn: &mut impl DbTxn, set: ValidatorSet) -> Vec<Transaction> {
|
||||
pub fn take(txn: &mut impl DbTxn, set: ExternalValidatorSet) -> Vec<Transaction> {
|
||||
let batches_vec = Self::get(txn, set).unwrap_or_default();
|
||||
txn.del(Self::key(set));
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ use serai_db::{DbTxn, Db};
|
|||
use scale::Encode;
|
||||
use borsh::BorshSerialize;
|
||||
use serai_client::{
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
||||
primitives::ExternalNetworkId,
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair, Session},
|
||||
Public, Serai, SeraiInInstructions,
|
||||
};
|
||||
|
||||
|
@ -79,7 +79,7 @@ pub struct ActiveTributary<D: Db, P: P2p> {
|
|||
#[derive(Clone)]
|
||||
pub enum TributaryEvent<D: Db, P: P2p> {
|
||||
NewTributary(ActiveTributary<D, P>),
|
||||
TributaryRetired(ValidatorSet),
|
||||
TributaryRetired(ExternalValidatorSet),
|
||||
}
|
||||
|
||||
// Creates a new tributary and sends it to all listeners.
|
||||
|
@ -145,7 +145,7 @@ async fn handle_processor_message<D: Db, P: P2p>(
|
|||
p2p: &P,
|
||||
cosign_channel: &mpsc::UnboundedSender<CosignedBlock>,
|
||||
tributaries: &HashMap<Session, ActiveTributary<D, P>>,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
msg: &processors::Message,
|
||||
) -> bool {
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
|
@ -193,7 +193,8 @@ async fn handle_processor_message<D: Db, P: P2p>(
|
|||
.iter()
|
||||
.map(|plan| plan.session)
|
||||
.filter(|session| {
|
||||
RetiredTributaryDb::get(&txn, ValidatorSet { network, session: *session }).is_none()
|
||||
RetiredTributaryDb::get(&txn, ExternalValidatorSet { network, session: *session })
|
||||
.is_none()
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
|
@ -265,7 +266,7 @@ async fn handle_processor_message<D: Db, P: P2p>(
|
|||
}
|
||||
// This causes an action on Substrate yet not on any Tributary
|
||||
coordinator::ProcessorMessage::SignedSlashReport { session, signature } => {
|
||||
let set = ValidatorSet { network, session: *session };
|
||||
let set = ExternalValidatorSet { network, session: *session };
|
||||
let signature: &[u8] = signature.as_ref();
|
||||
let signature = serai_client::Signature(signature.try_into().unwrap());
|
||||
|
||||
|
@ -393,7 +394,7 @@ async fn handle_processor_message<D: Db, P: P2p>(
|
|||
if let Some(relevant_tributary_value) = relevant_tributary {
|
||||
if RetiredTributaryDb::get(
|
||||
&txn,
|
||||
ValidatorSet { network: msg.network, session: relevant_tributary_value },
|
||||
ExternalValidatorSet { network: msg.network, session: relevant_tributary_value },
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
|
@ -782,7 +783,7 @@ async fn handle_processor_messages<D: Db, Pro: Processors, P: P2p>(
|
|||
processors: Pro,
|
||||
p2p: P,
|
||||
cosign_channel: mpsc::UnboundedSender<CosignedBlock>,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
mut tributary_event: mpsc::UnboundedReceiver<TributaryEvent<D, P>>,
|
||||
) {
|
||||
let mut tributaries = HashMap::new();
|
||||
|
@ -831,7 +832,7 @@ async fn handle_processor_messages<D: Db, Pro: Processors, P: P2p>(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
async fn handle_cosigns_and_batch_publication<D: Db, P: P2p>(
|
||||
mut db: D,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
mut tributary_event: mpsc::UnboundedReceiver<TributaryEvent<D, P>>,
|
||||
) {
|
||||
let mut tributaries = HashMap::new();
|
||||
|
@ -905,7 +906,7 @@ async fn handle_cosigns_and_batch_publication<D: Db, P: P2p>(
|
|||
for batch in start_id ..= last_id {
|
||||
let is_pre_handover = LookupHandoverBatchDb::get(&txn, network, batch + 1);
|
||||
if let Some(session) = is_pre_handover {
|
||||
let set = ValidatorSet { network, session };
|
||||
let set = ExternalValidatorSet { network, session };
|
||||
let mut queued = QueuedBatchesDb::take(&mut txn, set);
|
||||
// is_handover_batch is only set for handover `Batch`s we're participating in, making
|
||||
// this safe
|
||||
|
@ -923,7 +924,8 @@ async fn handle_cosigns_and_batch_publication<D: Db, P: P2p>(
|
|||
|
||||
let is_handover = LookupHandoverBatchDb::get(&txn, network, batch);
|
||||
if let Some(session) = is_handover {
|
||||
for queued in QueuedBatchesDb::take(&mut txn, ValidatorSet { network, session }) {
|
||||
for queued in QueuedBatchesDb::take(&mut txn, ExternalValidatorSet { network, session })
|
||||
{
|
||||
to_publish.push((session, queued));
|
||||
}
|
||||
}
|
||||
|
@ -970,10 +972,7 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
|
|||
mut tributary_event: broadcast::Receiver<TributaryEvent<D, P>>,
|
||||
) {
|
||||
let mut channels = HashMap::new();
|
||||
for network in serai_client::primitives::NETWORKS {
|
||||
if network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
let (processor_send, processor_recv) = mpsc::unbounded_channel();
|
||||
tokio::spawn(handle_processor_messages(
|
||||
db.clone(),
|
||||
|
@ -1195,7 +1194,7 @@ pub async fn run<D: Db, Pro: Processors, P: P2p>(
|
|||
}
|
||||
});
|
||||
|
||||
move |set: ValidatorSet, genesis, id_type, id: Vec<u8>| {
|
||||
move |set: ExternalValidatorSet, genesis, id_type, id: Vec<u8>| {
|
||||
log::debug!("recognized ID {:?} {}", id_type, hex::encode(&id));
|
||||
let mut raw_db = raw_db.clone();
|
||||
let key = key.clone();
|
||||
|
|
|
@ -11,7 +11,9 @@ use rand_core::{RngCore, OsRng};
|
|||
|
||||
use scale::{Decode, Encode};
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
use serai_client::{primitives::NetworkId, validator_sets::primitives::ValidatorSet, Serai};
|
||||
use serai_client::{
|
||||
primitives::ExternalNetworkId, validator_sets::primitives::ExternalValidatorSet, Serai,
|
||||
};
|
||||
|
||||
use serai_db::Db;
|
||||
|
||||
|
@ -69,7 +71,7 @@ const BLOCKS_PER_BATCH: usize = BLOCKS_PER_MINUTE + 1;
|
|||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub struct CosignedBlock {
|
||||
pub network: NetworkId,
|
||||
pub network: ExternalNetworkId,
|
||||
pub block_number: u64,
|
||||
pub block: [u8; 32],
|
||||
pub signature: [u8; 64],
|
||||
|
@ -208,8 +210,8 @@ pub struct HeartbeatBatch {
|
|||
pub trait P2p: Send + Sync + Clone + fmt::Debug + TributaryP2p {
|
||||
type Id: Send + Sync + Clone + Copy + fmt::Debug;
|
||||
|
||||
async fn subscribe(&self, set: ValidatorSet, genesis: [u8; 32]);
|
||||
async fn unsubscribe(&self, set: ValidatorSet, genesis: [u8; 32]);
|
||||
async fn subscribe(&self, set: ExternalValidatorSet, genesis: [u8; 32]);
|
||||
async fn unsubscribe(&self, set: ExternalValidatorSet, genesis: [u8; 32]);
|
||||
|
||||
async fn send_raw(&self, to: Self::Id, msg: Vec<u8>);
|
||||
async fn broadcast_raw(&self, kind: P2pMessageKind, msg: Vec<u8>);
|
||||
|
@ -309,7 +311,7 @@ struct Behavior {
|
|||
#[allow(clippy::type_complexity)]
|
||||
#[derive(Clone)]
|
||||
pub struct LibP2p {
|
||||
subscribe: Arc<Mutex<mpsc::UnboundedSender<(bool, ValidatorSet, [u8; 32])>>>,
|
||||
subscribe: Arc<Mutex<mpsc::UnboundedSender<(bool, ExternalValidatorSet, [u8; 32])>>>,
|
||||
send: Arc<Mutex<mpsc::UnboundedSender<(PeerId, Vec<u8>)>>>,
|
||||
broadcast: Arc<Mutex<mpsc::UnboundedSender<(P2pMessageKind, Vec<u8>)>>>,
|
||||
receive: Arc<Mutex<mpsc::UnboundedReceiver<Message<Self>>>>,
|
||||
|
@ -397,7 +399,7 @@ impl LibP2p {
|
|||
let (receive_send, receive_recv) = mpsc::unbounded_channel();
|
||||
let (subscribe_send, mut subscribe_recv) = mpsc::unbounded_channel();
|
||||
|
||||
fn topic_for_set(set: ValidatorSet) -> IdentTopic {
|
||||
fn topic_for_set(set: ExternalValidatorSet) -> IdentTopic {
|
||||
IdentTopic::new(format!("{LIBP2P_TOPIC}-{}", hex::encode(set.encode())))
|
||||
}
|
||||
|
||||
|
@ -407,7 +409,8 @@ impl LibP2p {
|
|||
// The addrs we're currently dialing, and the networks associated with them
|
||||
let dialing_peers = Arc::new(RwLock::new(HashMap::new()));
|
||||
// The peers we're currently connected to, and the networks associated with them
|
||||
let connected_peers = Arc::new(RwLock::new(HashMap::<Multiaddr, HashSet<NetworkId>>::new()));
|
||||
let connected_peers =
|
||||
Arc::new(RwLock::new(HashMap::<Multiaddr, HashSet<ExternalNetworkId>>::new()));
|
||||
|
||||
// Find and connect to peers
|
||||
let (connect_to_network_send, mut connect_to_network_recv) =
|
||||
|
@ -420,7 +423,7 @@ impl LibP2p {
|
|||
let connect_to_network_send = connect_to_network_send.clone();
|
||||
async move {
|
||||
loop {
|
||||
let connect = |network: NetworkId, addr: Multiaddr| {
|
||||
let connect = |network: ExternalNetworkId, addr: Multiaddr| {
|
||||
let dialing_peers = dialing_peers.clone();
|
||||
let connected_peers = connected_peers.clone();
|
||||
let to_dial_send = to_dial_send.clone();
|
||||
|
@ -507,7 +510,7 @@ impl LibP2p {
|
|||
connect_to_network_networks.insert(network);
|
||||
}
|
||||
for network in connect_to_network_networks {
|
||||
if let Ok(mut nodes) = serai.p2p_validators(network).await {
|
||||
if let Ok(mut nodes) = serai.p2p_validators(network.into()).await {
|
||||
// If there's an insufficient amount of nodes known, connect to all yet add it
|
||||
// back and break
|
||||
if nodes.len() < TARGET_PEERS {
|
||||
|
@ -557,7 +560,7 @@ impl LibP2p {
|
|||
|
||||
// Subscribe to any new topics
|
||||
set = subscribe_recv.recv() => {
|
||||
let (subscribe, set, genesis): (_, ValidatorSet, [u8; 32]) =
|
||||
let (subscribe, set, genesis): (_, ExternalValidatorSet, [u8; 32]) =
|
||||
set.expect("subscribe_recv closed. are we shutting down?");
|
||||
let topic = topic_for_set(set);
|
||||
if subscribe {
|
||||
|
@ -776,7 +779,7 @@ impl LibP2p {
|
|||
impl P2p for LibP2p {
|
||||
type Id = PeerId;
|
||||
|
||||
async fn subscribe(&self, set: ValidatorSet, genesis: [u8; 32]) {
|
||||
async fn subscribe(&self, set: ExternalValidatorSet, genesis: [u8; 32]) {
|
||||
self
|
||||
.subscribe
|
||||
.lock()
|
||||
|
@ -785,7 +788,7 @@ impl P2p for LibP2p {
|
|||
.expect("subscribe_send closed. are we shutting down?");
|
||||
}
|
||||
|
||||
async fn unsubscribe(&self, set: ValidatorSet, genesis: [u8; 32]) {
|
||||
async fn unsubscribe(&self, set: ExternalValidatorSet, genesis: [u8; 32]) {
|
||||
self
|
||||
.subscribe
|
||||
.lock()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use serai_client::primitives::NetworkId;
|
||||
use serai_client::primitives::ExternalNetworkId;
|
||||
use processor_messages::{ProcessorMessage, CoordinatorMessage};
|
||||
|
||||
use message_queue::{Service, Metadata, client::MessageQueue};
|
||||
|
@ -8,27 +8,27 @@ use message_queue::{Service, Metadata, client::MessageQueue};
|
|||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Message {
|
||||
pub id: u64,
|
||||
pub network: NetworkId,
|
||||
pub network: ExternalNetworkId,
|
||||
pub msg: ProcessorMessage,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait Processors: 'static + Send + Sync + Clone {
|
||||
async fn send(&self, network: NetworkId, msg: impl Send + Into<CoordinatorMessage>);
|
||||
async fn recv(&self, network: NetworkId) -> Message;
|
||||
async fn send(&self, network: ExternalNetworkId, msg: impl Send + Into<CoordinatorMessage>);
|
||||
async fn recv(&self, network: ExternalNetworkId) -> Message;
|
||||
async fn ack(&self, msg: Message);
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Processors for Arc<MessageQueue> {
|
||||
async fn send(&self, network: NetworkId, msg: impl Send + Into<CoordinatorMessage>) {
|
||||
async fn send(&self, network: ExternalNetworkId, msg: impl Send + Into<CoordinatorMessage>) {
|
||||
let msg: CoordinatorMessage = msg.into();
|
||||
let metadata =
|
||||
Metadata { from: self.service, to: Service::Processor(network), intent: msg.intent() };
|
||||
let msg = borsh::to_vec(&msg).unwrap();
|
||||
self.queue(metadata, msg).await;
|
||||
}
|
||||
async fn recv(&self, network: NetworkId) -> Message {
|
||||
async fn recv(&self, network: ExternalNetworkId) -> Message {
|
||||
let msg = self.next(Service::Processor(network)).await;
|
||||
assert_eq!(msg.from, Service::Processor(network));
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ use ciphersuite::{Ciphersuite, Ristretto};
|
|||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_client::{
|
||||
SeraiError, Serai,
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
primitives::ExternalNetworkId,
|
||||
validator_sets::primitives::{ExternalValidatorSet, Session},
|
||||
Serai, SeraiError,
|
||||
};
|
||||
|
||||
use serai_db::*;
|
||||
|
@ -70,13 +70,18 @@ impl LatestCosignedBlock {
|
|||
|
||||
db_channel! {
|
||||
SubstrateDbChannels {
|
||||
CosignTransactions: (network: NetworkId) -> (Session, u64, [u8; 32]),
|
||||
CosignTransactions: (network: ExternalNetworkId) -> (Session, u64, [u8; 32]),
|
||||
}
|
||||
}
|
||||
|
||||
impl CosignTransactions {
|
||||
// Append a cosign transaction.
|
||||
pub fn append_cosign(txn: &mut impl DbTxn, set: ValidatorSet, number: u64, hash: [u8; 32]) {
|
||||
pub fn append_cosign(
|
||||
txn: &mut impl DbTxn,
|
||||
set: ExternalValidatorSet,
|
||||
number: u64,
|
||||
hash: [u8; 32],
|
||||
) {
|
||||
CosignTransactions::send(txn, set.network, &(set.session, number, hash))
|
||||
}
|
||||
}
|
||||
|
@ -256,22 +261,22 @@ async fn advance_cosign_protocol_inner(
|
|||
// Using the keys of the prior block ensures this deadlock isn't reached
|
||||
let serai = serai.as_of(actual_block.header.parent_hash.into());
|
||||
|
||||
for network in serai_client::primitives::NETWORKS {
|
||||
for network in serai_client::primitives::EXTERNAL_NETWORKS {
|
||||
// Get the latest session to have set keys
|
||||
let set_with_keys = {
|
||||
let Some(latest_session) = serai.validator_sets().session(network).await? else {
|
||||
let Some(latest_session) = serai.validator_sets().session(network.into()).await? else {
|
||||
continue;
|
||||
};
|
||||
let prior_session = Session(latest_session.0.saturating_sub(1));
|
||||
if serai
|
||||
.validator_sets()
|
||||
.keys(ValidatorSet { network, session: prior_session })
|
||||
.keys(ExternalValidatorSet { network, session: prior_session })
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
ValidatorSet { network, session: prior_session }
|
||||
ExternalValidatorSet { network, session: prior_session }
|
||||
} else {
|
||||
let set = ValidatorSet { network, session: latest_session };
|
||||
let set = ExternalValidatorSet { network, session: latest_session };
|
||||
if serai.validator_sets().keys(set).await?.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
@ -280,7 +285,7 @@ async fn advance_cosign_protocol_inner(
|
|||
};
|
||||
|
||||
log::debug!("{:?} will be cosigning {block}", set_with_keys.network);
|
||||
cosigning.push((set_with_keys, in_set(key, &serai, set_with_keys).await?.unwrap()));
|
||||
cosigning.push((set_with_keys, in_set(key, &serai, set_with_keys.into()).await?.unwrap()));
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use serai_client::primitives::NetworkId;
|
||||
use serai_client::primitives::ExternalNetworkId;
|
||||
|
||||
pub use serai_db::*;
|
||||
|
||||
|
@ -9,7 +9,7 @@ mod inner_db {
|
|||
SubstrateDb {
|
||||
NextBlock: () -> u64,
|
||||
HandledEvent: (block: [u8; 32]) -> u32,
|
||||
BatchInstructionsHashDb: (network: NetworkId, id: u32) -> [u8; 32]
|
||||
BatchInstructionsHashDb: (network: ExternalNetworkId, id: u32) -> [u8; 32]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -9,11 +9,14 @@ use zeroize::Zeroizing;
|
|||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
|
||||
use serai_client::{
|
||||
SeraiError, Block, Serai, TemporalSerai,
|
||||
primitives::{BlockHash, NetworkId},
|
||||
validator_sets::{primitives::ValidatorSet, ValidatorSetsEvent},
|
||||
in_instructions::InInstructionsEvent,
|
||||
coins::CoinsEvent,
|
||||
in_instructions::InInstructionsEvent,
|
||||
primitives::{BlockHash, ExternalNetworkId},
|
||||
validator_sets::{
|
||||
primitives::{ExternalValidatorSet, ValidatorSet},
|
||||
ValidatorSetsEvent,
|
||||
},
|
||||
Block, Serai, SeraiError, TemporalSerai,
|
||||
};
|
||||
|
||||
use serai_db::DbTxn;
|
||||
|
@ -52,9 +55,9 @@ async fn handle_new_set<D: Db>(
|
|||
new_tributary_spec: &mpsc::UnboundedSender<TributarySpec>,
|
||||
serai: &Serai,
|
||||
block: &Block,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
) -> Result<(), SeraiError> {
|
||||
if in_set(key, &serai.as_of(block.hash()), set)
|
||||
if in_set(key, &serai.as_of(block.hash()), set.into())
|
||||
.await?
|
||||
.expect("NewSet for set which doesn't exist")
|
||||
{
|
||||
|
@ -64,7 +67,7 @@ async fn handle_new_set<D: Db>(
|
|||
let serai = serai.as_of(block.hash());
|
||||
let serai = serai.validator_sets();
|
||||
let set_participants =
|
||||
serai.participants(set.network).await?.expect("NewSet for set which doesn't exist");
|
||||
serai.participants(set.network.into()).await?.expect("NewSet for set which doesn't exist");
|
||||
|
||||
set_participants.into_iter().map(|(k, w)| (k, u16::try_from(w).unwrap())).collect::<Vec<_>>()
|
||||
};
|
||||
|
@ -131,7 +134,7 @@ async fn handle_batch_and_burns<Pro: Processors>(
|
|||
};
|
||||
|
||||
let mut batch_block = HashMap::new();
|
||||
let mut batches = HashMap::<NetworkId, Vec<u32>>::new();
|
||||
let mut batches = HashMap::<ExternalNetworkId, Vec<u32>>::new();
|
||||
let mut burns = HashMap::new();
|
||||
|
||||
let serai = serai.as_of(block.hash());
|
||||
|
@ -205,8 +208,8 @@ async fn handle_block<D: Db, Pro: Processors>(
|
|||
db: &mut D,
|
||||
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||
new_tributary_spec: &mpsc::UnboundedSender<TributarySpec>,
|
||||
perform_slash_report: &mpsc::UnboundedSender<ValidatorSet>,
|
||||
tributary_retired: &mpsc::UnboundedSender<ValidatorSet>,
|
||||
perform_slash_report: &mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
tributary_retired: &mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
processors: &Pro,
|
||||
serai: &Serai,
|
||||
block: Block,
|
||||
|
@ -226,12 +229,8 @@ async fn handle_block<D: Db, Pro: Processors>(
|
|||
panic!("NewSet event wasn't NewSet: {new_set:?}");
|
||||
};
|
||||
|
||||
// If this is Serai, do nothing
|
||||
// We only coordinate/process external networks
|
||||
if set.network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(set) = ExternalValidatorSet::try_from(set) else { continue };
|
||||
if HandledEvent::is_unhandled(db, hash, event_id) {
|
||||
log::info!("found fresh new set event {:?}", new_set);
|
||||
let mut txn = db.txn();
|
||||
|
@ -286,10 +285,7 @@ async fn handle_block<D: Db, Pro: Processors>(
|
|||
panic!("AcceptedHandover event wasn't AcceptedHandover: {accepted_handover:?}");
|
||||
};
|
||||
|
||||
if set.network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(set) = ExternalValidatorSet::try_from(set) else { continue };
|
||||
if HandledEvent::is_unhandled(db, hash, event_id) {
|
||||
log::info!("found fresh accepted handover event {:?}", accepted_handover);
|
||||
// TODO: This isn't atomic with the event handling
|
||||
|
@ -307,10 +303,7 @@ async fn handle_block<D: Db, Pro: Processors>(
|
|||
panic!("SetRetired event wasn't SetRetired: {retired_set:?}");
|
||||
};
|
||||
|
||||
if set.network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(set) = ExternalValidatorSet::try_from(set) else { continue };
|
||||
if HandledEvent::is_unhandled(db, hash, event_id) {
|
||||
log::info!("found fresh set retired event {:?}", retired_set);
|
||||
let mut txn = db.txn();
|
||||
|
@ -340,8 +333,8 @@ async fn handle_new_blocks<D: Db, Pro: Processors>(
|
|||
db: &mut D,
|
||||
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||
new_tributary_spec: &mpsc::UnboundedSender<TributarySpec>,
|
||||
perform_slash_report: &mpsc::UnboundedSender<ValidatorSet>,
|
||||
tributary_retired: &mpsc::UnboundedSender<ValidatorSet>,
|
||||
perform_slash_report: &mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
tributary_retired: &mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
processors: &Pro,
|
||||
serai: &Serai,
|
||||
next_block: &mut u64,
|
||||
|
@ -395,8 +388,8 @@ pub async fn scan_task<D: Db, Pro: Processors>(
|
|||
processors: Pro,
|
||||
serai: Arc<Serai>,
|
||||
new_tributary_spec: mpsc::UnboundedSender<TributarySpec>,
|
||||
perform_slash_report: mpsc::UnboundedSender<ValidatorSet>,
|
||||
tributary_retired: mpsc::UnboundedSender<ValidatorSet>,
|
||||
perform_slash_report: mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
tributary_retired: mpsc::UnboundedSender<ExternalValidatorSet>,
|
||||
) {
|
||||
log::info!("scanning substrate");
|
||||
let mut next_substrate_block = NextBlock::get(&db).unwrap_or_default();
|
||||
|
@ -494,9 +487,12 @@ pub async fn scan_task<D: Db, Pro: Processors>(
|
|||
/// retry.
|
||||
pub(crate) async fn expected_next_batch(
|
||||
serai: &Serai,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<u32, SeraiError> {
|
||||
async fn expected_next_batch_inner(serai: &Serai, network: NetworkId) -> Result<u32, SeraiError> {
|
||||
async fn expected_next_batch_inner(
|
||||
serai: &Serai,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<u32, SeraiError> {
|
||||
let serai = serai.as_of_latest_finalized_block().await?;
|
||||
let last = serai.in_instructions().last_batch_for_network(network).await?;
|
||||
Ok(if let Some(last) = last { last + 1 } else { 0 })
|
||||
|
@ -519,7 +515,7 @@ pub(crate) async fn expected_next_batch(
|
|||
/// This is deemed fine.
|
||||
pub(crate) async fn verify_published_batches<D: Db>(
|
||||
txn: &mut D::Transaction<'_>,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
optimistic_up_to: u32,
|
||||
) -> Option<u32> {
|
||||
// TODO: Localize from MainDb to SubstrateDb
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
collections::{VecDeque, HashSet, HashMap},
|
||||
};
|
||||
|
||||
use serai_client::{primitives::NetworkId, validator_sets::primitives::ValidatorSet};
|
||||
use serai_client::{primitives::ExternalNetworkId, validator_sets::primitives::ExternalValidatorSet};
|
||||
|
||||
use processor_messages::CoordinatorMessage;
|
||||
|
||||
|
@ -20,7 +20,7 @@ use crate::{
|
|||
pub mod tributary;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MemProcessors(pub Arc<RwLock<HashMap<NetworkId, VecDeque<CoordinatorMessage>>>>);
|
||||
pub struct MemProcessors(pub Arc<RwLock<HashMap<ExternalNetworkId, VecDeque<CoordinatorMessage>>>>);
|
||||
impl MemProcessors {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> MemProcessors {
|
||||
|
@ -30,12 +30,12 @@ impl MemProcessors {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl Processors for MemProcessors {
|
||||
async fn send(&self, network: NetworkId, msg: impl Send + Into<CoordinatorMessage>) {
|
||||
async fn send(&self, network: ExternalNetworkId, msg: impl Send + Into<CoordinatorMessage>) {
|
||||
let mut processors = self.0.write().await;
|
||||
let processor = processors.entry(network).or_insert_with(VecDeque::new);
|
||||
processor.push_back(msg.into());
|
||||
}
|
||||
async fn recv(&self, _: NetworkId) -> Message {
|
||||
async fn recv(&self, _: ExternalNetworkId) -> Message {
|
||||
todo!()
|
||||
}
|
||||
async fn ack(&self, _: Message) {
|
||||
|
@ -65,8 +65,8 @@ impl LocalP2p {
|
|||
impl P2p for LocalP2p {
|
||||
type Id = usize;
|
||||
|
||||
async fn subscribe(&self, _set: ValidatorSet, _genesis: [u8; 32]) {}
|
||||
async fn unsubscribe(&self, _set: ValidatorSet, _genesis: [u8; 32]) {}
|
||||
async fn subscribe(&self, _set: ExternalValidatorSet, _genesis: [u8; 32]) {}
|
||||
async fn unsubscribe(&self, _set: ExternalValidatorSet, _genesis: [u8; 32]) {}
|
||||
|
||||
async fn send_raw(&self, to: Self::Id, msg: Vec<u8>) {
|
||||
let mut msg_ref = msg.as_slice();
|
||||
|
|
|
@ -15,8 +15,8 @@ use ciphersuite::{
|
|||
use sp_application_crypto::sr25519;
|
||||
use borsh::BorshDeserialize;
|
||||
use serai_client::{
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
primitives::ExternalNetworkId,
|
||||
validator_sets::primitives::{ExternalValidatorSet, Session},
|
||||
};
|
||||
|
||||
use tokio::time::sleep;
|
||||
|
@ -50,7 +50,7 @@ pub fn new_spec<R: RngCore + CryptoRng>(
|
|||
|
||||
let start_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
|
||||
|
||||
let set = ValidatorSet { session: Session(0), network: NetworkId::Bitcoin };
|
||||
let set = ExternalValidatorSet { session: Session(0), network: ExternalNetworkId::Bitcoin };
|
||||
|
||||
let set_participants = keys
|
||||
.iter()
|
||||
|
|
|
@ -10,7 +10,7 @@ use frost::Participant;
|
|||
use sp_runtime::traits::Verify;
|
||||
use serai_client::{
|
||||
primitives::{SeraiAddress, Signature},
|
||||
validator_sets::primitives::{ValidatorSet, KeyPair},
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair},
|
||||
};
|
||||
|
||||
use tokio::time::sleep;
|
||||
|
@ -350,7 +350,7 @@ async fn dkg_test() {
|
|||
async fn publish_set_keys(
|
||||
&self,
|
||||
_db: &(impl Sync + Get),
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
removed: Vec<SeraiAddress>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
|
@ -362,7 +362,7 @@ async fn dkg_test() {
|
|||
&*serai_client::validator_sets::primitives::set_keys_message(&set, &[], &key_pair),
|
||||
&serai_client::Public(
|
||||
frost::dkg::musig::musig_key::<Ristretto>(
|
||||
&serai_client::validator_sets::primitives::musig_context(set),
|
||||
&serai_client::validator_sets::primitives::musig_context(set.into()),
|
||||
&self.spec.validators().into_iter().map(|(validator, _)| validator).collect::<Vec<_>>()
|
||||
)
|
||||
.unwrap()
|
||||
|
|
|
@ -7,7 +7,7 @@ use ciphersuite::{group::Group, Ciphersuite, Ristretto};
|
|||
use scale::{Encode, Decode};
|
||||
use serai_client::{
|
||||
primitives::{SeraiAddress, Signature},
|
||||
validator_sets::primitives::{MAX_KEY_SHARES_PER_SET, ValidatorSet, KeyPair},
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair, MAX_KEY_SHARES_PER_SET},
|
||||
};
|
||||
use processor_messages::coordinator::SubstrateSignableId;
|
||||
|
||||
|
@ -31,7 +31,7 @@ impl PublishSeraiTransaction for () {
|
|||
async fn publish_set_keys(
|
||||
&self,
|
||||
_db: &(impl Sync + serai_db::Get),
|
||||
_set: ValidatorSet,
|
||||
_set: ExternalValidatorSet,
|
||||
_removed: Vec<SeraiAddress>,
|
||||
_key_pair: KeyPair,
|
||||
_signature: Signature,
|
||||
|
|
|
@ -6,7 +6,7 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
|||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
use frost::Participant;
|
||||
|
||||
use serai_client::validator_sets::primitives::{KeyPair, ValidatorSet};
|
||||
use serai_client::validator_sets::primitives::{KeyPair, ExternalValidatorSet};
|
||||
|
||||
use processor_messages::coordinator::SubstrateSignableId;
|
||||
|
||||
|
@ -46,7 +46,7 @@ pub enum Accumulation {
|
|||
create_db!(
|
||||
Tributary {
|
||||
SeraiBlockNumber: (hash: [u8; 32]) -> u64,
|
||||
SeraiDkgCompleted: (spec: ValidatorSet) -> [u8; 32],
|
||||
SeraiDkgCompleted: (spec: ExternalValidatorSet) -> [u8; 32],
|
||||
|
||||
TributaryBlockNumber: (block: [u8; 32]) -> u32,
|
||||
LastHandledBlock: (genesis: [u8; 32]) -> [u8; 32],
|
||||
|
@ -80,7 +80,7 @@ create_db!(
|
|||
SlashReports: (genesis: [u8; 32], signer: [u8; 32]) -> Vec<u32>,
|
||||
SlashReported: (genesis: [u8; 32]) -> u16,
|
||||
SlashReportCutOff: (genesis: [u8; 32]) -> u64,
|
||||
SlashReport: (set: ValidatorSet) -> Vec<([u8; 32], u32)>,
|
||||
SlashReport: (set: ExternalValidatorSet) -> Vec<([u8; 32], u32)>,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
|
||||
use serai_client::validator_sets::primitives::ValidatorSet;
|
||||
use serai_client::validator_sets::primitives::ExternalValidatorSet;
|
||||
|
||||
use tributary::{
|
||||
ReadWrite,
|
||||
|
@ -40,7 +40,7 @@ pub fn removed_as_of_dkg_attempt(
|
|||
|
||||
pub fn removed_as_of_set_keys(
|
||||
getter: &impl Get,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
genesis: [u8; 32],
|
||||
) -> Option<Vec<<Ristretto as Ciphersuite>::G>> {
|
||||
// SeraiDkgCompleted has the key placed on-chain.
|
||||
|
|
|
@ -10,7 +10,7 @@ use tokio::sync::broadcast;
|
|||
use scale::{Encode, Decode};
|
||||
use serai_client::{
|
||||
primitives::{SeraiAddress, Signature},
|
||||
validator_sets::primitives::{KeyPair, ValidatorSet},
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair},
|
||||
Serai,
|
||||
};
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub enum RecognizedIdType {
|
|||
pub trait RIDTrait {
|
||||
async fn recognized_id(
|
||||
&self,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
genesis: [u8; 32],
|
||||
kind: RecognizedIdType,
|
||||
id: Vec<u8>,
|
||||
|
@ -47,12 +47,12 @@ pub trait RIDTrait {
|
|||
#[async_trait::async_trait]
|
||||
impl<
|
||||
FRid: Send + Future<Output = ()>,
|
||||
F: Sync + Fn(ValidatorSet, [u8; 32], RecognizedIdType, Vec<u8>) -> FRid,
|
||||
F: Sync + Fn(ExternalValidatorSet, [u8; 32], RecognizedIdType, Vec<u8>) -> FRid,
|
||||
> RIDTrait for F
|
||||
{
|
||||
async fn recognized_id(
|
||||
&self,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
genesis: [u8; 32],
|
||||
kind: RecognizedIdType,
|
||||
id: Vec<u8>,
|
||||
|
@ -66,7 +66,7 @@ pub trait PublishSeraiTransaction {
|
|||
async fn publish_set_keys(
|
||||
&self,
|
||||
db: &(impl Sync + Get),
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
removed: Vec<SeraiAddress>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
|
@ -86,7 +86,7 @@ mod impl_pst_for_serai {
|
|||
async fn publish(
|
||||
serai: &Serai,
|
||||
db: &impl Get,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
tx: serai_client::Transaction,
|
||||
meta: $Meta,
|
||||
) -> bool {
|
||||
|
@ -128,7 +128,7 @@ mod impl_pst_for_serai {
|
|||
async fn publish_set_keys(
|
||||
&self,
|
||||
db: &(impl Sync + Get),
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
removed: Vec<SeraiAddress>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
|
@ -140,7 +140,7 @@ mod impl_pst_for_serai {
|
|||
key_pair,
|
||||
signature,
|
||||
);
|
||||
async fn check(serai: SeraiValidatorSets<'_>, set: ValidatorSet, (): ()) -> bool {
|
||||
async fn check(serai: SeraiValidatorSets<'_>, set: ExternalValidatorSet, (): ()) -> bool {
|
||||
if matches!(serai.keys(set).await, Ok(Some(_))) {
|
||||
log::info!("another coordinator set key pair for {:?}", set);
|
||||
return true;
|
||||
|
|
|
@ -119,7 +119,7 @@ impl<T: DbTxn, C: Encode> SigningProtocol<'_, T, C> {
|
|||
|
||||
let algorithm = Schnorrkel::new(b"substrate");
|
||||
let keys: ThresholdKeys<Ristretto> =
|
||||
musig(&musig_context(self.spec.set()), self.key, participants)
|
||||
musig(&musig_context(self.spec.set().into()), self.key, participants)
|
||||
.expect("signing for a set we aren't in/validator present multiple times")
|
||||
.into();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use frost::Participant;
|
|||
use scale::Encode;
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_client::{primitives::PublicKey, validator_sets::primitives::ValidatorSet};
|
||||
use serai_client::{primitives::PublicKey, validator_sets::primitives::ExternalValidatorSet};
|
||||
|
||||
fn borsh_serialize_validators<W: io::Write>(
|
||||
validators: &Vec<(<Ristretto as Ciphersuite>::G, u16)>,
|
||||
|
@ -43,7 +43,7 @@ fn borsh_deserialize_validators<R: io::Read>(
|
|||
pub struct TributarySpec {
|
||||
serai_block: [u8; 32],
|
||||
start_time: u64,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
#[borsh(
|
||||
serialize_with = "borsh_serialize_validators",
|
||||
deserialize_with = "borsh_deserialize_validators"
|
||||
|
@ -55,7 +55,7 @@ impl TributarySpec {
|
|||
pub fn new(
|
||||
serai_block: [u8; 32],
|
||||
start_time: u64,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
set_participants: Vec<(PublicKey, u16)>,
|
||||
) -> TributarySpec {
|
||||
let mut validators = vec![];
|
||||
|
@ -68,7 +68,7 @@ impl TributarySpec {
|
|||
Self { serai_block, start_time, set, validators }
|
||||
}
|
||||
|
||||
pub fn set(&self) -> ValidatorSet {
|
||||
pub fn set(&self) -> ExternalValidatorSet {
|
||||
self.set
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ pub(crate) use std::{
|
|||
pub(crate) use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
||||
pub(crate) use schnorr_signatures::SchnorrSignature;
|
||||
|
||||
pub(crate) use serai_primitives::NetworkId;
|
||||
pub(crate) use serai_primitives::ExternalNetworkId;
|
||||
|
||||
pub(crate) use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
|
@ -193,10 +193,7 @@ async fn main() {
|
|||
KEYS.write().unwrap().insert(service, key);
|
||||
let mut queues = QUEUES.write().unwrap();
|
||||
if service == Service::Coordinator {
|
||||
for network in serai_primitives::NETWORKS {
|
||||
if network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
for network in serai_primitives::EXTERNAL_NETWORKS {
|
||||
queues.insert(
|
||||
(service, Service::Processor(network)),
|
||||
RwLock::new(Queue(db.clone(), service, Service::Processor(network))),
|
||||
|
@ -210,17 +207,13 @@ async fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
// Make queues for each NetworkId, other than Serai
|
||||
for network in serai_primitives::NETWORKS {
|
||||
if network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
// Make queues for each ExternalNetworkId
|
||||
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 {
|
||||
NetworkId::Serai => unreachable!(),
|
||||
NetworkId::Bitcoin => "BITCOIN_KEY",
|
||||
NetworkId::Ethereum => "ETHEREUM_KEY",
|
||||
NetworkId::Monero => "MONERO_KEY",
|
||||
ExternalNetworkId::Bitcoin => "BITCOIN_KEY",
|
||||
ExternalNetworkId::Ethereum => "ETHEREUM_KEY",
|
||||
ExternalNetworkId::Monero => "MONERO_KEY",
|
||||
}) else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
@ -3,11 +3,11 @@ use ciphersuite::{group::GroupEncoding, Ciphersuite, Ristretto};
|
|||
|
||||
use borsh::{BorshSerialize, BorshDeserialize};
|
||||
|
||||
use serai_primitives::NetworkId;
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, BorshSerialize, BorshDeserialize)]
|
||||
pub enum Service {
|
||||
Processor(NetworkId),
|
||||
Processor(ExternalNetworkId),
|
||||
Coordinator,
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ use frost_schnorrkel::Schnorrkel;
|
|||
use log::{info, debug, warn};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NetworkId, BlockHash},
|
||||
primitives::{ExternalNetworkId, BlockHash},
|
||||
in_instructions::primitives::{Batch, SignedBatch, batch_message},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
@ -41,7 +41,7 @@ type SignatureShare = <AlgorithmSignMachine<Ristretto, Schnorrkel> as SignMachin
|
|||
pub struct BatchSigner<D: Db> {
|
||||
db: PhantomData<D>,
|
||||
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
session: Session,
|
||||
keys: Vec<ThresholdKeys<Ristretto>>,
|
||||
|
||||
|
@ -65,7 +65,7 @@ impl<D: Db> fmt::Debug for BatchSigner<D> {
|
|||
|
||||
impl<D: Db> BatchSigner<D> {
|
||||
pub fn new(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
session: Session,
|
||||
keys: Vec<ThresholdKeys<Ristretto>>,
|
||||
) -> BatchSigner<D> {
|
||||
|
|
|
@ -9,7 +9,7 @@ use log::{info, warn};
|
|||
use tokio::time::sleep;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{BlockHash, NetworkId},
|
||||
primitives::{BlockHash, ExternalNetworkId},
|
||||
validator_sets::primitives::{Session, KeyPair},
|
||||
};
|
||||
|
||||
|
@ -736,19 +736,23 @@ async fn main() {
|
|||
"http://".to_string() + &login + "@" + &hostname + ":" + &port
|
||||
};
|
||||
let network_id = match env::var("NETWORK").expect("network wasn't specified").as_str() {
|
||||
"bitcoin" => NetworkId::Bitcoin,
|
||||
"ethereum" => NetworkId::Ethereum,
|
||||
"monero" => NetworkId::Monero,
|
||||
"bitcoin" => ExternalNetworkId::Bitcoin,
|
||||
"ethereum" => ExternalNetworkId::Ethereum,
|
||||
"monero" => ExternalNetworkId::Monero,
|
||||
_ => panic!("unrecognized network"),
|
||||
};
|
||||
|
||||
let coordinator = MessageQueue::from_env(Service::Processor(network_id));
|
||||
|
||||
// This allow is necessary since each configuration deletes the other networks from the following
|
||||
// match arms. So we match all cases but since all cases already there according to the compiler
|
||||
// we put this to allow clippy to get pass this.
|
||||
#[allow(unreachable_patterns)]
|
||||
match network_id {
|
||||
#[cfg(feature = "bitcoin")]
|
||||
NetworkId::Bitcoin => run(db, Bitcoin::new(url).await, coordinator).await,
|
||||
ExternalNetworkId::Bitcoin => run(db, Bitcoin::new(url).await, coordinator).await,
|
||||
#[cfg(feature = "ethereum")]
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
let relayer_hostname = env::var("ETHEREUM_RELAYER_HOSTNAME")
|
||||
.expect("ethereum relayer hostname wasn't specified")
|
||||
.to_string();
|
||||
|
@ -758,7 +762,7 @@ async fn main() {
|
|||
run(db.clone(), Ethereum::new(db, url, relayer_url).await, coordinator).await
|
||||
}
|
||||
#[cfg(feature = "monero")]
|
||||
NetworkId::Monero => run(db, Monero::new(url).await, coordinator).await,
|
||||
ExternalNetworkId::Monero => run(db, Monero::new(url).await, coordinator).await,
|
||||
_ => panic!("spawning a processor for an unsupported network"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@ use ciphersuite::Ciphersuite;
|
|||
pub use serai_db::*;
|
||||
|
||||
use scale::{Encode, Decode};
|
||||
use serai_client::{primitives::Balance, in_instructions::primitives::InInstructionWithBalance};
|
||||
#[rustfmt::skip]
|
||||
use serai_client::{
|
||||
in_instructions::primitives::InInstructionWithBalance,
|
||||
primitives::ExternalBalance
|
||||
};
|
||||
|
||||
use crate::{
|
||||
Get, Plan,
|
||||
|
@ -69,7 +73,7 @@ create_db!(
|
|||
OperatingCostsDb: () -> u64,
|
||||
ResolvedDb: (tx: &[u8]) -> [u8; 32],
|
||||
SigningDb: (key: &[u8]) -> Vec<u8>,
|
||||
ForwardedOutputDb: (balance: Balance) -> Vec<u8>,
|
||||
ForwardedOutputDb: (balance: ExternalBalance) -> Vec<u8>,
|
||||
DelayedOutputDb: () -> Vec<u8>
|
||||
}
|
||||
);
|
||||
|
@ -224,7 +228,7 @@ impl ForwardedOutputDb {
|
|||
|
||||
pub fn take_forwarded_output(
|
||||
txn: &mut impl DbTxn,
|
||||
balance: Balance,
|
||||
balance: ExternalBalance,
|
||||
) -> Option<InInstructionWithBalance> {
|
||||
let outputs = Self::get(txn, balance)?;
|
||||
let mut outputs_ref = outputs.as_slice();
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::io;
|
|||
|
||||
use ciphersuite::Ciphersuite;
|
||||
|
||||
use serai_client::primitives::{NetworkId, Balance};
|
||||
use serai_client::primitives::{ExternalBalance, ExternalNetworkId};
|
||||
|
||||
use crate::{networks::Network, Db, Payment, Plan};
|
||||
|
||||
|
@ -34,18 +34,18 @@ pub trait Scheduler<N: Network>: Sized + Clone + PartialEq + Debug {
|
|||
fn new<D: Db>(
|
||||
txn: &mut D::Transaction<'_>,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Self;
|
||||
|
||||
/// Load a Scheduler from the DB.
|
||||
fn from_db<D: Db>(
|
||||
db: &D,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> io::Result<Self>;
|
||||
|
||||
/// Check if a branch is usable.
|
||||
fn can_use_branch(&self, balance: Balance) -> bool;
|
||||
fn can_use_branch(&self, balance: ExternalBalance) -> bool;
|
||||
|
||||
/// Schedule a series of outputs/payments.
|
||||
fn schedule<D: Db>(
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{io, collections::HashSet};
|
|||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
|
||||
use serai_client::primitives::{NetworkId, Coin, Balance};
|
||||
use serai_client::primitives::{ExternalBalance, ExternalCoin, ExternalNetworkId};
|
||||
|
||||
use crate::{
|
||||
Get, DbTxn, Db, Payment, Plan, create_db,
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Scheduler<N: Network> {
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
coins: HashSet<Coin>,
|
||||
coins: HashSet<ExternalCoin>,
|
||||
rotated: bool,
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ impl<N: Network<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
fn new<D: Db>(
|
||||
_txn: &mut D::Transaction<'_>,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Self {
|
||||
assert!(N::branch_address(key).is_none());
|
||||
assert!(N::change_address(key).is_none());
|
||||
|
@ -91,7 +91,7 @@ impl<N: Network<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
fn from_db<D: Db>(
|
||||
db: &D,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> io::Result<Self> {
|
||||
Ok(Scheduler {
|
||||
key,
|
||||
|
@ -100,7 +100,7 @@ impl<N: Network<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
})
|
||||
}
|
||||
|
||||
fn can_use_branch(&self, _balance: Balance) -> bool {
|
||||
fn can_use_branch(&self, _balance: ExternalBalance) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
|
||||
use ciphersuite::{group::GroupEncoding, Ciphersuite};
|
||||
|
||||
use serai_client::primitives::{NetworkId, Coin, Amount, Balance};
|
||||
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount, ExternalBalance};
|
||||
|
||||
use crate::{
|
||||
DbTxn, Db, Payment, Plan,
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
|||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Scheduler<N: UtxoNetwork> {
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
|
||||
// Serai, when it has more outputs expected than it can handle in a single transaction, will
|
||||
// schedule the outputs to be handled later. Immediately, it just creates additional outputs
|
||||
|
@ -57,7 +57,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
|||
|
||||
fn read<R: Read>(
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
reader: &mut R,
|
||||
) -> io::Result<Self> {
|
||||
let mut read_plans = || -> io::Result<_> {
|
||||
|
@ -145,7 +145,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
|||
pub fn new<D: Db>(
|
||||
txn: &mut D::Transaction<'_>,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Self {
|
||||
assert!(N::branch_address(key).is_some());
|
||||
assert!(N::change_address(key).is_some());
|
||||
|
@ -173,7 +173,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
|||
pub fn from_db<D: Db>(
|
||||
db: &D,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> io::Result<Self> {
|
||||
let coin = {
|
||||
let coins = network.coins();
|
||||
|
@ -190,7 +190,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
|||
Self::read(key, coin, reader)
|
||||
}
|
||||
|
||||
pub fn can_use_branch(&self, balance: Balance) -> bool {
|
||||
pub fn can_use_branch(&self, balance: ExternalBalance) -> bool {
|
||||
assert_eq!(balance.coin, self.coin);
|
||||
self.plans.contains_key(&balance.amount.0)
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> Scheduler<N> {
|
|||
Payment {
|
||||
address: branch_address.clone(),
|
||||
data: None,
|
||||
balance: Balance { coin: self.coin, amount: Amount(amount) },
|
||||
balance: ExternalBalance { coin: self.coin, amount: Amount(amount) },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
fn new<D: Db>(
|
||||
txn: &mut D::Transaction<'_>,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Self {
|
||||
Scheduler::new::<D>(txn, key, network)
|
||||
}
|
||||
|
@ -545,13 +545,13 @@ impl<N: UtxoNetwork<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
fn from_db<D: Db>(
|
||||
db: &D,
|
||||
key: <N::Curve as Ciphersuite>::G,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> io::Result<Self> {
|
||||
Scheduler::from_db::<D>(db, key, network)
|
||||
}
|
||||
|
||||
/// Check if a branch is usable.
|
||||
fn can_use_branch(&self, balance: Balance) -> bool {
|
||||
fn can_use_branch(&self, balance: ExternalBalance) -> bool {
|
||||
Scheduler::can_use_branch(self, balance)
|
||||
}
|
||||
|
||||
|
@ -574,7 +574,7 @@ impl<N: UtxoNetwork<Scheduler = Self>> SchedulerTrait<N> for Scheduler<N> {
|
|||
|
||||
/// Note a branch output as having been created, with the amount it was actually created with,
|
||||
/// or not having been created due to being too small.
|
||||
// TODO: Move this to Balance.
|
||||
// TODO: Move this to ExternalBalance.
|
||||
fn created_output<D: Db>(
|
||||
&mut self,
|
||||
txn: &mut D::Transaction<'_>,
|
||||
|
|
|
@ -42,7 +42,7 @@ use bitcoin_serai::bitcoin::{
|
|||
};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{MAX_DATA_LEN, Coin, NetworkId, Amount, Balance},
|
||||
primitives::{MAX_DATA_LEN, ExternalCoin, ExternalNetworkId, Amount, ExternalBalance},
|
||||
networks::bitcoin::Address,
|
||||
};
|
||||
|
||||
|
@ -125,8 +125,8 @@ impl OutputTrait<Bitcoin> for Output {
|
|||
self.presumed_origin.clone()
|
||||
}
|
||||
|
||||
fn balance(&self) -> Balance {
|
||||
Balance { coin: Coin::Bitcoin, amount: Amount(self.output.value()) }
|
||||
fn balance(&self) -> ExternalBalance {
|
||||
ExternalBalance { coin: ExternalCoin::Bitcoin, amount: Amount(self.output.value()) }
|
||||
}
|
||||
|
||||
fn data(&self) -> &[u8] {
|
||||
|
@ -423,7 +423,7 @@ impl Bitcoin {
|
|||
calculating_fee: bool,
|
||||
) -> Result<Option<BSignableTransaction>, NetworkError> {
|
||||
for payment in payments {
|
||||
assert_eq!(payment.balance.coin, Coin::Bitcoin);
|
||||
assert_eq!(payment.balance.coin, ExternalCoin::Bitcoin);
|
||||
}
|
||||
|
||||
// TODO2: Use an fee representative of several blocks, cached inside Self
|
||||
|
@ -598,7 +598,7 @@ impl Network for Bitcoin {
|
|||
|
||||
type Address = Address;
|
||||
|
||||
const NETWORK: NetworkId = NetworkId::Bitcoin;
|
||||
const NETWORK: ExternalNetworkId = ExternalNetworkId::Bitcoin;
|
||||
const ID: &'static str = "Bitcoin";
|
||||
const ESTIMATED_BLOCK_TIME_IN_SECONDS: usize = 600;
|
||||
const CONFIRMATIONS: usize = 6;
|
||||
|
|
|
@ -38,7 +38,7 @@ use tokio::{
|
|||
};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Coin, Amount, Balance, NetworkId},
|
||||
primitives::{ExternalCoin, Amount, ExternalBalance, ExternalNetworkId},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
||||
|
@ -68,20 +68,20 @@ const DAI: [u8; 20] =
|
|||
Err(_) => panic!("invalid test DAI hex address"),
|
||||
};
|
||||
|
||||
fn coin_to_serai_coin(coin: &EthereumCoin) -> Option<Coin> {
|
||||
fn coin_to_serai_coin(coin: &EthereumCoin) -> Option<ExternalCoin> {
|
||||
match coin {
|
||||
EthereumCoin::Ether => Some(Coin::Ether),
|
||||
EthereumCoin::Ether => Some(ExternalCoin::Ether),
|
||||
EthereumCoin::Erc20(token) => {
|
||||
if *token == DAI {
|
||||
return Some(Coin::Dai);
|
||||
return Some(ExternalCoin::Dai);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn amount_to_serai_amount(coin: Coin, amount: U256) -> Amount {
|
||||
assert_eq!(coin.network(), NetworkId::Ethereum);
|
||||
fn amount_to_serai_amount(coin: ExternalCoin, amount: U256) -> Amount {
|
||||
assert_eq!(coin.network(), ExternalNetworkId::Ethereum);
|
||||
assert_eq!(coin.decimals(), 8);
|
||||
// Remove 10 decimals so we go from 18 decimals to 8 decimals
|
||||
let divisor = U256::from(10_000_000_000u64);
|
||||
|
@ -89,8 +89,8 @@ fn amount_to_serai_amount(coin: Coin, amount: U256) -> Amount {
|
|||
Amount(u64::try_from(amount / divisor).unwrap())
|
||||
}
|
||||
|
||||
fn balance_to_ethereum_amount(balance: Balance) -> U256 {
|
||||
assert_eq!(balance.coin.network(), NetworkId::Ethereum);
|
||||
fn balance_to_ethereum_amount(balance: ExternalBalance) -> U256 {
|
||||
assert_eq!(balance.coin.network(), ExternalNetworkId::Ethereum);
|
||||
assert_eq!(balance.coin.decimals(), 8);
|
||||
// Restore 10 decimals so we go from 8 decimals to 18 decimals
|
||||
let factor = U256::from(10_000_000_000u64);
|
||||
|
@ -201,14 +201,14 @@ impl<D: Db> Output<Ethereum<D>> for EthereumInInstruction {
|
|||
Some(Address(self.from))
|
||||
}
|
||||
|
||||
fn balance(&self) -> Balance {
|
||||
fn balance(&self) -> ExternalBalance {
|
||||
let coin = coin_to_serai_coin(&self.coin).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"requesting coin for an EthereumInInstruction with a coin {}",
|
||||
"we don't handle. this never should have been yielded"
|
||||
)
|
||||
});
|
||||
Balance { coin, amount: amount_to_serai_amount(coin, self.amount) }
|
||||
ExternalBalance { coin, amount: amount_to_serai_amount(coin, self.amount) }
|
||||
}
|
||||
fn data(&self) -> &[u8] {
|
||||
&self.data
|
||||
|
@ -394,7 +394,7 @@ impl<D: Db> Network for Ethereum<D> {
|
|||
|
||||
type Address = Address;
|
||||
|
||||
const NETWORK: NetworkId = NetworkId::Ethereum;
|
||||
const NETWORK: ExternalNetworkId = ExternalNetworkId::Ethereum;
|
||||
const ID: &'static str = "Ethereum";
|
||||
const ESTIMATED_BLOCK_TIME_IN_SECONDS: usize = 32 * 12;
|
||||
const CONFIRMATIONS: usize = 1;
|
||||
|
@ -681,7 +681,7 @@ impl<D: Db> Network for Ethereum<D> {
|
|||
OutInstructionTarget::Direct(payment.address.0)
|
||||
},
|
||||
value: {
|
||||
assert_eq!(payment.balance.coin, Coin::Ether); // TODO
|
||||
assert_eq!(payment.balance.coin, ExternalCoin::Ether); // TODO
|
||||
balance_to_ethereum_amount(payment.balance)
|
||||
},
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ use frost::{
|
|||
sign::PreprocessMachine,
|
||||
};
|
||||
|
||||
use serai_client::primitives::{NetworkId, Balance};
|
||||
use serai_client::primitives::{ExternalBalance, ExternalNetworkId};
|
||||
|
||||
use log::error;
|
||||
|
||||
|
@ -115,7 +115,7 @@ pub trait Output<N: Network>: Send + Sync + Sized + Clone + PartialEq + Eq + Deb
|
|||
|
||||
fn presumed_origin(&self) -> Option<N::Address>;
|
||||
|
||||
fn balance(&self) -> Balance;
|
||||
fn balance(&self) -> ExternalBalance;
|
||||
fn data(&self) -> &[u8];
|
||||
|
||||
fn write<W: io::Write>(&self, writer: &mut W) -> io::Result<()>;
|
||||
|
@ -126,13 +126,13 @@ pub trait Output<N: Network>: Send + Sync + Sized + Clone + PartialEq + Eq + Deb
|
|||
pub trait Transaction<N: Network>: Send + Sync + Sized + Clone + PartialEq + Debug {
|
||||
type Id: 'static + Id;
|
||||
fn id(&self) -> Self::Id;
|
||||
// TODO: Move to Balance
|
||||
// TODO: Move to ExternalBalance
|
||||
#[cfg(test)]
|
||||
async fn fee(&self, network: &N) -> u64;
|
||||
}
|
||||
|
||||
pub trait SignableTransaction: Send + Sync + Clone + Debug {
|
||||
// TODO: Move to Balance
|
||||
// TODO: Move to ExternalBalance
|
||||
fn fee(&self) -> u64;
|
||||
}
|
||||
|
||||
|
@ -280,7 +280,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Debug {
|
|||
+ TryFrom<Vec<u8>>;
|
||||
|
||||
/// Network ID for this network.
|
||||
const NETWORK: NetworkId;
|
||||
const NETWORK: ExternalNetworkId;
|
||||
/// String ID for this network.
|
||||
const ID: &'static str;
|
||||
/// The estimated amount of time a block will take.
|
||||
|
@ -297,7 +297,7 @@ pub trait Network: 'static + Send + Sync + Clone + PartialEq + Debug {
|
|||
/// For any received output, there's the cost to spend the output. This value MUST exceed the
|
||||
/// cost to spend said output, and should by a notable margin (not just 2x, yet an order of
|
||||
/// magnitude).
|
||||
// TODO: Dust needs to be diversified per Coin
|
||||
// TODO: Dust needs to be diversified per ExternalCoin
|
||||
const DUST: u64;
|
||||
|
||||
/// The cost to perform input aggregation with a 2-input 1-output TX.
|
||||
|
|
|
@ -31,7 +31,7 @@ use monero_wallet::Scanner;
|
|||
use tokio::time::sleep;
|
||||
|
||||
pub use serai_client::{
|
||||
primitives::{MAX_DATA_LEN, Coin, NetworkId, Amount, Balance},
|
||||
primitives::{MAX_DATA_LEN, ExternalCoin, ExternalNetworkId, Amount, ExternalBalance},
|
||||
networks::monero::Address,
|
||||
};
|
||||
|
||||
|
@ -85,8 +85,8 @@ impl OutputTrait<Monero> for Output {
|
|||
None
|
||||
}
|
||||
|
||||
fn balance(&self) -> Balance {
|
||||
Balance { coin: Coin::Monero, amount: Amount(self.0.commitment().amount) }
|
||||
fn balance(&self) -> ExternalBalance {
|
||||
ExternalBalance { coin: ExternalCoin::Monero, amount: Amount(self.0.commitment().amount) }
|
||||
}
|
||||
|
||||
fn data(&self) -> &[u8] {
|
||||
|
@ -308,7 +308,7 @@ impl Monero {
|
|||
calculating_fee: bool,
|
||||
) -> Result<Option<MakeSignableTransactionResult>, NetworkError> {
|
||||
for payment in payments {
|
||||
assert_eq!(payment.balance.coin, Coin::Monero);
|
||||
assert_eq!(payment.balance.coin, ExternalCoin::Monero);
|
||||
}
|
||||
|
||||
// TODO2: Use an fee representative of several blocks, cached inside Self
|
||||
|
@ -363,7 +363,7 @@ impl Monero {
|
|||
.legacy_address(MoneroNetwork::Mainnet),
|
||||
)
|
||||
.unwrap(),
|
||||
balance: Balance { coin: Coin::Monero, amount: Amount(0) },
|
||||
balance: ExternalBalance { coin: ExternalCoin::Monero, amount: Amount(0) },
|
||||
data: None,
|
||||
});
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ impl Network for Monero {
|
|||
|
||||
type Address = Address;
|
||||
|
||||
const NETWORK: NetworkId = NetworkId::Monero;
|
||||
const NETWORK: ExternalNetworkId = ExternalNetworkId::Monero;
|
||||
const ID: &'static str = "Monero";
|
||||
const ESTIMATED_BLOCK_TIME_IN_SECONDS: usize = 120;
|
||||
const CONFIRMATIONS: usize = 10;
|
||||
|
|
|
@ -6,7 +6,7 @@ use transcript::{Transcript, RecommendedTranscript};
|
|||
use ciphersuite::group::GroupEncoding;
|
||||
use frost::curve::Ciphersuite;
|
||||
|
||||
use serai_client::primitives::Balance;
|
||||
use serai_client::primitives::ExternalBalance;
|
||||
|
||||
use crate::{
|
||||
networks::{Output, Network},
|
||||
|
@ -17,7 +17,7 @@ use crate::{
|
|||
pub struct Payment<N: Network> {
|
||||
pub address: N::Address,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub balance: Balance,
|
||||
pub balance: ExternalBalance,
|
||||
}
|
||||
|
||||
impl<N: Network> Payment<N> {
|
||||
|
@ -69,7 +69,7 @@ impl<N: Network> Payment<N> {
|
|||
None
|
||||
};
|
||||
|
||||
let balance = Balance::decode(&mut scale::IoReader(reader))
|
||||
let balance = ExternalBalance::decode(&mut scale::IoReader(reader))
|
||||
.map_err(|_| io::Error::other("invalid balance"))?;
|
||||
|
||||
Ok(Payment { address, data, balance })
|
||||
|
|
|
@ -17,9 +17,9 @@ use frost_schnorrkel::Schnorrkel;
|
|||
use log::{info, warn};
|
||||
|
||||
use serai_client::{
|
||||
primitives::ExternalNetworkId,
|
||||
validator_sets::primitives::{report_slashes_message, ExternalValidatorSet, Session},
|
||||
Public,
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{Session, ValidatorSet, report_slashes_message},
|
||||
};
|
||||
|
||||
use messages::coordinator::*;
|
||||
|
@ -38,7 +38,7 @@ type SignatureShare = <AlgorithmSignMachine<Ristretto, Schnorrkel> as SignMachin
|
|||
>>::SignatureShare;
|
||||
|
||||
pub struct SlashReportSigner {
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
session: Session,
|
||||
keys: Vec<ThresholdKeys<Ristretto>>,
|
||||
report: Vec<([u8; 32], u32)>,
|
||||
|
@ -66,7 +66,7 @@ impl fmt::Debug for SlashReportSigner {
|
|||
impl SlashReportSigner {
|
||||
pub fn new(
|
||||
txn: &mut impl DbTxn,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
session: Session,
|
||||
keys: Vec<ThresholdKeys<Ristretto>>,
|
||||
report: Vec<([u8; 32], u32)>,
|
||||
|
@ -178,7 +178,7 @@ impl SlashReportSigner {
|
|||
let (machine, share) = match machine.sign(
|
||||
preprocesses,
|
||||
&report_slashes_message(
|
||||
&ValidatorSet { network: self.network, session: self.session },
|
||||
&ExternalValidatorSet { network: self.network, session: self.session },
|
||||
&self
|
||||
.report
|
||||
.clone()
|
||||
|
|
|
@ -33,17 +33,17 @@ fn test_batch_signer() {
|
|||
let block = BlockHash([0xaa; 32]);
|
||||
|
||||
let batch = Batch {
|
||||
network: NetworkId::Monero,
|
||||
network: ExternalNetworkId::Monero,
|
||||
id,
|
||||
block,
|
||||
instructions: vec![
|
||||
InInstructionWithBalance {
|
||||
instruction: InInstruction::Transfer(SeraiAddress([0xbb; 32])),
|
||||
balance: Balance { coin: Coin::Bitcoin, amount: Amount(1000) },
|
||||
balance: ExternalBalance { coin: ExternalCoin::Bitcoin, amount: Amount(1000) },
|
||||
},
|
||||
InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::SwapAndAddLiquidity(SeraiAddress([0xbb; 32]))),
|
||||
balance: Balance { coin: Coin::Monero, amount: Amount(9999999999999999) },
|
||||
balance: ExternalBalance { coin: ExternalCoin::Monero, amount: Amount(9999999999999999) },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -70,7 +70,7 @@ fn test_batch_signer() {
|
|||
let i = Participant::new(u16::try_from(i).unwrap()).unwrap();
|
||||
let keys = keys.get(&i).unwrap().clone();
|
||||
|
||||
let mut signer = BatchSigner::<MemDb>::new(NetworkId::Monero, Session(0), vec![keys]);
|
||||
let mut signer = BatchSigner::<MemDb>::new(ExternalNetworkId::Monero, Session(0), vec![keys]);
|
||||
let mut db = MemDb::new();
|
||||
|
||||
let mut txn = db.txn();
|
||||
|
|
|
@ -12,7 +12,7 @@ use frost::{
|
|||
use serai_db::{DbTxn, Db, MemDb};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NetworkId, Coin, Amount, Balance},
|
||||
primitives::{ExternalNetworkId, ExternalCoin, Amount, ExternalBalance},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
||||
|
@ -185,12 +185,11 @@ pub async fn test_signer<N: Network>(
|
|||
let payments = vec![Payment {
|
||||
address: N::external_address(&network, key).await,
|
||||
data: None,
|
||||
balance: Balance {
|
||||
balance: ExternalBalance {
|
||||
coin: match N::NETWORK {
|
||||
NetworkId::Serai => panic!("test_signer called with Serai"),
|
||||
NetworkId::Bitcoin => Coin::Bitcoin,
|
||||
NetworkId::Ethereum => Coin::Ether,
|
||||
NetworkId::Monero => Coin::Monero,
|
||||
ExternalNetworkId::Bitcoin => ExternalCoin::Bitcoin,
|
||||
ExternalNetworkId::Ethereum => ExternalCoin::Ether,
|
||||
ExternalNetworkId::Monero => ExternalCoin::Monero,
|
||||
},
|
||||
amount: Amount(amount),
|
||||
},
|
||||
|
@ -224,7 +223,7 @@ pub async fn test_signer<N: Network>(
|
|||
.await;
|
||||
// Don't run if Ethereum as the received output will revert by the contract
|
||||
// (and therefore not actually exist)
|
||||
if N::NETWORK != NetworkId::Ethereum {
|
||||
if N::NETWORK != ExternalNetworkId::Ethereum {
|
||||
assert_eq!(outputs.len(), 1 + usize::from(u8::from(plan.change.is_some())));
|
||||
// Adjust the amount for the fees
|
||||
let amount = amount - tx.fee(&network).await;
|
||||
|
|
|
@ -11,7 +11,7 @@ use tokio::time::timeout;
|
|||
use serai_db::{DbTxn, Db, MemDb};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NetworkId, Coin, Amount, Balance},
|
||||
primitives::{ExternalNetworkId, ExternalCoin, Amount, ExternalBalance},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
||||
|
@ -89,12 +89,11 @@ pub async fn test_wallet<N: Network>(
|
|||
vec![Payment {
|
||||
address: N::external_address(&network, key).await,
|
||||
data: None,
|
||||
balance: Balance {
|
||||
balance: ExternalBalance {
|
||||
coin: match N::NETWORK {
|
||||
NetworkId::Serai => panic!("test_wallet called with Serai"),
|
||||
NetworkId::Bitcoin => Coin::Bitcoin,
|
||||
NetworkId::Ethereum => Coin::Ether,
|
||||
NetworkId::Monero => Coin::Monero,
|
||||
ExternalNetworkId::Bitcoin => ExternalCoin::Bitcoin,
|
||||
ExternalNetworkId::Ethereum => ExternalCoin::Ether,
|
||||
ExternalNetworkId::Monero => ExternalCoin::Monero,
|
||||
},
|
||||
amount: Amount(amount),
|
||||
},
|
||||
|
@ -117,12 +116,11 @@ pub async fn test_wallet<N: Network>(
|
|||
vec![Payment {
|
||||
address: N::external_address(&network, key).await,
|
||||
data: None,
|
||||
balance: Balance {
|
||||
balance: ExternalBalance {
|
||||
coin: match N::NETWORK {
|
||||
NetworkId::Serai => panic!("test_wallet called with Serai"),
|
||||
NetworkId::Bitcoin => Coin::Bitcoin,
|
||||
NetworkId::Ethereum => Coin::Ether,
|
||||
NetworkId::Monero => Coin::Monero,
|
||||
ExternalNetworkId::Bitcoin => ExternalCoin::Bitcoin,
|
||||
ExternalNetworkId::Ethereum => ExternalCoin::Ether,
|
||||
ExternalNetworkId::Monero => ExternalCoin::Monero,
|
||||
},
|
||||
amount: Amount(amount),
|
||||
}
|
||||
|
@ -160,7 +158,7 @@ pub async fn test_wallet<N: Network>(
|
|||
|
||||
// Don't run if Ethereum as the received output will revert by the contract
|
||||
// (and therefore not actually exist)
|
||||
if N::NETWORK != NetworkId::Ethereum {
|
||||
if N::NETWORK != ExternalNetworkId::Ethereum {
|
||||
assert_eq!(outputs.len(), 1 + usize::from(u8::from(plans[0].change.is_some())));
|
||||
// Adjust the amount for the fees
|
||||
let amount = amount - tx.fee(&network).await;
|
||||
|
@ -183,7 +181,7 @@ pub async fn test_wallet<N: Network>(
|
|||
network.mine_block().await;
|
||||
}
|
||||
|
||||
if N::NETWORK != NetworkId::Ethereum {
|
||||
if N::NETWORK != ExternalNetworkId::Ethereum {
|
||||
match timeout(Duration::from_secs(30), scanner.events.recv()).await.unwrap().unwrap() {
|
||||
ScannerEvent::Block { is_retirement_block, block: block_id, outputs: these_outputs } => {
|
||||
scanner.multisig_completed.send(false).unwrap();
|
||||
|
|
|
@ -2,7 +2,7 @@ use sp_runtime::BoundedVec;
|
|||
|
||||
use serai_primitives::*;
|
||||
|
||||
type PoolId = Coin;
|
||||
type PoolId = ExternalCoin;
|
||||
type MaxSwapPathLength = sp_core::ConstU32<3>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
|
@ -10,7 +10,7 @@ type MaxSwapPathLength = sp_core::ConstU32<3>;
|
|||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
add_liquidity {
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
coin_desired: SubstrateAmount,
|
||||
sri_desired: SubstrateAmount,
|
||||
coin_min: SubstrateAmount,
|
||||
|
@ -18,7 +18,7 @@ pub enum Call {
|
|||
mint_to: SeraiAddress,
|
||||
},
|
||||
remove_liquidity {
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
lp_token_burn: SubstrateAmount,
|
||||
coin_min_receive: SubstrateAmount,
|
||||
sri_min_receive: SubstrateAmount,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use serai_primitives::NetworkId;
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
|
||||
#[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 {
|
||||
EconomicSecurityReached { network: NetworkId },
|
||||
EconomicSecurityReached { network: ExternalNetworkId },
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use primitives::*;
|
|||
#[derive(Clone, PartialEq, Eq, Debug, scale::Encode, scale::Decode, scale_info::TypeInfo)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Call {
|
||||
remove_coin_liquidity { balance: Balance },
|
||||
remove_coin_liquidity { balance: ExternalBalance },
|
||||
oraclize_values { values: Values, signature: Signature },
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,7 @@ pub enum Call {
|
|||
#[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: Balance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: Balance },
|
||||
GenesisLiquidityAddedToPool { coin1: Balance, coin2: Balance },
|
||||
EconomicSecurityReached { network: NetworkId },
|
||||
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ pub enum Call {
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
|
||||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Event {
|
||||
Batch { network: NetworkId, id: u32, block: BlockHash, instructions_hash: [u8; 32] },
|
||||
InstructionFailure { network: NetworkId, id: u32, index: u32 },
|
||||
Halt { network: NetworkId },
|
||||
Batch { network: ExternalNetworkId, id: u32, block: BlockHash, instructions_hash: [u8; 32] },
|
||||
InstructionFailure { network: ExternalNetworkId, id: u32, index: u32 },
|
||||
Halt { network: ExternalNetworkId },
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ use serai_validator_sets_primitives::*;
|
|||
#[cfg_attr(all(feature = "std", feature = "serde"), derive(serde::Deserialize))]
|
||||
pub enum Call {
|
||||
set_keys {
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
removed_participants: BoundedVec<SeraiAddress, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
},
|
||||
report_slashes {
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
slashes: BoundedVec<(SeraiAddress, u32), ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
signature: Signature,
|
||||
},
|
||||
|
@ -47,7 +47,7 @@ pub enum Event {
|
|||
removed: SeraiAddress,
|
||||
},
|
||||
KeyGen {
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
key_pair: KeyPair,
|
||||
},
|
||||
AcceptedHandover {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use sp_core::bounded_vec::BoundedVec;
|
||||
use serai_abi::primitives::{SeraiAddress, Amount, Coin};
|
||||
use serai_abi::primitives::{Amount, Coin, ExternalCoin, SeraiAddress};
|
||||
|
||||
use crate::{SeraiError, TemporalSerai};
|
||||
|
||||
|
@ -20,7 +20,7 @@ impl<'a> SeraiDex<'a> {
|
|||
}
|
||||
|
||||
pub fn add_liquidity(
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
coin_amount: Amount,
|
||||
sri_amount: Amount,
|
||||
min_coin_amount: Amount,
|
||||
|
@ -61,11 +61,14 @@ impl<'a> SeraiDex<'a> {
|
|||
}
|
||||
|
||||
/// Returns the reserves of `coin:SRI` pool.
|
||||
pub async fn get_reserves(&self, coin: Coin) -> Result<Option<(Amount, Amount)>, SeraiError> {
|
||||
self.0.runtime_api("DexApi_get_reserves", (coin, Coin::Serai)).await
|
||||
pub async fn get_reserves(
|
||||
&self,
|
||||
coin: ExternalCoin,
|
||||
) -> Result<Option<(Amount, Amount)>, SeraiError> {
|
||||
self.0.runtime_api("DexApi_get_reserves", (Coin::from(coin), Coin::Serai)).await
|
||||
}
|
||||
|
||||
pub async fn oracle_value(&self, coin: Coin) -> Result<Option<Amount>, SeraiError> {
|
||||
pub async fn oracle_value(&self, coin: ExternalCoin) -> Result<Option<Amount>, SeraiError> {
|
||||
self.0.storage(PALLET, "SecurityOracleValue", coin).await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ impl<'a> SeraiGenesisLiquidity<'a> {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn remove_coin_liquidity(balance: Balance) -> serai_abi::Call {
|
||||
pub fn remove_coin_liquidity(balance: ExternalBalance) -> serai_abi::Call {
|
||||
serai_abi::Call::GenesisLiquidity(serai_abi::genesis_liquidity::Call::remove_coin_liquidity {
|
||||
balance,
|
||||
})
|
||||
|
@ -44,7 +44,7 @@ impl<'a> SeraiGenesisLiquidity<'a> {
|
|||
pub async fn liquidity(
|
||||
&self,
|
||||
address: &SeraiAddress,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
) -> Result<LiquidityAmount, SeraiError> {
|
||||
Ok(
|
||||
self
|
||||
|
@ -59,7 +59,7 @@ impl<'a> SeraiGenesisLiquidity<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn supply(&self, coin: Coin) -> Result<LiquidityAmount, SeraiError> {
|
||||
pub async fn supply(&self, coin: ExternalCoin) -> Result<LiquidityAmount, SeraiError> {
|
||||
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or(LiquidityAmount::zero()))
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ pub use serai_abi::in_instructions::primitives;
|
|||
use primitives::SignedBatch;
|
||||
|
||||
use crate::{
|
||||
primitives::{BlockHash, NetworkId},
|
||||
primitives::{BlockHash, ExternalNetworkId},
|
||||
Transaction, SeraiError, Serai, TemporalSerai,
|
||||
};
|
||||
|
||||
|
@ -15,14 +15,14 @@ pub struct SeraiInInstructions<'a>(pub(crate) &'a TemporalSerai<'a>);
|
|||
impl<'a> SeraiInInstructions<'a> {
|
||||
pub async fn latest_block_for_network(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<Option<BlockHash>, SeraiError> {
|
||||
self.0.storage(PALLET, "LatestNetworkBlock", network).await
|
||||
}
|
||||
|
||||
pub async fn last_batch_for_network(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<Option<u32>, SeraiError> {
|
||||
self.0.storage(PALLET, "LastBatch", network).await
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use scale::Encode;
|
||||
|
||||
use serai_abi::primitives::{SeraiAddress, Amount, Coin, Balance};
|
||||
use serai_abi::primitives::{Amount, ExternalBalance, ExternalCoin, SeraiAddress};
|
||||
|
||||
use crate::{TemporalSerai, SeraiError};
|
||||
|
||||
|
@ -9,13 +9,13 @@ const PALLET: &str = "LiquidityTokens";
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct SeraiLiquidityTokens<'a>(pub(crate) &'a TemporalSerai<'a>);
|
||||
impl<'a> SeraiLiquidityTokens<'a> {
|
||||
pub async fn token_supply(&self, coin: Coin) -> Result<Amount, SeraiError> {
|
||||
pub async fn token_supply(&self, coin: ExternalCoin) -> Result<Amount, SeraiError> {
|
||||
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or(Amount(0)))
|
||||
}
|
||||
|
||||
pub async fn token_balance(
|
||||
&self,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
address: SeraiAddress,
|
||||
) -> Result<Amount, SeraiError> {
|
||||
Ok(
|
||||
|
@ -31,11 +31,16 @@ impl<'a> SeraiLiquidityTokens<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn transfer(to: SeraiAddress, balance: Balance) -> serai_abi::Call {
|
||||
serai_abi::Call::LiquidityTokens(serai_abi::liquidity_tokens::Call::transfer { to, balance })
|
||||
pub fn transfer(to: SeraiAddress, balance: ExternalBalance) -> serai_abi::Call {
|
||||
serai_abi::Call::LiquidityTokens(serai_abi::liquidity_tokens::Call::transfer {
|
||||
to,
|
||||
balance: balance.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn burn(balance: Balance) -> serai_abi::Call {
|
||||
serai_abi::Call::LiquidityTokens(serai_abi::liquidity_tokens::Call::burn { balance })
|
||||
pub fn burn(balance: ExternalBalance) -> serai_abi::Call {
|
||||
serai_abi::Call::LiquidityTokens(serai_abi::liquidity_tokens::Call::burn {
|
||||
balance: balance.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ use scale::Encode;
|
|||
|
||||
use sp_core::sr25519::{Public, Signature};
|
||||
|
||||
use serai_abi::primitives::Amount;
|
||||
use serai_abi::{primitives::Amount, validator_sets::primitives::ExternalValidatorSet};
|
||||
pub use serai_abi::validator_sets::primitives;
|
||||
use primitives::{Session, ValidatorSet, KeyPair};
|
||||
use primitives::{Session, KeyPair};
|
||||
|
||||
use crate::{
|
||||
primitives::{NetworkId, SeraiAddress},
|
||||
primitives::{NetworkId, ExternalNetworkId, SeraiAddress},
|
||||
Transaction, Serai, TemporalSerai, SeraiError,
|
||||
};
|
||||
|
||||
|
@ -167,13 +167,13 @@ impl<'a> SeraiValidatorSets<'a> {
|
|||
}
|
||||
|
||||
// TODO: Store these separately since we almost never need both at once?
|
||||
pub async fn keys(&self, set: ValidatorSet) -> Result<Option<KeyPair>, SeraiError> {
|
||||
pub async fn keys(&self, set: ExternalValidatorSet) -> Result<Option<KeyPair>, SeraiError> {
|
||||
self.0.storage(PALLET, "Keys", (sp_core::hashing::twox_64(&set.encode()), set)).await
|
||||
}
|
||||
|
||||
pub async fn key_pending_slash_report(
|
||||
&self,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<Option<Public>, SeraiError> {
|
||||
self.0.storage(PALLET, "PendingSlashReport", network).await
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ impl<'a> SeraiValidatorSets<'a> {
|
|||
}
|
||||
|
||||
pub fn set_keys(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
removed_participants: sp_runtime::BoundedVec<
|
||||
SeraiAddress,
|
||||
sp_core::ConstU32<{ primitives::MAX_KEY_SHARES_PER_SET / 3 }>,
|
||||
|
@ -212,7 +212,7 @@ impl<'a> SeraiValidatorSets<'a> {
|
|||
}
|
||||
|
||||
pub fn report_slashes(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
slashes: sp_runtime::BoundedVec<
|
||||
(SeraiAddress, u32),
|
||||
sp_core::ConstU32<{ primitives::MAX_KEY_SHARES_PER_SET / 3 }>,
|
||||
|
|
|
@ -8,7 +8,7 @@ use blake2::{
|
|||
use scale::Encode;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Amount, NetworkId, Coin, Balance, BlockHash, SeraiAddress},
|
||||
primitives::{Amount, BlockHash, ExternalBalance, ExternalCoin, SeraiAddress},
|
||||
in_instructions::{
|
||||
primitives::{InInstruction, InInstructionWithBalance, Batch},
|
||||
InInstructionsEvent,
|
||||
|
@ -22,18 +22,17 @@ use common::in_instructions::provide_batch;
|
|||
|
||||
serai_test!(
|
||||
publish_batch: (|serai: Serai| async move {
|
||||
let network = NetworkId::Bitcoin;
|
||||
let id = 0;
|
||||
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
|
||||
let mut address = SeraiAddress::new([0; 32]);
|
||||
OsRng.fill_bytes(&mut address.0);
|
||||
|
||||
let coin = Coin::Bitcoin;
|
||||
let coin = ExternalCoin::Bitcoin;
|
||||
let network = coin.network();
|
||||
let amount = Amount(OsRng.next_u64().saturating_add(1));
|
||||
let balance = Balance { coin, amount };
|
||||
let balance = ExternalBalance { coin, amount };
|
||||
|
||||
let batch = Batch {
|
||||
network,
|
||||
|
@ -67,9 +66,9 @@ serai_test!(
|
|||
let serai = serai.coins();
|
||||
assert_eq!(
|
||||
serai.mint_events().await.unwrap(),
|
||||
vec![CoinsEvent::Mint { to: address, balance }]
|
||||
vec![CoinsEvent::Mint { to: address, balance: balance.into() }]
|
||||
);
|
||||
assert_eq!(serai.coin_supply(coin).await.unwrap(), amount);
|
||||
assert_eq!(serai.coin_balance(coin, address).await.unwrap(), amount);
|
||||
assert_eq!(serai.coin_supply(coin.into()).await.unwrap(), amount);
|
||||
assert_eq!(serai.coin_balance(coin.into(), address).await.unwrap(), amount);
|
||||
})
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ use sp_core::Pair;
|
|||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
Amount, NetworkId, Coin, Balance, BlockHash, SeraiAddress, Data, ExternalAddress,
|
||||
Amount, ExternalCoin, ExternalBalance, BlockHash, SeraiAddress, Data, ExternalAddress,
|
||||
insecure_pair_from_name,
|
||||
},
|
||||
in_instructions::{
|
||||
|
@ -28,9 +28,7 @@ use common::{tx::publish_tx, in_instructions::provide_batch};
|
|||
|
||||
serai_test!(
|
||||
burn: (|serai: Serai| async move {
|
||||
let network = NetworkId::Bitcoin;
|
||||
let id = 0;
|
||||
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
|
||||
|
@ -38,9 +36,10 @@ serai_test!(
|
|||
let public = pair.public();
|
||||
let address = SeraiAddress::from(public);
|
||||
|
||||
let coin = Coin::Bitcoin;
|
||||
let coin = ExternalCoin::Bitcoin;
|
||||
let network = coin.network();
|
||||
let amount = Amount(OsRng.next_u64().saturating_add(1));
|
||||
let balance = Balance { coin, amount };
|
||||
let balance = ExternalBalance { coin, amount };
|
||||
|
||||
let batch = Batch {
|
||||
network,
|
||||
|
@ -69,10 +68,10 @@ serai_test!(
|
|||
|
||||
assert_eq!(
|
||||
serai.coins().mint_events().await.unwrap(),
|
||||
vec![CoinsEvent::Mint { to: address, balance }]
|
||||
vec![CoinsEvent::Mint { to: address, balance: balance.into() }]
|
||||
);
|
||||
assert_eq!(serai.coins().coin_supply(coin).await.unwrap(), amount);
|
||||
assert_eq!(serai.coins().coin_balance(coin, address).await.unwrap(), amount);
|
||||
assert_eq!(serai.coins().coin_supply(coin.into()).await.unwrap(), amount);
|
||||
assert_eq!(serai.coins().coin_balance(coin.into(), address).await.unwrap(), amount);
|
||||
|
||||
// Now burn it
|
||||
let mut rand_bytes = vec![0; 32];
|
||||
|
@ -99,7 +98,7 @@ serai_test!(
|
|||
let serai = serai.coins();
|
||||
let events = serai.burn_with_instruction_events().await.unwrap();
|
||||
assert_eq!(events, vec![CoinsEvent::BurnWithInstruction { from: address, instruction }]);
|
||||
assert_eq!(serai.coin_supply(coin).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_balance(coin, address).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_supply(coin.into()).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_balance(coin.into(), address).await.unwrap(), Amount(0));
|
||||
})
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use serai_abi::primitives::{Coin, Amount};
|
||||
use serai_abi::primitives::{Amount, Coin, ExternalCoin};
|
||||
|
||||
use serai_client::{Serai, SeraiDex};
|
||||
use sp_core::{sr25519::Pair, Pair as PairTrait};
|
||||
|
@ -8,7 +8,7 @@ use crate::common::tx::publish_tx;
|
|||
#[allow(dead_code)]
|
||||
pub async fn add_liquidity(
|
||||
serai: &Serai,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
coin_amount: Amount,
|
||||
sri_amount: Amount,
|
||||
nonce: u32,
|
||||
|
|
|
@ -11,11 +11,12 @@ use sp_core::{sr25519::Signature, Pair as PairTrait};
|
|||
|
||||
use serai_abi::{
|
||||
genesis_liquidity::primitives::{oraclize_values_message, Values},
|
||||
validator_sets::primitives::{musig_context, Session, ValidatorSet},
|
||||
in_instructions::primitives::{InInstruction, InInstructionWithBalance, Batch},
|
||||
in_instructions::primitives::{Batch, InInstruction, InInstructionWithBalance},
|
||||
primitives::{
|
||||
Amount, NetworkId, Coin, Balance, BlockHash, SeraiAddress, insecure_pair_from_name,
|
||||
insecure_pair_from_name, Amount, ExternalBalance, BlockHash, ExternalCoin, ExternalNetworkId,
|
||||
NetworkId, SeraiAddress, EXTERNAL_COINS,
|
||||
},
|
||||
validator_sets::primitives::{musig_context, Session, ValidatorSet},
|
||||
};
|
||||
|
||||
use serai_client::{Serai, SeraiGenesisLiquidity};
|
||||
|
@ -25,12 +26,11 @@ use crate::common::{in_instructions::provide_batch, tx::publish_tx};
|
|||
#[allow(dead_code)]
|
||||
pub async fn set_up_genesis(
|
||||
serai: &Serai,
|
||||
coins: &[Coin],
|
||||
values: &HashMap<Coin, u64>,
|
||||
) -> (HashMap<Coin, Vec<(SeraiAddress, Amount)>>, HashMap<NetworkId, u32>) {
|
||||
values: &HashMap<ExternalCoin, u64>,
|
||||
) -> (HashMap<ExternalCoin, Vec<(SeraiAddress, Amount)>>, HashMap<ExternalNetworkId, u32>) {
|
||||
// make accounts with amounts
|
||||
let mut accounts = HashMap::new();
|
||||
for coin in coins {
|
||||
for coin in EXTERNAL_COINS {
|
||||
// make 5 accounts per coin
|
||||
let mut values = vec![];
|
||||
for _ in 0 .. 5 {
|
||||
|
@ -38,18 +38,18 @@ pub async fn set_up_genesis(
|
|||
OsRng.fill_bytes(&mut address.0);
|
||||
values.push((address, Amount(OsRng.next_u64() % 10u64.pow(coin.decimals()))));
|
||||
}
|
||||
accounts.insert(*coin, values);
|
||||
accounts.insert(coin, values);
|
||||
}
|
||||
|
||||
// send a batch per coin
|
||||
let mut batch_ids: HashMap<NetworkId, u32> = HashMap::new();
|
||||
for coin in coins {
|
||||
let mut batch_ids: HashMap<ExternalNetworkId, u32> = HashMap::new();
|
||||
for coin in EXTERNAL_COINS {
|
||||
// set up instructions
|
||||
let instructions = accounts[coin]
|
||||
let instructions = accounts[&coin]
|
||||
.iter()
|
||||
.map(|(addr, amount)| InInstructionWithBalance {
|
||||
instruction: InInstruction::GenesisLiquidity(*addr),
|
||||
balance: Balance { coin: *coin, amount: *amount },
|
||||
balance: ExternalBalance { coin, amount: *amount },
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -73,8 +73,11 @@ pub async fn set_up_genesis(
|
|||
// set values relative to each other. We can do that without checking for genesis period blocks
|
||||
// since we are running in test(fast-epoch) mode.
|
||||
// TODO: Random values here
|
||||
let values =
|
||||
Values { monero: values[&Coin::Monero], ether: values[&Coin::Ether], dai: values[&Coin::Dai] };
|
||||
let values = Values {
|
||||
monero: values[&ExternalCoin::Monero],
|
||||
ether: values[&ExternalCoin::Ether],
|
||||
dai: values[&ExternalCoin::Dai],
|
||||
};
|
||||
set_values(serai, &values).await;
|
||||
|
||||
(accounts, batch_ids)
|
||||
|
|
|
@ -9,8 +9,8 @@ use scale::Encode;
|
|||
use sp_core::Pair;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{insecure_pair_from_name, BlockHash, NetworkId, Balance, SeraiAddress},
|
||||
validator_sets::primitives::{ValidatorSet, KeyPair},
|
||||
primitives::{insecure_pair_from_name, BlockHash, ExternalBalance, SeraiAddress},
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair},
|
||||
in_instructions::{
|
||||
primitives::{Batch, SignedBatch, batch_message, InInstruction, InInstructionWithBalance},
|
||||
InInstructionsEvent,
|
||||
|
@ -23,8 +23,8 @@ use crate::common::{tx::publish_tx, validator_sets::set_keys};
|
|||
#[allow(dead_code)]
|
||||
pub async fn provide_batch(serai: &Serai, batch: Batch) -> [u8; 32] {
|
||||
let serai_latest = serai.as_of_latest_finalized_block().await.unwrap();
|
||||
let session = serai_latest.validator_sets().session(batch.network).await.unwrap().unwrap();
|
||||
let set = ValidatorSet { session, network: batch.network };
|
||||
let session = serai_latest.validator_sets().session(batch.network.into()).await.unwrap().unwrap();
|
||||
let set = ExternalValidatorSet { session, network: batch.network };
|
||||
|
||||
let pair = insecure_pair_from_name(&format!("ValidatorSet {set:?}"));
|
||||
let keys = if let Some(keys) = serai_latest.validator_sets().keys(set).await.unwrap() {
|
||||
|
@ -65,8 +65,7 @@ pub async fn provide_batch(serai: &Serai, batch: Batch) -> [u8; 32] {
|
|||
#[allow(dead_code)]
|
||||
pub async fn mint_coin(
|
||||
serai: &Serai,
|
||||
balance: Balance,
|
||||
network: NetworkId,
|
||||
balance: ExternalBalance,
|
||||
batch_id: u32,
|
||||
address: SeraiAddress,
|
||||
) -> [u8; 32] {
|
||||
|
@ -74,7 +73,7 @@ pub async fn mint_coin(
|
|||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
|
||||
let batch = Batch {
|
||||
network,
|
||||
network: balance.coin.network(),
|
||||
id: batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
|
|
|
@ -15,7 +15,7 @@ use schnorrkel::Schnorrkel;
|
|||
|
||||
use serai_client::{
|
||||
validator_sets::{
|
||||
primitives::{ValidatorSet, KeyPair, musig_context, set_keys_message},
|
||||
primitives::{ExternalValidatorSet, KeyPair, musig_context, set_keys_message},
|
||||
ValidatorSetsEvent,
|
||||
},
|
||||
Amount, Serai, SeraiValidatorSets,
|
||||
|
@ -26,7 +26,7 @@ use crate::common::tx::publish_tx;
|
|||
#[allow(dead_code)]
|
||||
pub async fn set_keys(
|
||||
serai: &Serai,
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
key_pair: KeyPair,
|
||||
pairs: &[Pair],
|
||||
) -> [u8; 32] {
|
||||
|
@ -46,7 +46,8 @@ pub async fn set_keys(
|
|||
assert_eq!(Ristretto::generator() * secret_key, pub_keys[i]);
|
||||
|
||||
threshold_keys.push(
|
||||
musig::<Ristretto>(&musig_context(set), &Zeroizing::new(secret_key), &pub_keys).unwrap(),
|
||||
musig::<Ristretto>(&musig_context(set.into()), &Zeroizing::new(secret_key), &pub_keys)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ use serai_abi::in_instructions::primitives::DexCall;
|
|||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
Amount, NetworkId, Coin, Balance, BlockHash, insecure_pair_from_name, ExternalAddress,
|
||||
SeraiAddress,
|
||||
Amount, Coin, Balance, BlockHash, insecure_pair_from_name, ExternalAddress, SeraiAddress,
|
||||
ExternalCoin, ExternalBalance,
|
||||
},
|
||||
in_instructions::primitives::{
|
||||
InInstruction, InInstructionWithBalance, Batch, IN_INSTRUCTION_EXECUTOR, OutAddress,
|
||||
|
@ -28,15 +28,14 @@ use common::{
|
|||
// TODO: Check Transfer events
|
||||
serai_test!(
|
||||
add_liquidity: (|serai: Serai| async move {
|
||||
let coin = Coin::Monero;
|
||||
let coin = ExternalCoin::Monero;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint sriXMR in the account so that we can add liq.
|
||||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
ExternalBalance { coin, amount: Amount(100_000_000_000_000) },
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -61,7 +60,7 @@ serai_test!(
|
|||
vec![DexEvent::LiquidityAdded {
|
||||
who: pair.public().into(),
|
||||
mint_to: pair.public().into(),
|
||||
pool_id: Coin::Monero,
|
||||
pool_id: coin,
|
||||
coin_amount: coin_amount.0,
|
||||
sri_amount: sri_amount.0,
|
||||
lp_token_minted: 49_999999990000
|
||||
|
@ -71,15 +70,14 @@ serai_test!(
|
|||
|
||||
// Tests coin -> SRI and SRI -> coin swaps.
|
||||
swap_coin_to_sri: (|serai: Serai| async move {
|
||||
let coin = Coin::Ether;
|
||||
let coin = ExternalCoin::Ether;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint sriXMR in the account so that we can add liq.
|
||||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
ExternalBalance { coin, amount: Amount(100_000_000_000_000) },
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -96,14 +94,21 @@ serai_test!(
|
|||
|
||||
// now that we have our liquid pool, swap some coin to SRI.
|
||||
let mut amount_in = Amount(25_000_000_000_000);
|
||||
let mut block = common_swap(&serai, coin, Coin::Serai, amount_in, Amount(1), 1, pair.clone())
|
||||
let mut block = common_swap(
|
||||
&serai,
|
||||
coin.into(),
|
||||
Coin::Serai,
|
||||
amount_in,
|
||||
Amount(1),
|
||||
1,
|
||||
pair.clone())
|
||||
.await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let mut path = BoundedVec::try_from(vec![coin, Coin::Serai]).unwrap();
|
||||
let mut path = BoundedVec::try_from(vec![coin.into(), Coin::Serai]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
@ -117,13 +122,21 @@ serai_test!(
|
|||
|
||||
// now swap some SRI to coin
|
||||
amount_in = Amount(10_000_000_000_000);
|
||||
block = common_swap(&serai, Coin::Serai, coin, amount_in, Amount(1), 2, pair.clone()).await;
|
||||
block = common_swap(
|
||||
&serai,
|
||||
Coin::Serai,
|
||||
coin.into(),
|
||||
amount_in,
|
||||
Amount(1),
|
||||
2,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
path = BoundedVec::try_from(vec![Coin::Serai, coin]).unwrap();
|
||||
path = BoundedVec::try_from(vec![Coin::Serai, coin.into()]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
@ -137,23 +150,21 @@ serai_test!(
|
|||
})
|
||||
|
||||
swap_coin_to_coin: (|serai: Serai| async move {
|
||||
let coin1 = Coin::Monero;
|
||||
let coin2 = Coin::Dai;
|
||||
let coin1 = ExternalCoin::Monero;
|
||||
let coin2 = ExternalCoin::Dai;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
|
||||
// mint coins
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
ExternalBalance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
.await;
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
ExternalBalance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
0,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -177,13 +188,21 @@ serai_test!(
|
|||
|
||||
// swap coin1 -> coin2
|
||||
let amount_in = Amount(25_000_000_000_000);
|
||||
let block = common_swap(&serai, coin1, coin2, amount_in, Amount(1), 2, pair.clone()).await;
|
||||
let block = common_swap(
|
||||
&serai,
|
||||
coin1.into(),
|
||||
coin2.into(),
|
||||
amount_in,
|
||||
Amount(1),
|
||||
2,
|
||||
pair.clone()
|
||||
).await;
|
||||
|
||||
// get only the swap events
|
||||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![coin1.into(), Coin::Serai, coin2.into()]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
@ -197,7 +216,7 @@ serai_test!(
|
|||
})
|
||||
|
||||
add_liquidity_in_instructions: (|serai: Serai| async move {
|
||||
let coin = Coin::Bitcoin;
|
||||
let coin = ExternalCoin::Bitcoin;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
let mut batch_id = 0;
|
||||
|
||||
|
@ -205,8 +224,7 @@ serai_test!(
|
|||
// Ferdie account is already pre-funded with SRI.
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Bitcoin,
|
||||
ExternalBalance { coin, amount: Amount(100_000_000_000_000) },
|
||||
batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -227,12 +245,12 @@ serai_test!(
|
|||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Bitcoin,
|
||||
network: coin.network(),
|
||||
id: batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::SwapAndAddLiquidity(pair.public().into())),
|
||||
balance: Balance { coin: Coin::Bitcoin, amount: Amount(20_000_000_000_000) },
|
||||
balance: ExternalBalance { coin, amount: Amount(20_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -244,7 +262,7 @@ serai_test!(
|
|||
vec![DexEvent::LiquidityAdded {
|
||||
who: IN_INSTRUCTION_EXECUTOR,
|
||||
mint_to: pair.public().into(),
|
||||
pool_id: Coin::Bitcoin,
|
||||
pool_id: coin,
|
||||
coin_amount: 10_000_000_000_000, // half of sent amount
|
||||
sri_amount: 111_333_778_668,
|
||||
lp_token_minted: 1_054_092_553_383
|
||||
|
@ -253,8 +271,8 @@ serai_test!(
|
|||
})
|
||||
|
||||
swap_in_instructions: (|serai: Serai| async move {
|
||||
let coin1 = Coin::Monero;
|
||||
let coin2 = Coin::Ether;
|
||||
let coin1 = ExternalCoin::Monero;
|
||||
let coin2 = ExternalCoin::Ether;
|
||||
let pair = insecure_pair_from_name("Ferdie");
|
||||
let mut coin1_batch_id = 0;
|
||||
let mut coin2_batch_id = 0;
|
||||
|
@ -262,8 +280,7 @@ serai_test!(
|
|||
// mint coins
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin1, amount: Amount(10_000_000_000_000_000) },
|
||||
NetworkId::Monero,
|
||||
ExternalBalance { coin: coin1, amount: Amount(10_000_000_000_000_000) },
|
||||
coin1_batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -271,8 +288,7 @@ serai_test!(
|
|||
coin1_batch_id += 1;
|
||||
mint_coin(
|
||||
&serai,
|
||||
Balance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
NetworkId::Ethereum,
|
||||
ExternalBalance { coin: coin2, amount: Amount(100_000_000_000_000) },
|
||||
coin2_batch_id,
|
||||
pair.clone().public().into(),
|
||||
)
|
||||
|
@ -305,18 +321,18 @@ serai_test!(
|
|||
let out_address = OutAddress::External(ExternalAddress::new(rand_bytes.clone()).unwrap());
|
||||
|
||||
// amount is the min out amount
|
||||
let out_balance = Balance { coin: coin2, amount: Amount(1) };
|
||||
let out_balance = Balance { coin: coin2.into(), amount: Amount(1) };
|
||||
|
||||
// now that we have our pools, we can try to swap
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Monero,
|
||||
network: coin1.network(),
|
||||
id: coin1_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address)),
|
||||
balance: Balance { coin: coin1, amount: Amount(200_000_000_000_000) },
|
||||
balance: ExternalBalance { coin: coin1, amount: Amount(200_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -325,7 +341,7 @@ serai_test!(
|
|||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai, coin2]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![coin1.into(), Coin::Serai, coin2.into()]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
@ -345,18 +361,18 @@ serai_test!(
|
|||
OutAddress::Serai(SeraiAddress::new(rand_bytes.clone().try_into().unwrap()));
|
||||
|
||||
// amount is the min out amount
|
||||
let out_balance = Balance { coin: coin1, amount: Amount(1) };
|
||||
let out_balance = Balance { coin: coin1.into(), amount: Amount(1) };
|
||||
|
||||
// now that we have our pools, we can try to swap
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Ethereum,
|
||||
network: coin2.network(),
|
||||
id: coin2_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address.clone())),
|
||||
balance: Balance { coin: coin2, amount: Amount(200_000_000_000) },
|
||||
balance: ExternalBalance { coin: coin2, amount: Amount(200_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -364,7 +380,7 @@ serai_test!(
|
|||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin2, Coin::Serai, coin1]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![coin2.into(), Coin::Serai, coin1.into()]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
@ -389,12 +405,12 @@ serai_test!(
|
|||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch {
|
||||
network: NetworkId::Monero,
|
||||
network: coin1.network(),
|
||||
id: coin1_batch_id,
|
||||
block: block_hash,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction: InInstruction::Dex(DexCall::Swap(out_balance, out_address.clone())),
|
||||
balance: Balance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
balance: ExternalBalance { coin: coin1, amount: Amount(100_000_000_000_000) },
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -402,7 +418,7 @@ serai_test!(
|
|||
let mut events = serai.as_of(block).dex().events().await.unwrap();
|
||||
events.retain(|e| matches!(e, DexEvent::SwapExecuted { .. }));
|
||||
|
||||
let path = BoundedVec::try_from(vec![coin1, Coin::Serai]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![coin1.into(), Coin::Serai]).unwrap();
|
||||
assert_eq!(
|
||||
events,
|
||||
vec![DexEvent::SwapExecuted {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use serai_client::{primitives::NetworkId, Serai};
|
||||
use serai_client::{primitives::ExternalNetworkId, Serai};
|
||||
|
||||
#[tokio::test]
|
||||
async fn dht() {
|
||||
|
@ -44,7 +44,7 @@ async fn dht() {
|
|||
assert!(!Serai::new(serai_rpc.clone())
|
||||
.await
|
||||
.unwrap()
|
||||
.p2p_validators(NetworkId::Bitcoin)
|
||||
.p2p_validators(ExternalNetworkId::Bitcoin.into())
|
||||
.await
|
||||
.unwrap()
|
||||
.is_empty());
|
||||
|
|
|
@ -7,16 +7,13 @@ use serai_abi::{
|
|||
emissions::primitives::{INITIAL_REWARD_PER_BLOCK, SECURE_BY},
|
||||
in_instructions::primitives::Batch,
|
||||
primitives::{
|
||||
BlockHash, Coin, COINS, FAST_EPOCH_DURATION, FAST_EPOCH_INITIAL_PERIOD, NETWORKS,
|
||||
TARGET_BLOCK_TIME,
|
||||
BlockHash, ExternalBalance, ExternalCoin, ExternalNetworkId, EXTERNAL_NETWORKS,
|
||||
FAST_EPOCH_DURATION, FAST_EPOCH_INITIAL_PERIOD, NETWORKS, TARGET_BLOCK_TIME, Amount, NetworkId,
|
||||
},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Amount, NetworkId, Balance},
|
||||
Serai,
|
||||
};
|
||||
use serai_client::Serai;
|
||||
|
||||
mod common;
|
||||
use common::{genesis_liquidity::set_up_genesis, in_instructions::provide_batch};
|
||||
|
@ -27,31 +24,32 @@ serai_test_fast_epoch!(
|
|||
})
|
||||
);
|
||||
|
||||
async fn send_batches(serai: &Serai, ids: &mut HashMap<NetworkId, u32>) {
|
||||
for network in NETWORKS {
|
||||
if network != NetworkId::Serai {
|
||||
// set up batch id
|
||||
ids
|
||||
.entry(network)
|
||||
.and_modify(|v| {
|
||||
*v += 1;
|
||||
})
|
||||
.or_insert(0);
|
||||
async fn send_batches(serai: &Serai, ids: &mut HashMap<ExternalNetworkId, u32>) {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
// set up batch id
|
||||
ids
|
||||
.entry(network)
|
||||
.and_modify(|v| {
|
||||
*v += 1;
|
||||
})
|
||||
.or_insert(0);
|
||||
|
||||
// set up block hash
|
||||
let mut block = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block.0);
|
||||
// set up block hash
|
||||
let mut block = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block.0);
|
||||
|
||||
provide_batch(serai, Batch { network, id: ids[&network], block, instructions: vec![] }).await;
|
||||
}
|
||||
provide_batch(serai, Batch { network, id: ids[&network], block, instructions: vec![] }).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn test_emissions(serai: Serai) {
|
||||
// set up the genesis
|
||||
let coins = COINS.into_iter().filter(|c| *c != Coin::native()).collect::<Vec<_>>();
|
||||
let values = HashMap::from([(Coin::Monero, 184100), (Coin::Ether, 4785000), (Coin::Dai, 1500)]);
|
||||
let (_, mut batch_ids) = set_up_genesis(&serai, &coins, &values).await;
|
||||
let values = HashMap::from([
|
||||
(ExternalCoin::Monero, 184100),
|
||||
(ExternalCoin::Ether, 4785000),
|
||||
(ExternalCoin::Dai, 1500),
|
||||
]);
|
||||
let (_, mut batch_ids) = set_up_genesis(&serai, &values).await;
|
||||
|
||||
// wait until genesis is complete
|
||||
let mut genesis_complete_block = None;
|
||||
|
@ -144,7 +142,7 @@ async fn test_emissions(serai: Serai) {
|
|||
}
|
||||
|
||||
/// Returns the required stake in terms SRI for a given `Balance`.
|
||||
async fn required_stake(serai: &TemporalSerai<'_>, balance: Balance) -> u64 {
|
||||
async fn required_stake(serai: &TemporalSerai<'_>, balance: ExternalBalance) -> u64 {
|
||||
// This is inclusive to an increase in accuracy
|
||||
let sri_per_coin = serai.dex().oracle_value(balance.coin).await.unwrap().unwrap_or(Amount(0));
|
||||
|
||||
|
@ -208,18 +206,14 @@ async fn get_distances(
|
|||
// we can check the supply to see how much coin hence liability we have.
|
||||
let mut distances: HashMap<NetworkId, u64> = HashMap::new();
|
||||
let mut total_distance = 0;
|
||||
for n in NETWORKS {
|
||||
if n == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for n in EXTERNAL_NETWORKS {
|
||||
let mut required = 0;
|
||||
for c in n.coins() {
|
||||
let amount = serai.coins().coin_supply(*c).await.unwrap();
|
||||
required += required_stake(serai, Balance { coin: *c, amount }).await;
|
||||
let amount = serai.coins().coin_supply(c.into()).await.unwrap();
|
||||
required += required_stake(serai, ExternalBalance { coin: c, amount }).await;
|
||||
}
|
||||
|
||||
let mut current = *current_stake.get(&n).unwrap();
|
||||
let mut current = *current_stake.get(&n.into()).unwrap();
|
||||
if current > required {
|
||||
current = required;
|
||||
}
|
||||
|
@ -227,7 +221,7 @@ async fn get_distances(
|
|||
let distance = required - current;
|
||||
total_distance += distance;
|
||||
|
||||
distances.insert(n, distance);
|
||||
distances.insert(n.into(), distance);
|
||||
}
|
||||
|
||||
// add serai network portion(20%)
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{time::Duration, collections::HashMap};
|
|||
|
||||
use serai_client::Serai;
|
||||
|
||||
use serai_abi::primitives::{Coin, COINS, Amount, GENESIS_SRI};
|
||||
use serai_abi::primitives::{Amount, Coin, ExternalCoin, COINS, EXTERNAL_COINS, GENESIS_SRI};
|
||||
|
||||
use serai_client::genesis_liquidity::primitives::{
|
||||
GENESIS_LIQUIDITY_ACCOUNT, INITIAL_GENESIS_LP_SHARES,
|
||||
|
@ -19,9 +19,12 @@ serai_test_fast_epoch!(
|
|||
|
||||
pub async fn test_genesis_liquidity(serai: Serai) {
|
||||
// set up the genesis
|
||||
let coins = COINS.into_iter().filter(|c| *c != Coin::native()).collect::<Vec<_>>();
|
||||
let values = HashMap::from([(Coin::Monero, 184100), (Coin::Ether, 4785000), (Coin::Dai, 1500)]);
|
||||
let (accounts, _) = set_up_genesis(&serai, &coins, &values).await;
|
||||
let values = HashMap::from([
|
||||
(ExternalCoin::Monero, 184100),
|
||||
(ExternalCoin::Ether, 4785000),
|
||||
(ExternalCoin::Dai, 1500),
|
||||
]);
|
||||
let (accounts, _) = set_up_genesis(&serai, &values).await;
|
||||
|
||||
// wait until genesis is complete
|
||||
while serai
|
||||
|
@ -55,9 +58,9 @@ pub async fn test_genesis_liquidity(serai: Serai) {
|
|||
// check pools has proper liquidity
|
||||
let mut pool_amounts = HashMap::new();
|
||||
let mut total_value = 0u128;
|
||||
for coin in coins.clone() {
|
||||
for coin in EXTERNAL_COINS {
|
||||
let total_coin = accounts[&coin].iter().fold(0u128, |acc, value| acc + u128::from(value.1 .0));
|
||||
let value = if coin != Coin::Bitcoin {
|
||||
let value = if coin != ExternalCoin::Bitcoin {
|
||||
(total_coin * u128::from(values[&coin])) / 10u128.pow(coin.decimals())
|
||||
} else {
|
||||
total_coin
|
||||
|
@ -69,8 +72,8 @@ pub async fn test_genesis_liquidity(serai: Serai) {
|
|||
|
||||
// check distributed SRI per pool
|
||||
let mut total_sri_distributed = 0u128;
|
||||
for coin in coins.clone() {
|
||||
let sri = if coin == *COINS.last().unwrap() {
|
||||
for coin in EXTERNAL_COINS {
|
||||
let sri = if coin == *EXTERNAL_COINS.last().unwrap() {
|
||||
u128::from(GENESIS_SRI).checked_sub(total_sri_distributed).unwrap()
|
||||
} else {
|
||||
(pool_amounts[&coin].1 * u128::from(GENESIS_SRI)) / total_value
|
||||
|
@ -83,7 +86,7 @@ pub async fn test_genesis_liquidity(serai: Serai) {
|
|||
}
|
||||
|
||||
// check each liquidity provider got liquidity tokens proportional to their value
|
||||
for coin in coins {
|
||||
for coin in EXTERNAL_COINS {
|
||||
let liq_supply = serai.genesis_liquidity().supply(coin).await.unwrap();
|
||||
for (acc, amount) in &accounts[&coin] {
|
||||
let acc_liq_shares = serai.genesis_liquidity().liquidity(acc, coin).await.unwrap().shares;
|
||||
|
|
|
@ -7,17 +7,18 @@ use sp_core::{
|
|||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
NETWORKS, NetworkId, BlockHash, insecure_pair_from_name, FAST_EPOCH_DURATION, TARGET_BLOCK_TIME,
|
||||
NETWORKS, NetworkId, BlockHash, insecure_pair_from_name, FAST_EPOCH_DURATION,
|
||||
TARGET_BLOCK_TIME, ExternalNetworkId, Amount,
|
||||
},
|
||||
validator_sets::{
|
||||
primitives::{Session, ValidatorSet, KeyPair},
|
||||
primitives::{Session, ValidatorSet, ExternalValidatorSet, KeyPair},
|
||||
ValidatorSetsEvent,
|
||||
},
|
||||
in_instructions::{
|
||||
primitives::{Batch, SignedBatch, batch_message},
|
||||
SeraiInInstructions,
|
||||
},
|
||||
Amount, Serai,
|
||||
Serai,
|
||||
};
|
||||
|
||||
mod common;
|
||||
|
@ -58,8 +59,8 @@ async fn get_ordered_keys(serai: &Serai, network: NetworkId, accounts: &[Pair])
|
|||
|
||||
serai_test!(
|
||||
set_keys_test: (|serai: Serai| async move {
|
||||
let network = NetworkId::Bitcoin;
|
||||
let set = ValidatorSet { session: Session(0), network };
|
||||
let network = ExternalNetworkId::Bitcoin;
|
||||
let set = ExternalValidatorSet { session: Session(0), network };
|
||||
|
||||
let pair = insecure_pair_from_name("Alice");
|
||||
let public = pair.public();
|
||||
|
@ -89,7 +90,7 @@ serai_test!(
|
|||
{
|
||||
let vs_serai = serai.as_of_latest_finalized_block().await.unwrap();
|
||||
let vs_serai = vs_serai.validator_sets();
|
||||
let participants = vs_serai.participants(set.network).await
|
||||
let participants = vs_serai.participants(set.network.into()).await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
|
@ -197,9 +198,9 @@ async fn validator_set_rotation() {
|
|||
// amounts for single key share per network
|
||||
let key_shares = HashMap::from([
|
||||
(NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
(NetworkId::Bitcoin, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
(NetworkId::Monero, Amount(100_000 * 10_u64.pow(8))),
|
||||
(NetworkId::Ethereum, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
(NetworkId::External(ExternalNetworkId::Bitcoin), Amount(1_000_000 * 10_u64.pow(8))),
|
||||
(NetworkId::External(ExternalNetworkId::Monero), Amount(100_000 * 10_u64.pow(8))),
|
||||
(NetworkId::External(ExternalNetworkId::Ethereum), Amount(1_000_000 * 10_u64.pow(8))),
|
||||
]);
|
||||
|
||||
// genesis participants per network
|
||||
|
@ -208,9 +209,9 @@ async fn validator_set_rotation() {
|
|||
accounts[.. 4].to_vec().iter().map(|pair| pair.public()).collect::<Vec<_>>();
|
||||
let mut participants = HashMap::from([
|
||||
(NetworkId::Serai, default_participants.clone()),
|
||||
(NetworkId::Bitcoin, default_participants.clone()),
|
||||
(NetworkId::Monero, default_participants.clone()),
|
||||
(NetworkId::Ethereum, default_participants),
|
||||
(NetworkId::External(ExternalNetworkId::Bitcoin), default_participants.clone()),
|
||||
(NetworkId::External(ExternalNetworkId::Monero), default_participants.clone()),
|
||||
(NetworkId::External(ExternalNetworkId::Ethereum), default_participants),
|
||||
]);
|
||||
|
||||
// test the set rotation
|
||||
|
@ -237,7 +238,8 @@ async fn validator_set_rotation() {
|
|||
|
||||
// set the keys if it is an external set
|
||||
if network != NetworkId::Serai {
|
||||
let set = ValidatorSet { session: Session(0), network };
|
||||
let set =
|
||||
ExternalValidatorSet { session: Session(0), network: network.try_into().unwrap() };
|
||||
let key_pair = get_random_key_pair();
|
||||
let pairs = get_ordered_keys(&serai, network, &accounts).await;
|
||||
set_keys(&serai, set, key_pair, &pairs).await;
|
||||
|
@ -265,7 +267,8 @@ async fn validator_set_rotation() {
|
|||
|
||||
if network != NetworkId::Serai {
|
||||
// set the keys if it is an external set
|
||||
let set = ValidatorSet { session: Session(1), network };
|
||||
let set =
|
||||
ExternalValidatorSet { session: Session(1), network: network.try_into().unwrap() };
|
||||
|
||||
// we need the whole substrate key pair to sign the batch
|
||||
let (substrate_pair, key_pair) = {
|
||||
|
@ -283,7 +286,12 @@ async fn validator_set_rotation() {
|
|||
// provide a batch to complete the handover and retire the previous set
|
||||
let mut block_hash = BlockHash([0; 32]);
|
||||
OsRng.fill_bytes(&mut block_hash.0);
|
||||
let batch = Batch { network, id: 0, block: block_hash, instructions: vec![] };
|
||||
let batch = Batch {
|
||||
network: network.try_into().unwrap(),
|
||||
id: 0,
|
||||
block: block_hash,
|
||||
instructions: vec![],
|
||||
};
|
||||
publish_tx(
|
||||
&serai,
|
||||
&SeraiInInstructions::execute_batch(SignedBatch {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
use serai_primitives::{Coin, SubstrateAmount, Balance};
|
||||
use serai_primitives::{Balance, Coin, ExternalBalance, SubstrateAmount};
|
||||
|
||||
pub trait AllowMint {
|
||||
fn is_allowed(balance: &Balance) -> bool;
|
||||
fn is_allowed(balance: &ExternalBalance) -> bool;
|
||||
}
|
||||
|
||||
impl AllowMint for () {
|
||||
fn is_allowed(_: &Balance) -> bool {
|
||||
fn is_allowed(_: &ExternalBalance) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,10 @@ pub mod pallet {
|
|||
pub fn mint(to: Public, balance: Balance) -> Result<(), Error<T, I>> {
|
||||
// If the coin isn't Serai, which we're always allowed to mint, and the mint isn't explicitly
|
||||
// allowed, error
|
||||
if (balance.coin != Coin::Serai) && (!T::AllowMint::is_allowed(&balance)) {
|
||||
if !ExternalCoin::try_from(balance.coin)
|
||||
.map(|coin| T::AllowMint::is_allowed(&ExternalBalance { coin, amount: balance.amount }))
|
||||
.unwrap_or(true)
|
||||
{
|
||||
Err(Error::<T, I>::MintNotAllowed)?;
|
||||
}
|
||||
|
||||
|
@ -230,22 +233,18 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
/// Burn `balance` with `OutInstructionWithBalance` from the caller.
|
||||
/// Errors if called for SRI or Instance1 instance of this pallet.
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight((0, DispatchClass::Normal))] // TODO
|
||||
pub fn burn_with_instruction(
|
||||
origin: OriginFor<T>,
|
||||
instruction: OutInstructionWithBalance,
|
||||
) -> DispatchResult {
|
||||
if instruction.balance.coin == Coin::Serai {
|
||||
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
||||
}
|
||||
if TypeId::of::<I>() == TypeId::of::<LiquidityTokensInstance>() {
|
||||
Err(Error::<T, I>::BurnWithInstructionNotAllowed)?;
|
||||
}
|
||||
|
||||
let from = ensure_signed(origin)?;
|
||||
Self::burn_internal(from, instruction.balance)?;
|
||||
Self::burn_internal(from, instruction.balance.into())?;
|
||||
Self::deposit_event(Event::BurnWithInstruction { from, instruction });
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use serde::{Serialize, Deserialize};
|
|||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::{Balance, SeraiAddress, ExternalAddress, Data, system_address};
|
||||
use serai_primitives::{system_address, Data, ExternalAddress, ExternalBalance, SeraiAddress};
|
||||
|
||||
pub const FEE_ACCOUNT: SeraiAddress = system_address(b"Coins-fees");
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub struct OutInstruction {
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct OutInstructionWithBalance {
|
||||
pub instruction: OutInstruction,
|
||||
pub balance: Balance,
|
||||
pub balance: ExternalBalance,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
|
|
|
@ -38,7 +38,7 @@ type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup
|
|||
|
||||
type LiquidityTokens<T> = coins_pallet::Pallet<T, coins_pallet::Instance1>;
|
||||
|
||||
fn create_coin<T: Config>(coin: &Coin) -> (T::AccountId, AccountIdLookupOf<T>) {
|
||||
fn create_coin<T: Config>(coin: &ExternalCoin) -> (T::AccountId, AccountIdLookupOf<T>) {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let caller_lookup = T::Lookup::unlookup(caller);
|
||||
assert_ok!(Coins::<T>::mint(
|
||||
|
@ -47,12 +47,14 @@ fn create_coin<T: Config>(coin: &Coin) -> (T::AccountId, AccountIdLookupOf<T>) {
|
|||
));
|
||||
assert_ok!(Coins::<T>::mint(
|
||||
caller,
|
||||
Balance { coin: *coin, amount: Amount(INITIAL_COIN_BALANCE) }
|
||||
Balance { coin: (*coin).into(), amount: Amount(INITIAL_COIN_BALANCE) }
|
||||
));
|
||||
(caller, caller_lookup)
|
||||
}
|
||||
|
||||
fn create_coin_and_pool<T: Config>(coin: &Coin) -> (Coin, T::AccountId, AccountIdLookupOf<T>) {
|
||||
fn create_coin_and_pool<T: Config>(
|
||||
coin: &ExternalCoin,
|
||||
) -> (ExternalCoin, T::AccountId, AccountIdLookupOf<T>) {
|
||||
let (caller, caller_lookup) = create_coin::<T>(coin);
|
||||
assert_ok!(Dex::<T>::create_pool(*coin));
|
||||
|
||||
|
@ -62,7 +64,7 @@ fn create_coin_and_pool<T: Config>(coin: &Coin) -> (Coin, T::AccountId, AccountI
|
|||
benchmarks! {
|
||||
add_liquidity {
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin2 = ExternalCoin::Bitcoin;
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
|
||||
let add_amount: u64 = 1000;
|
||||
}: _(
|
||||
|
@ -75,13 +77,13 @@ benchmarks! {
|
|||
caller
|
||||
)
|
||||
verify {
|
||||
let pool_id = Dex::<T>::get_pool_id(coin1, coin2).unwrap();
|
||||
let pool_id = Dex::<T>::get_pool_id(coin1, coin2.into()).unwrap();
|
||||
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
|
||||
add_amount,
|
||||
1000u64,
|
||||
).unwrap();
|
||||
assert_eq!(
|
||||
LiquidityTokens::<T>::balance(caller, lp_token).0,
|
||||
LiquidityTokens::<T>::balance(caller, lp_token.into()).0,
|
||||
lp_minted
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -91,7 +93,7 @@ benchmarks! {
|
|||
assert_eq!(
|
||||
Coins::<T>::balance(
|
||||
Dex::<T>::get_pool_account(pool_id),
|
||||
Coin::Bitcoin,
|
||||
ExternalCoin::Bitcoin.into(),
|
||||
).0,
|
||||
1000
|
||||
);
|
||||
|
@ -99,7 +101,7 @@ benchmarks! {
|
|||
|
||||
remove_liquidity {
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = ExternalCoin::Monero;
|
||||
let (lp_token, caller, _) = create_coin_and_pool::<T>(&coin2);
|
||||
let add_amount: u64 = 100;
|
||||
let lp_minted = Dex::<T>::calc_lp_amount_for_zero_supply(
|
||||
|
@ -117,7 +119,7 @@ benchmarks! {
|
|||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
let total_supply = LiquidityTokens::<T>::supply(lp_token);
|
||||
let total_supply = LiquidityTokens::<T>::supply(Coin::from(lp_token));
|
||||
}: _(
|
||||
SystemOrigin::Signed(caller),
|
||||
coin2,
|
||||
|
@ -127,7 +129,7 @@ benchmarks! {
|
|||
caller
|
||||
)
|
||||
verify {
|
||||
let new_total_supply = LiquidityTokens::<T>::supply(lp_token);
|
||||
let new_total_supply = LiquidityTokens::<T>::supply(Coin::from(lp_token));
|
||||
assert_eq!(
|
||||
new_total_supply,
|
||||
total_supply - remove_lp_amount
|
||||
|
@ -136,8 +138,8 @@ benchmarks! {
|
|||
|
||||
swap_exact_tokens_for_tokens {
|
||||
let native = Coin::native();
|
||||
let coin1 = Coin::Bitcoin;
|
||||
let coin2 = Coin::Ether;
|
||||
let coin1 = ExternalCoin::Bitcoin;
|
||||
let coin2 = ExternalCoin::Ether;
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
|
||||
let (_, _) = create_coin::<T>(&coin2);
|
||||
|
||||
|
@ -168,21 +170,21 @@ benchmarks! {
|
|||
caller,
|
||||
)?;
|
||||
|
||||
let path = vec![coin1, native, coin2];
|
||||
let path = vec![Coin::from(coin1), native, Coin::from(coin2)];
|
||||
let path = BoundedVec::<_, T::MaxSwapPathLength>::try_from(path).unwrap();
|
||||
let native_balance = Coins::<T>::balance(caller, native).0;
|
||||
let coin1_balance = Coins::<T>::balance(caller, Coin::Bitcoin).0;
|
||||
let coin1_balance = Coins::<T>::balance(caller, ExternalCoin::Bitcoin.into()).0;
|
||||
}: _(SystemOrigin::Signed(caller), path, swap_amount, 1u64, caller)
|
||||
verify {
|
||||
let ed_bump = 2u64;
|
||||
let new_coin1_balance = Coins::<T>::balance(caller, Coin::Bitcoin).0;
|
||||
let new_coin1_balance = Coins::<T>::balance(caller, ExternalCoin::Bitcoin.into()).0;
|
||||
assert_eq!(new_coin1_balance, coin1_balance - 100u64);
|
||||
}
|
||||
|
||||
swap_tokens_for_exact_tokens {
|
||||
let native = Coin::native();
|
||||
let coin1 = Coin::Bitcoin;
|
||||
let coin2 = Coin::Ether;
|
||||
let coin1 = ExternalCoin::Bitcoin;
|
||||
let coin2 = ExternalCoin::Ether;
|
||||
let (_, caller, _) = create_coin_and_pool::<T>(&coin1);
|
||||
let (_, _) = create_coin::<T>(&coin2);
|
||||
|
||||
|
@ -208,10 +210,10 @@ benchmarks! {
|
|||
0u64,
|
||||
caller,
|
||||
)?;
|
||||
let path = vec![coin1, native, coin2];
|
||||
let path = vec![Coin::from(coin1), native, Coin::from(coin2)];
|
||||
|
||||
let path: BoundedVec<_, T::MaxSwapPathLength> = BoundedVec::try_from(path).unwrap();
|
||||
let coin2_balance = Coins::<T>::balance(caller, Coin::Ether).0;
|
||||
let coin2_balance = Coins::<T>::balance(caller, ExternalCoin::Ether.into()).0;
|
||||
}: _(
|
||||
SystemOrigin::Signed(caller),
|
||||
path.clone(),
|
||||
|
@ -220,7 +222,7 @@ benchmarks! {
|
|||
caller
|
||||
)
|
||||
verify {
|
||||
let new_coin2_balance = Coins::<T>::balance(caller, Coin::Ether).0;
|
||||
let new_coin2_balance = Coins::<T>::balance(caller, ExternalCoin::Ether.into()).0;
|
||||
assert_eq!(new_coin2_balance, coin2_balance + 100u64);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ mod tests;
|
|||
#[cfg(test)]
|
||||
mod mock;
|
||||
|
||||
use frame_support::ensure;
|
||||
use frame_support::{ensure, pallet_prelude::*, BoundedBTreeSet};
|
||||
use frame_system::{
|
||||
pallet_prelude::{BlockNumberFor, OriginFor},
|
||||
ensure_signed,
|
||||
|
@ -86,9 +86,12 @@ use frame_system::{
|
|||
|
||||
pub use pallet::*;
|
||||
|
||||
use sp_runtime::{traits::TrailingZeroInput, DispatchError};
|
||||
use sp_runtime::{
|
||||
traits::{TrailingZeroInput, IntegerSquareRoot},
|
||||
DispatchError,
|
||||
};
|
||||
|
||||
use serai_primitives::{NetworkId, Coin, SubstrateAmount};
|
||||
use serai_primitives::*;
|
||||
|
||||
use sp_std::prelude::*;
|
||||
pub use types::*;
|
||||
|
@ -103,20 +106,16 @@ pub use weights::WeightInfo;
|
|||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
use frame_support::{pallet_prelude::*, BoundedBTreeSet};
|
||||
|
||||
use sp_core::sr25519::Public;
|
||||
use sp_runtime::traits::IntegerSquareRoot;
|
||||
|
||||
use coins_pallet::{Pallet as CoinsPallet, Config as CoinsConfig};
|
||||
|
||||
use serai_primitives::{Coin, Amount, Balance, SubstrateAmount, reverse_lexicographic_order};
|
||||
|
||||
/// Pool ID.
|
||||
///
|
||||
/// The pool's `AccountId` is derived from this type. Any changes to the type may necessitate a
|
||||
/// migration.
|
||||
pub type PoolId = Coin;
|
||||
pub type PoolId = ExternalCoin;
|
||||
|
||||
/// LiquidityTokens Pallet as an instance of coins pallet.
|
||||
pub type LiquidityTokens<T> = coins_pallet::Pallet<T, coins_pallet::Instance1>;
|
||||
|
@ -164,7 +163,7 @@ pub mod pallet {
|
|||
#[pallet::storage]
|
||||
#[pallet::getter(fn spot_price_for_block)]
|
||||
pub type SpotPriceForBlock<T: Config> =
|
||||
StorageDoubleMap<_, Identity, BlockNumberFor<T>, Identity, Coin, Amount, OptionQuery>;
|
||||
StorageDoubleMap<_, Identity, BlockNumberFor<T>, Identity, ExternalCoin, Amount, OptionQuery>;
|
||||
|
||||
/// Moving window of prices from each block.
|
||||
///
|
||||
|
@ -173,30 +172,32 @@ pub mod pallet {
|
|||
/// low to high.
|
||||
#[pallet::storage]
|
||||
pub type SpotPrices<T: Config> =
|
||||
StorageDoubleMap<_, Identity, Coin, Identity, [u8; 8], u16, OptionQuery>;
|
||||
StorageDoubleMap<_, Identity, ExternalCoin, Identity, [u8; 8], u16, OptionQuery>;
|
||||
|
||||
// SpotPrices, yet with keys stored in reverse lexicographic order.
|
||||
#[pallet::storage]
|
||||
pub type ReverseSpotPrices<T: Config> =
|
||||
StorageDoubleMap<_, Identity, Coin, Identity, [u8; 8], (), OptionQuery>;
|
||||
StorageDoubleMap<_, Identity, ExternalCoin, Identity, [u8; 8], (), OptionQuery>;
|
||||
|
||||
/// Current length of the `SpotPrices` map.
|
||||
#[pallet::storage]
|
||||
pub type SpotPricesLength<T: Config> = StorageMap<_, Identity, Coin, u16, OptionQuery>;
|
||||
pub type SpotPricesLength<T: Config> = StorageMap<_, Identity, ExternalCoin, u16, OptionQuery>;
|
||||
|
||||
/// Current position of the median within the `SpotPrices` map;
|
||||
#[pallet::storage]
|
||||
pub type CurrentMedianPosition<T: Config> = StorageMap<_, Identity, Coin, u16, OptionQuery>;
|
||||
pub type CurrentMedianPosition<T: Config> =
|
||||
StorageMap<_, Identity, ExternalCoin, u16, OptionQuery>;
|
||||
|
||||
/// Current median price of the prices in the `SpotPrices` map at any given time.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn median_price)]
|
||||
pub type MedianPrice<T: Config> = StorageMap<_, Identity, Coin, Amount, OptionQuery>;
|
||||
pub type MedianPrice<T: Config> = StorageMap<_, Identity, ExternalCoin, Amount, OptionQuery>;
|
||||
|
||||
/// The price used for evaluating economic security, which is the highest observed median price.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn security_oracle_value)]
|
||||
pub type SecurityOracleValue<T: Config> = StorageMap<_, Identity, Coin, Amount, OptionQuery>;
|
||||
pub type SecurityOracleValue<T: Config> =
|
||||
StorageMap<_, Identity, ExternalCoin, Amount, OptionQuery>;
|
||||
|
||||
/// Total swap volume of a given pool in terms of SRI.
|
||||
#[pallet::storage]
|
||||
|
@ -205,7 +206,7 @@ pub mod pallet {
|
|||
|
||||
impl<T: Config> Pallet<T> {
|
||||
fn restore_median(
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
mut current_median_pos: u16,
|
||||
mut current_median: Amount,
|
||||
length: u16,
|
||||
|
@ -256,7 +257,7 @@ pub mod pallet {
|
|||
MedianPrice::<T>::set(coin, Some(current_median));
|
||||
}
|
||||
|
||||
pub(crate) fn insert_into_median(coin: Coin, amount: Amount) {
|
||||
pub(crate) fn insert_into_median(coin: ExternalCoin, amount: Amount) {
|
||||
let new_quantity_of_presences =
|
||||
SpotPrices::<T>::get(coin, amount.0.to_be_bytes()).unwrap_or(0) + 1;
|
||||
SpotPrices::<T>::set(coin, amount.0.to_be_bytes(), Some(new_quantity_of_presences));
|
||||
|
@ -286,7 +287,7 @@ pub mod pallet {
|
|||
Self::restore_median(coin, current_median_pos, current_median, new_length);
|
||||
}
|
||||
|
||||
pub(crate) fn remove_from_median(coin: Coin, amount: Amount) {
|
||||
pub(crate) fn remove_from_median(coin: ExternalCoin, amount: Amount) {
|
||||
let mut current_median = MedianPrice::<T>::get(coin).unwrap();
|
||||
|
||||
let mut current_median_pos = CurrentMedianPosition::<T>::get(coin).unwrap();
|
||||
|
@ -451,7 +452,7 @@ pub mod pallet {
|
|||
// insert the new price to our oracle window
|
||||
// The spot price for 1 coin, in atomic units, to SRI is used
|
||||
let sri_per_coin =
|
||||
if let Ok((sri_balance, coin_balance)) = Self::get_reserves(&Coin::native(), &coin) {
|
||||
if let Ok((sri_balance, coin_balance)) = Self::get_reserves(&Coin::Serai, &coin.into()) {
|
||||
// We use 1 coin to handle rounding errors which may occur with atomic units
|
||||
// If we used atomic units, any coin whose atomic unit is worth less than SRI's atomic
|
||||
// unit would cause a 'price' of 0
|
||||
|
@ -493,9 +494,9 @@ pub mod pallet {
|
|||
/// (the id of which is returned in the `Event::PoolCreated` event).
|
||||
///
|
||||
/// Once a pool is created, someone may [`Pallet::add_liquidity`] to it.
|
||||
pub(crate) fn create_pool(coin: Coin) -> DispatchResult {
|
||||
pub(crate) fn create_pool(coin: ExternalCoin) -> DispatchResult {
|
||||
// get pool_id
|
||||
let pool_id = Self::get_pool_id(coin, Coin::Serai)?;
|
||||
let pool_id = Self::get_pool_id(coin.into(), Coin::native())?;
|
||||
ensure!(!Pools::<T>::contains_key(pool_id), Error::<T>::PoolExists);
|
||||
|
||||
let pool_account = Self::get_pool_account(pool_id);
|
||||
|
@ -508,9 +509,11 @@ pub mod pallet {
|
|||
|
||||
/// A hook to be called whenever a network's session is rotated.
|
||||
pub fn on_new_session(network: NetworkId) {
|
||||
// reset the oracle value
|
||||
for coin in network.coins() {
|
||||
SecurityOracleValue::<T>::set(*coin, Self::median_price(coin));
|
||||
// Only track the price for non-SRI coins as this is SRI denominated
|
||||
if let NetworkId::External(n) = network {
|
||||
for coin in n.coins() {
|
||||
SecurityOracleValue::<T>::set(coin, Self::median_price(coin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -532,7 +535,7 @@ pub mod pallet {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_liquidity(
|
||||
origin: OriginFor<T>,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
coin_desired: SubstrateAmount,
|
||||
sri_desired: SubstrateAmount,
|
||||
coin_min: SubstrateAmount,
|
||||
|
@ -542,7 +545,7 @@ pub mod pallet {
|
|||
let sender = ensure_signed(origin)?;
|
||||
ensure!((sri_desired > 0) && (coin_desired > 0), Error::<T>::WrongDesiredAmount);
|
||||
|
||||
let pool_id = Self::get_pool_id(coin, Coin::Serai)?;
|
||||
let pool_id = Self::get_pool_id(coin.into(), Coin::native())?;
|
||||
|
||||
// create the pool if it doesn't exist. We can just attempt to do that because our checks
|
||||
// far enough to allow that.
|
||||
|
@ -552,7 +555,7 @@ pub mod pallet {
|
|||
let pool_account = Self::get_pool_account(pool_id);
|
||||
|
||||
let sri_reserve = Self::get_balance(&pool_account, Coin::Serai);
|
||||
let coin_reserve = Self::get_balance(&pool_account, coin);
|
||||
let coin_reserve = Self::get_balance(&pool_account, coin.into());
|
||||
|
||||
let sri_amount: SubstrateAmount;
|
||||
let coin_amount: SubstrateAmount;
|
||||
|
@ -583,16 +586,20 @@ pub mod pallet {
|
|||
&pool_account,
|
||||
Balance { coin: Coin::Serai, amount: Amount(sri_amount) },
|
||||
)?;
|
||||
Self::transfer(&sender, &pool_account, Balance { coin, amount: Amount(coin_amount) })?;
|
||||
Self::transfer(
|
||||
&sender,
|
||||
&pool_account,
|
||||
Balance { coin: coin.into(), amount: Amount(coin_amount) },
|
||||
)?;
|
||||
|
||||
let total_supply = LiquidityTokens::<T>::supply(coin);
|
||||
let total_supply = LiquidityTokens::<T>::supply(Coin::from(coin));
|
||||
|
||||
let lp_token_amount: SubstrateAmount;
|
||||
if total_supply == 0 {
|
||||
lp_token_amount = Self::calc_lp_amount_for_zero_supply(sri_amount, coin_amount)?;
|
||||
LiquidityTokens::<T>::mint(
|
||||
pool_account,
|
||||
Balance { coin, amount: Amount(T::MintMinLiquidity::get()) },
|
||||
Balance { coin: coin.into(), amount: Amount(T::MintMinLiquidity::get()) },
|
||||
)?;
|
||||
} else {
|
||||
let side1 = Self::mul_div(sri_amount, total_supply, sri_reserve)?;
|
||||
|
@ -605,7 +612,10 @@ pub mod pallet {
|
|||
Error::<T>::InsufficientLiquidityMinted
|
||||
);
|
||||
|
||||
LiquidityTokens::<T>::mint(mint_to, Balance { coin, amount: Amount(lp_token_amount) })?;
|
||||
LiquidityTokens::<T>::mint(
|
||||
mint_to,
|
||||
Balance { coin: coin.into(), amount: Amount(lp_token_amount) },
|
||||
)?;
|
||||
|
||||
Self::deposit_event(Event::LiquidityAdded {
|
||||
who: sender,
|
||||
|
@ -626,25 +636,24 @@ pub mod pallet {
|
|||
#[pallet::weight(T::WeightInfo::remove_liquidity())]
|
||||
pub fn remove_liquidity(
|
||||
origin: OriginFor<T>,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
lp_token_burn: SubstrateAmount,
|
||||
coin_min_receive: SubstrateAmount,
|
||||
sri_min_receive: SubstrateAmount,
|
||||
withdraw_to: T::AccountId,
|
||||
) -> DispatchResult {
|
||||
let sender = ensure_signed(origin.clone())?;
|
||||
ensure!(coin != Coin::Serai, Error::<T>::EqualCoins);
|
||||
|
||||
let pool_id = Self::get_pool_id(coin, Coin::Serai).unwrap();
|
||||
let pool_id = Self::get_pool_id(coin.into(), Coin::native()).unwrap();
|
||||
ensure!(lp_token_burn > 0, Error::<T>::ZeroLiquidity);
|
||||
|
||||
Pools::<T>::get(pool_id).as_ref().ok_or(Error::<T>::PoolNotFound)?;
|
||||
|
||||
let pool_account = Self::get_pool_account(pool_id);
|
||||
let sri_reserve = Self::get_balance(&pool_account, Coin::Serai);
|
||||
let coin_reserve = Self::get_balance(&pool_account, coin);
|
||||
let coin_reserve = Self::get_balance(&pool_account, coin.into());
|
||||
|
||||
let total_supply = LiquidityTokens::<T>::supply(coin);
|
||||
let total_supply = LiquidityTokens::<T>::supply(Coin::from(coin));
|
||||
let lp_redeem_amount = lp_token_burn;
|
||||
|
||||
let sri_amount = Self::mul_div(lp_redeem_amount, sri_reserve, total_supply)?;
|
||||
|
@ -665,14 +674,21 @@ pub mod pallet {
|
|||
ensure!(coin_reserve_left >= 1, Error::<T>::ReserveLeftLessThanMinimum);
|
||||
|
||||
// burn the provided lp token amount that includes the fee
|
||||
LiquidityTokens::<T>::burn(origin, Balance { coin, amount: Amount(lp_token_burn) })?;
|
||||
LiquidityTokens::<T>::burn(
|
||||
origin,
|
||||
Balance { coin: coin.into(), amount: Amount(lp_token_burn) },
|
||||
)?;
|
||||
|
||||
Self::transfer(
|
||||
&pool_account,
|
||||
&withdraw_to,
|
||||
Balance { coin: Coin::Serai, amount: Amount(sri_amount) },
|
||||
)?;
|
||||
Self::transfer(&pool_account, &withdraw_to, Balance { coin, amount: Amount(coin_amount) })?;
|
||||
Self::transfer(
|
||||
&pool_account,
|
||||
&withdraw_to,
|
||||
Balance { coin: coin.into(), amount: Amount(coin_amount) },
|
||||
)?;
|
||||
|
||||
Self::deposit_event(Event::LiquidityRemoved {
|
||||
who: sender,
|
||||
|
@ -920,11 +936,9 @@ pub mod pallet {
|
|||
pub fn get_pool_id(coin1: Coin, coin2: Coin) -> Result<PoolId, Error<T>> {
|
||||
ensure!((coin1 == Coin::Serai) || (coin2 == Coin::Serai), Error::<T>::PoolNotFound);
|
||||
ensure!(coin1 != coin2, Error::<T>::EqualCoins);
|
||||
if coin1 == Coin::Serai {
|
||||
Ok(coin2)
|
||||
} else {
|
||||
Ok(coin1)
|
||||
}
|
||||
ExternalCoin::try_from(coin1)
|
||||
.or_else(|()| ExternalCoin::try_from(coin2))
|
||||
.map_err(|()| Error::<T>::PoolNotFound)
|
||||
}
|
||||
|
||||
/// Returns the balance of each coin in the pool.
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
// It has been forked into a crate distributed under the AGPL 3.0.
|
||||
// Please check the current distribution for up-to-date copyright and licensing information.
|
||||
|
||||
use crate::{mock::*, *};
|
||||
use crate::{
|
||||
mock::{*, MEDIAN_PRICE_WINDOW_LENGTH},
|
||||
*,
|
||||
};
|
||||
use frame_support::{assert_noop, assert_ok};
|
||||
|
||||
pub use coins_pallet as coins;
|
||||
|
@ -72,11 +75,13 @@ fn check_pool_accounts_dont_collide() {
|
|||
let mut map = HashSet::new();
|
||||
|
||||
for coin in coins() {
|
||||
let account = Dex::get_pool_account(coin);
|
||||
if map.contains(&account) {
|
||||
panic!("Collision at {coin:?}");
|
||||
if let Coin::External(c) = coin {
|
||||
let account = Dex::get_pool_account(c);
|
||||
if map.contains(&account) {
|
||||
panic!("Collision at {c:?}");
|
||||
}
|
||||
map.insert(account);
|
||||
}
|
||||
map.insert(account);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,11 +103,11 @@ fn can_create_pool() {
|
|||
let coin_account_deposit: u64 = 0;
|
||||
let user: PublicKey = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(1000) }));
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_eq!(balance(user, coin1), 1000 - coin_account_deposit);
|
||||
|
||||
|
@ -111,15 +116,13 @@ fn can_create_pool() {
|
|||
[Event::<Test>::PoolCreated { pool_id, pool_account: Dex::get_pool_account(pool_id) }]
|
||||
);
|
||||
assert_eq!(pools(), vec![pool_id]);
|
||||
|
||||
assert_noop!(Dex::create_pool(coin1), Error::<Test>::EqualCoins);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_same_pool_twice_should_fail() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let coin = Coin::Dai;
|
||||
let coin = ExternalCoin::Dai;
|
||||
assert_ok!(Dex::create_pool(coin));
|
||||
assert_noop!(Dex::create_pool(coin), Error::<Test>::PoolExists);
|
||||
});
|
||||
|
@ -129,13 +132,13 @@ fn create_same_pool_twice_should_fail() {
|
|||
fn different_pools_should_have_different_lp_tokens() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin3 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Bitcoin);
|
||||
let coin3 = Coin::External(ExternalCoin::Ether);
|
||||
let pool_id_1_2 = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
let pool_id_1_3 = Dex::get_pool_id(coin1, coin3).unwrap();
|
||||
|
||||
let lp_token2_1 = coin2;
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
let lp_token3_1 = coin3;
|
||||
|
||||
assert_eq!(
|
||||
|
@ -146,7 +149,7 @@ fn different_pools_should_have_different_lp_tokens() {
|
|||
}]
|
||||
);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin3));
|
||||
assert_ok!(Dex::create_pool(coin3.try_into().unwrap()));
|
||||
assert_eq!(
|
||||
events(),
|
||||
[Event::<Test>::PoolCreated {
|
||||
|
@ -164,13 +167,13 @@ fn can_add_liquidity() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Dai;
|
||||
let coin3 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Dai);
|
||||
let coin3 = Coin::External(ExternalCoin::Monero);
|
||||
|
||||
let lp_token1 = coin2;
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
let lp_token2 = coin3;
|
||||
assert_ok!(Dex::create_pool(coin3));
|
||||
assert_ok!(Dex::create_pool(coin3.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(
|
||||
user,
|
||||
|
@ -179,7 +182,15 @@ fn can_add_liquidity() {
|
|||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin3, amount: Amount(1000) }));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 10, 10000, 10, 10000, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
user,
|
||||
));
|
||||
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
assert!(events().contains(&Event::<Test>::LiquidityAdded {
|
||||
|
@ -198,7 +209,15 @@ fn can_add_liquidity() {
|
|||
assert_eq!(pool_balance(user, lp_token1), 216);
|
||||
|
||||
// try to pass the non-native - native coins, the result should be the same
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin3, 10, 10000, 10, 10000, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin3.try_into().unwrap(),
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
user,
|
||||
));
|
||||
|
||||
let pool_id = Dex::get_pool_id(coin1, coin3).unwrap();
|
||||
assert!(events().contains(&Event::<Test>::LiquidityAdded {
|
||||
|
@ -223,12 +242,15 @@ fn add_tiny_liquidity_leads_to_insufficient_liquidity_minted_error() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin2 = ExternalCoin::Bitcoin;
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(1000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(
|
||||
user,
|
||||
Balance { coin: coin2.into(), amount: Amount(1000) }
|
||||
));
|
||||
|
||||
assert_noop!(
|
||||
Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 1, 1, 1, 1, user),
|
||||
|
@ -242,11 +264,11 @@ fn add_tiny_liquidity_directly_to_pool_address() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin3 = Coin::Dai;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
let coin3 = Coin::External(ExternalCoin::Dai);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin3));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
assert_ok!(Dex::create_pool(coin3.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(10000 * 2) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(10000) }));
|
||||
|
@ -259,7 +281,15 @@ fn add_tiny_liquidity_directly_to_pool_address() {
|
|||
Balance { coin: coin1, amount: Amount(1000) }
|
||||
));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 10, 10000, 10, 10000, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
user,
|
||||
));
|
||||
|
||||
// check the same but for coin3 (non-native token)
|
||||
let pallet_account = Dex::get_pool_account(Dex::get_pool_id(coin1, coin3).unwrap());
|
||||
|
@ -267,7 +297,15 @@ fn add_tiny_liquidity_directly_to_pool_address() {
|
|||
pallet_account,
|
||||
Balance { coin: coin2, amount: Amount(1) }
|
||||
));
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin3, 10, 10000, 10, 10000, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin3.try_into().unwrap(),
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
user,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -276,11 +314,11 @@ fn can_remove_liquidity() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
|
||||
let lp_token = coin2;
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(
|
||||
user,
|
||||
|
@ -290,7 +328,7 @@ fn can_remove_liquidity() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
100000,
|
||||
1000000000,
|
||||
100000,
|
||||
|
@ -302,7 +340,7 @@ fn can_remove_liquidity() {
|
|||
|
||||
assert_ok!(Dex::remove_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
total_lp_received,
|
||||
0,
|
||||
0,
|
||||
|
@ -334,15 +372,23 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Dai;
|
||||
let coin2 = Coin::External(ExternalCoin::Dai);
|
||||
let lp_token = coin2;
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(10000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 10, 10000, 10, 10000, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
10,
|
||||
10000,
|
||||
10,
|
||||
10000,
|
||||
user,
|
||||
));
|
||||
|
||||
// Only 216 lp_tokens_minted
|
||||
assert_eq!(pool_balance(user, lp_token), 216);
|
||||
|
@ -350,7 +396,7 @@ fn can_not_redeem_more_lp_tokens_than_were_minted() {
|
|||
assert_noop!(
|
||||
Dex::remove_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
216 + 1, // Try and redeem 10 lp tokens while only 9 minted.
|
||||
0,
|
||||
0,
|
||||
|
@ -366,14 +412,22 @@ fn can_quote_price() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(100000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 200, 10000, 1, 1, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
200,
|
||||
10000,
|
||||
1,
|
||||
1,
|
||||
user,
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
Dex::quote_price_exact_tokens_for_tokens(Coin::native(), coin2, 3000, false,),
|
||||
|
@ -481,14 +535,22 @@ fn quote_price_exact_tokens_for_tokens_matches_execution() {
|
|||
let user = system_address(b"user1").into();
|
||||
let user2 = system_address(b"user2").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin2 = Coin::External(ExternalCoin::Bitcoin);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(100000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 200, 10000, 1, 1, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
200,
|
||||
10000,
|
||||
1,
|
||||
1,
|
||||
user,
|
||||
));
|
||||
|
||||
let amount = 1;
|
||||
let quoted_price = 49;
|
||||
|
@ -518,14 +580,22 @@ fn quote_price_tokens_for_exact_tokens_matches_execution() {
|
|||
let user = system_address(b"user1").into();
|
||||
let user2 = system_address(b"user2").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(100000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 200, 10000, 1, 1, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
200,
|
||||
10000,
|
||||
1,
|
||||
1,
|
||||
user,
|
||||
));
|
||||
|
||||
let amount = 49;
|
||||
let quoted_price = 1;
|
||||
|
@ -557,10 +627,10 @@ fn can_swap_with_native() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(10000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
@ -570,7 +640,7 @@ fn can_swap_with_native() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -602,8 +672,8 @@ fn can_swap_with_realistic_values() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let sri = Coin::native();
|
||||
let dai = Coin::Dai;
|
||||
assert_ok!(Dex::create_pool(dai));
|
||||
let dai = Coin::External(ExternalCoin::Dai);
|
||||
assert_ok!(Dex::create_pool(dai.try_into().unwrap()));
|
||||
|
||||
const UNIT: u64 = 1_000_000_000;
|
||||
|
||||
|
@ -620,7 +690,7 @@ fn can_swap_with_realistic_values() {
|
|||
let liquidity_dai = 1_000_000 * UNIT;
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
dai,
|
||||
dai.try_into().unwrap(),
|
||||
liquidity_dai,
|
||||
liquidity_sri,
|
||||
1,
|
||||
|
@ -653,9 +723,9 @@ fn can_not_swap_in_pool_with_no_liquidity_added_yet() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
// Check can't swap an empty pool
|
||||
assert_noop!(
|
||||
|
@ -676,11 +746,11 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin2 = Coin::External(ExternalCoin::Bitcoin);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
let lp_token = coin2;
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(10000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
@ -690,7 +760,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -714,7 +784,7 @@ fn check_no_panic_when_try_swap_close_to_empty_pool() {
|
|||
|
||||
assert_ok!(Dex::remove_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
lp_token_minted,
|
||||
1,
|
||||
1,
|
||||
|
@ -787,9 +857,9 @@ fn swap_should_not_work_if_too_much_slippage() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(10000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
@ -799,7 +869,7 @@ fn swap_should_not_work_if_too_much_slippage() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -827,10 +897,10 @@ fn can_swap_tokens_for_exact_tokens() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Dai;
|
||||
let coin2 = Coin::External(ExternalCoin::Dai);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(20000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
@ -844,7 +914,7 @@ fn can_swap_tokens_for_exact_tokens() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -882,11 +952,11 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() {
|
|||
let user = system_address(b"user1").into();
|
||||
let user2 = system_address(b"user2").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
let pool_id = Dex::get_pool_id(coin1, coin2).unwrap();
|
||||
let lp_token = coin2;
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
let base1 = 10000;
|
||||
let base2 = 1000;
|
||||
|
@ -903,7 +973,7 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user2),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -947,7 +1017,7 @@ fn can_swap_tokens_for_exact_tokens_when_not_liquidity_provider() {
|
|||
|
||||
assert_ok!(Dex::remove_liquidity(
|
||||
RuntimeOrigin::signed(user2),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
lp_token_minted,
|
||||
0,
|
||||
0,
|
||||
|
@ -961,9 +1031,9 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(20000) }));
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin2, amount: Amount(1000) }));
|
||||
|
@ -973,7 +1043,7 @@ fn swap_tokens_for_exact_tokens_should_not_work_if_too_much_slippage() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -1001,11 +1071,11 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Dai;
|
||||
let coin3 = Coin::Monero;
|
||||
let coin2 = Coin::External(ExternalCoin::Dai);
|
||||
let coin3 = Coin::External(ExternalCoin::Monero);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin3));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
assert_ok!(Dex::create_pool(coin3.try_into().unwrap()));
|
||||
|
||||
let base1 = 10000;
|
||||
let base2 = 10000;
|
||||
|
@ -1019,7 +1089,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -1028,7 +1098,7 @@ fn swap_exact_tokens_for_tokens_in_multi_hops() {
|
|||
));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin3,
|
||||
coin3.try_into().unwrap(),
|
||||
liquidity3,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -1089,11 +1159,11 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() {
|
|||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Bitcoin;
|
||||
let coin3 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Bitcoin);
|
||||
let coin3 = Coin::External(ExternalCoin::Ether);
|
||||
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin3));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
assert_ok!(Dex::create_pool(coin3.try_into().unwrap()));
|
||||
|
||||
let base1 = 10000;
|
||||
let base2 = 10000;
|
||||
|
@ -1107,7 +1177,7 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() {
|
|||
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2,
|
||||
coin2.try_into().unwrap(),
|
||||
liquidity2,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -1116,7 +1186,7 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() {
|
|||
));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin3,
|
||||
coin3.try_into().unwrap(),
|
||||
liquidity3,
|
||||
liquidity1,
|
||||
1,
|
||||
|
@ -1154,7 +1224,7 @@ fn swap_tokens_for_exact_tokens_in_multi_hops() {
|
|||
fn can_not_swap_same_coin() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let user = system_address(b"user1").into();
|
||||
let coin1 = Coin::Dai;
|
||||
let coin1 = Coin::External(ExternalCoin::Dai);
|
||||
assert_ok!(CoinsPallet::<Test>::mint(user, Balance { coin: coin1, amount: Amount(1000) }));
|
||||
|
||||
let exchange_amount = 10;
|
||||
|
@ -1188,10 +1258,10 @@ fn validate_pool_id_sorting() {
|
|||
// Serai < Bitcoin < Ether < Dai < Monero.
|
||||
// coin1 <= coin2 for this test to pass.
|
||||
let native = Coin::native();
|
||||
let coin1 = Coin::Bitcoin;
|
||||
let coin2 = Coin::Monero;
|
||||
assert_eq!(Dex::get_pool_id(native, coin2).unwrap(), coin2);
|
||||
assert_eq!(Dex::get_pool_id(coin2, native).unwrap(), coin2);
|
||||
let coin1 = Coin::External(ExternalCoin::Bitcoin);
|
||||
let coin2 = Coin::External(ExternalCoin::Monero);
|
||||
assert_eq!(Dex::get_pool_id(native, coin2).unwrap(), coin2.try_into().unwrap());
|
||||
assert_eq!(Dex::get_pool_id(coin2, native).unwrap(), coin2.try_into().unwrap());
|
||||
assert!(matches!(Dex::get_pool_id(native, native), Err(Error::<Test>::EqualCoins)));
|
||||
assert!(matches!(Dex::get_pool_id(coin2, coin1), Err(Error::<Test>::PoolNotFound)));
|
||||
assert!(coin2 > coin1);
|
||||
|
@ -1216,7 +1286,7 @@ fn cannot_block_pool_creation() {
|
|||
|
||||
// The target pool the user wants to create is Native <=> Coin(2)
|
||||
let coin1 = Coin::native();
|
||||
let coin2 = Coin::Ether;
|
||||
let coin2 = Coin::External(ExternalCoin::Ether);
|
||||
|
||||
// Attacker computes the still non-existing pool account for the target pair
|
||||
let pool_account = Dex::get_pool_account(Dex::get_pool_id(coin2, coin1).unwrap());
|
||||
|
@ -1238,7 +1308,7 @@ fn cannot_block_pool_creation() {
|
|||
}
|
||||
|
||||
// User can still create the pool
|
||||
assert_ok!(Dex::create_pool(coin2));
|
||||
assert_ok!(Dex::create_pool(coin2.try_into().unwrap()));
|
||||
|
||||
// User has to transfer one Coin(2) token to the pool account (otherwise add_liquidity will
|
||||
// fail with `CoinTwoDepositDidNotMeetMinimum`), also transfer native token for the same error.
|
||||
|
@ -1256,7 +1326,15 @@ fn cannot_block_pool_creation() {
|
|||
));
|
||||
|
||||
// add_liquidity shouldn't fail because of the number of consumers
|
||||
assert_ok!(Dex::add_liquidity(RuntimeOrigin::signed(user), coin2, 100, 9900, 10, 9900, user,));
|
||||
assert_ok!(Dex::add_liquidity(
|
||||
RuntimeOrigin::signed(user),
|
||||
coin2.try_into().unwrap(),
|
||||
100,
|
||||
9900,
|
||||
10,
|
||||
9900,
|
||||
user,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1281,7 +1359,7 @@ fn test_median_price() {
|
|||
prices.push(OsRng.next_u64());
|
||||
}
|
||||
}
|
||||
let coin = Coin::Bitcoin;
|
||||
let coin = ExternalCoin::Bitcoin;
|
||||
|
||||
assert!(prices.len() >= (2 * usize::from(MEDIAN_PRICE_WINDOW_LENGTH)));
|
||||
for i in 0 .. prices.len() {
|
||||
|
|
|
@ -24,7 +24,7 @@ pub mod pallet {
|
|||
#[pallet::event]
|
||||
#[pallet::generate_deposit(fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
EconomicSecurityReached { network: NetworkId },
|
||||
EconomicSecurityReached { network: ExternalNetworkId },
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
|
@ -33,17 +33,19 @@ pub mod pallet {
|
|||
#[pallet::storage]
|
||||
#[pallet::getter(fn economic_security_block)]
|
||||
pub(crate) type EconomicSecurityBlock<T: Config> =
|
||||
StorageMap<_, Identity, NetworkId, BlockNumberFor<T>, OptionQuery>;
|
||||
StorageMap<_, Identity, ExternalNetworkId, BlockNumberFor<T>, OptionQuery>;
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
|
||||
// we accept we reached economic security once we can mint smallest amount of a network's coin
|
||||
for coin in COINS {
|
||||
for coin in EXTERNAL_COINS {
|
||||
let existing = EconomicSecurityBlock::<T>::get(coin.network());
|
||||
// TODO: we don't need to check for oracle value if is_allowed returns false when there is
|
||||
// no coin value
|
||||
if existing.is_none() &&
|
||||
Dex::<T>::security_oracle_value(coin).is_some() &&
|
||||
<T as CoinsConfig>::AllowMint::is_allowed(&Balance { coin, amount: Amount(1) })
|
||||
<T as CoinsConfig>::AllowMint::is_allowed(&ExternalBalance { coin, amount: Amount(1) })
|
||||
{
|
||||
EconomicSecurityBlock::<T>::set(coin.network(), Some(n));
|
||||
Self::deposit_event(Event::EconomicSecurityReached { network: coin.network() });
|
||||
|
|
|
@ -84,7 +84,8 @@ pub mod pallet {
|
|||
pub type CurrentSession<T: Config> = StorageMap<_, Identity, NetworkId, u32, ValueQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
pub(crate) type LastSwapVolume<T: Config> = StorageMap<_, Identity, Coin, u64, OptionQuery>;
|
||||
pub(crate) type LastSwapVolume<T: Config> =
|
||||
StorageMap<_, Identity, ExternalCoin, u64, OptionQuery>;
|
||||
|
||||
#[pallet::genesis_build]
|
||||
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
|
||||
|
@ -136,19 +137,16 @@ pub mod pallet {
|
|||
let mut total_distance: u64 = 0;
|
||||
let reward_this_epoch = if pre_ec_security {
|
||||
// calculate distance to economic security per network
|
||||
for n in NETWORKS {
|
||||
if n == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for n in EXTERNAL_NETWORKS {
|
||||
let required = ValidatorSets::<T>::required_stake_for_network(n);
|
||||
let mut current = ValidatorSets::<T>::total_allocated_stake(n).unwrap_or(Amount(0)).0;
|
||||
let mut current =
|
||||
ValidatorSets::<T>::total_allocated_stake(NetworkId::from(n)).unwrap_or(Amount(0)).0;
|
||||
if current > required {
|
||||
current = required;
|
||||
}
|
||||
|
||||
let distance = required - current;
|
||||
distances.insert(n, distance);
|
||||
distances.insert(NetworkId::from(n), distance);
|
||||
total_distance = total_distance.saturating_add(distance);
|
||||
}
|
||||
|
||||
|
@ -192,9 +190,8 @@ pub mod pallet {
|
|||
)
|
||||
} else {
|
||||
// get swap volumes
|
||||
let mut volume_per_coin: BTreeMap<Coin, u64> = BTreeMap::new();
|
||||
for c in COINS {
|
||||
// this should return 0 for SRI and so it shouldn't affect the total volume.
|
||||
let mut volume_per_coin: BTreeMap<ExternalCoin, u64> = BTreeMap::new();
|
||||
for c in EXTERNAL_COINS {
|
||||
let current_volume = Dex::<T>::swap_volume(c).unwrap_or(0);
|
||||
let last_volume = LastSwapVolume::<T>::get(c).unwrap_or(0);
|
||||
let vol_this_epoch = current_volume.saturating_sub(last_volume);
|
||||
|
@ -209,11 +206,13 @@ pub mod pallet {
|
|||
let mut volume_per_network: BTreeMap<NetworkId, u64> = BTreeMap::new();
|
||||
for (c, vol) in &volume_per_coin {
|
||||
volume_per_network.insert(
|
||||
c.network(),
|
||||
(*volume_per_network.get(&c.network()).unwrap_or(&0)).saturating_add(*vol),
|
||||
c.network().into(),
|
||||
(*volume_per_network.get(&c.network().into()).unwrap_or(&0)).saturating_add(*vol),
|
||||
);
|
||||
total_volume = total_volume.saturating_add(*vol);
|
||||
}
|
||||
// we add the serai network now
|
||||
volume_per_network.insert(NetworkId::Serai, 0);
|
||||
|
||||
(
|
||||
volume_per_network
|
||||
|
@ -245,12 +244,13 @@ pub mod pallet {
|
|||
|
||||
// distribute the rewards within the network
|
||||
for (n, reward) in rewards_per_network {
|
||||
let (validators_reward, network_pool_reward) = if n == NetworkId::Serai {
|
||||
(reward, 0)
|
||||
} else {
|
||||
let validators_reward = if let NetworkId::External(external_network) = n {
|
||||
// calculate pool vs validator share
|
||||
let capacity = ValidatorSets::<T>::total_allocated_stake(n).unwrap_or(Amount(0)).0;
|
||||
let required = ValidatorSets::<T>::required_stake_for_network(n);
|
||||
let capacity =
|
||||
ValidatorSets::<T>::total_allocated_stake(NetworkId::from(external_network))
|
||||
.unwrap_or(Amount(0))
|
||||
.0;
|
||||
let required = ValidatorSets::<T>::required_stake_for_network(external_network);
|
||||
let unused_capacity = capacity.saturating_sub(required);
|
||||
|
||||
let distribution = unused_capacity.saturating_mul(ACCURACY_MULTIPLIER) / capacity;
|
||||
|
@ -258,41 +258,44 @@ pub mod pallet {
|
|||
|
||||
let validators_reward = DESIRED_DISTRIBUTION.saturating_mul(reward) / total;
|
||||
let network_pool_reward = reward.saturating_sub(validators_reward);
|
||||
(validators_reward, network_pool_reward)
|
||||
|
||||
// send the rest to the pool
|
||||
if network_pool_reward != 0 {
|
||||
// these should be available to unwrap if we have a network_pool_reward. Because that
|
||||
// means we had an unused capacity hence in a post-ec era.
|
||||
let vpn = volume_per_network.as_ref().unwrap();
|
||||
let vpc = volume_per_coin.as_ref().unwrap();
|
||||
for c in external_network.coins() {
|
||||
let pool_reward = u64::try_from(
|
||||
u128::from(network_pool_reward).saturating_mul(u128::from(vpc[&c])) /
|
||||
u128::from(vpn[&n]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if Coins::<T>::mint(
|
||||
Dex::<T>::get_pool_account(c),
|
||||
Balance { coin: Coin::Serai, amount: Amount(pool_reward) },
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
// TODO: log the failure
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validators_reward
|
||||
} else {
|
||||
reward
|
||||
};
|
||||
|
||||
// distribute validators rewards
|
||||
Self::distribute_to_validators(n, validators_reward);
|
||||
|
||||
// send the rest to the pool
|
||||
if network_pool_reward != 0 {
|
||||
// these should be available to unwrap if we have a network_pool_reward. Because that
|
||||
// means we had an unused capacity hence in a post-ec era.
|
||||
let vpn = volume_per_network.as_ref().unwrap();
|
||||
let vpc = volume_per_coin.as_ref().unwrap();
|
||||
for c in n.coins() {
|
||||
let pool_reward = u64::try_from(
|
||||
u128::from(network_pool_reward).saturating_mul(u128::from(vpc[c])) /
|
||||
u128::from(vpn[&n]),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if Coins::<T>::mint(
|
||||
Dex::<T>::get_pool_account(*c),
|
||||
Balance { coin: Coin::Serai, amount: Amount(pool_reward) },
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
// TODO: log the failure
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we have the past session participants here in the emissions pallet so that we can
|
||||
// distribute rewards to them in the next session. Ideally we should be able to fetch this
|
||||
// information from valiadtor sets pallet.
|
||||
// information from validator sets pallet.
|
||||
Self::update_participants();
|
||||
Weight::zero() // TODO
|
||||
}
|
||||
|
@ -318,11 +321,7 @@ pub mod pallet {
|
|||
|
||||
/// Returns true if any of the external networks haven't reached economic security yet.
|
||||
fn pre_ec_security() -> bool {
|
||||
for n in NETWORKS {
|
||||
if n == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for n in EXTERNAL_NETWORKS {
|
||||
if EconomicSecurity::<T>::economic_security_block(n).is_none() {
|
||||
return true;
|
||||
}
|
||||
|
@ -362,16 +361,30 @@ pub mod pallet {
|
|||
pub fn swap_to_staked_sri(
|
||||
to: PublicKey,
|
||||
network: NetworkId,
|
||||
balance: Balance,
|
||||
balance: ExternalBalance,
|
||||
) -> DispatchResult {
|
||||
// check the network didn't reach the economic security yet
|
||||
if EconomicSecurity::<T>::economic_security_block(network).is_some() {
|
||||
Err(Error::<T>::NetworkHasEconomicSecurity)?;
|
||||
if let NetworkId::External(n) = network {
|
||||
if EconomicSecurity::<T>::economic_security_block(n).is_some() {
|
||||
Err(Error::<T>::NetworkHasEconomicSecurity)?;
|
||||
}
|
||||
} else {
|
||||
// we target 20% of the network's stake to be behind the Serai network
|
||||
let mut total_stake = 0;
|
||||
for n in NETWORKS {
|
||||
total_stake += ValidatorSets::<T>::total_allocated_stake(n).unwrap_or(Amount(0)).0;
|
||||
}
|
||||
|
||||
let stake = ValidatorSets::<T>::total_allocated_stake(network).unwrap_or(Amount(0)).0;
|
||||
let desired_stake = total_stake / (100 / SERAI_VALIDATORS_DESIRED_PERCENTAGE);
|
||||
if stake >= desired_stake {
|
||||
Err(Error::<T>::NetworkHasEconomicSecurity)?;
|
||||
}
|
||||
}
|
||||
|
||||
// swap half of the liquidity for SRI to form PoL.
|
||||
let half = balance.amount.0 / 2;
|
||||
let path = BoundedVec::try_from(vec![balance.coin, Coin::Serai]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![balance.coin.into(), Coin::Serai]).unwrap();
|
||||
let origin = RawOrigin::Signed(POL_ACCOUNT.into());
|
||||
Dex::<T>::swap_exact_tokens_for_tokens(
|
||||
origin.clone().into(),
|
||||
|
|
|
@ -54,9 +54,9 @@ pub mod pallet {
|
|||
#[pallet::event]
|
||||
#[pallet::generate_deposit(fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
GenesisLiquidityAdded { by: SeraiAddress, balance: Balance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: Balance },
|
||||
GenesisLiquidityAddedToPool { coin1: Balance, sri: Amount },
|
||||
GenesisLiquidityAdded { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityRemoved { by: SeraiAddress, balance: ExternalBalance },
|
||||
GenesisLiquidityAddedToPool { coin: ExternalBalance, sri: Amount },
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
|
@ -64,15 +64,23 @@ pub mod pallet {
|
|||
|
||||
/// Keeps shares and the amount of coins per account.
|
||||
#[pallet::storage]
|
||||
pub(crate) type Liquidity<T: Config> =
|
||||
StorageDoubleMap<_, Identity, Coin, Blake2_128Concat, PublicKey, LiquidityAmount, OptionQuery>;
|
||||
pub(crate) type Liquidity<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Identity,
|
||||
ExternalCoin,
|
||||
Blake2_128Concat,
|
||||
PublicKey,
|
||||
LiquidityAmount,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
/// Keeps the total shares and the total amount of coins per coin.
|
||||
#[pallet::storage]
|
||||
pub(crate) type Supply<T: Config> = StorageMap<_, Identity, Coin, LiquidityAmount, OptionQuery>;
|
||||
pub(crate) type Supply<T: Config> =
|
||||
StorageMap<_, Identity, ExternalCoin, LiquidityAmount, OptionQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
pub(crate) type Oracle<T: Config> = StorageMap<_, Identity, Coin, u64, OptionQuery>;
|
||||
pub(crate) type Oracle<T: Config> = StorageMap<_, Identity, ExternalCoin, u64, OptionQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn genesis_complete_block)]
|
||||
|
@ -102,11 +110,7 @@ pub mod pallet {
|
|||
// get pool & total values
|
||||
let mut pool_values = vec![];
|
||||
let mut total_value: u128 = 0;
|
||||
for coin in COINS {
|
||||
if coin == Coin::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for coin in EXTERNAL_COINS {
|
||||
// initial coin value in terms of btc
|
||||
let Some(value) = Oracle::<T>::get(coin) else {
|
||||
continue;
|
||||
|
@ -158,7 +162,7 @@ pub mod pallet {
|
|||
|
||||
// let everyone know about the event
|
||||
Self::deposit_event(Event::GenesisLiquidityAddedToPool {
|
||||
coin1: Balance { coin, amount: Amount(u64::try_from(pool_amount).unwrap()) },
|
||||
coin: ExternalBalance { coin, amount: Amount(u64::try_from(pool_amount).unwrap()) },
|
||||
sri: Amount(sri_amount),
|
||||
});
|
||||
}
|
||||
|
@ -180,7 +184,7 @@ pub mod pallet {
|
|||
impl<T: Config> Pallet<T> {
|
||||
/// Add genesis liquidity for the given account. All accounts that provide liquidity
|
||||
/// will receive the genesis SRI according to their liquidity ratio.
|
||||
pub fn add_coin_liquidity(account: PublicKey, balance: Balance) -> DispatchResult {
|
||||
pub fn add_coin_liquidity(account: PublicKey, balance: ExternalBalance) -> DispatchResult {
|
||||
// check we are still in genesis period
|
||||
if Self::genesis_ended() {
|
||||
Err(Error::<T>::GenesisPeriodEnded)?;
|
||||
|
@ -227,7 +231,7 @@ pub mod pallet {
|
|||
/// If networks is yet to be reached that threshold, None is returned.
|
||||
fn blocks_since_ec_security() -> Option<u64> {
|
||||
let mut min = u64::MAX;
|
||||
for n in NETWORKS {
|
||||
for n in EXTERNAL_NETWORKS {
|
||||
let ec_security_block =
|
||||
EconomicSecurity::<T>::economic_security_block(n)?.saturated_into::<u64>();
|
||||
let current = <frame_system::Pallet<T>>::block_number().saturated_into::<u64>();
|
||||
|
@ -243,11 +247,7 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
fn oraclization_is_done() -> bool {
|
||||
for c in COINS {
|
||||
if c == Coin::Serai {
|
||||
continue;
|
||||
}
|
||||
|
||||
for c in EXTERNAL_COINS {
|
||||
if Oracle::<T>::get(c).is_none() {
|
||||
return false;
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ pub mod pallet {
|
|||
/// Remove the provided genesis liquidity for an account.
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight((0, DispatchClass::Operational))] // TODO
|
||||
pub fn remove_coin_liquidity(origin: OriginFor<T>, balance: Balance) -> DispatchResult {
|
||||
pub fn remove_coin_liquidity(origin: OriginFor<T>, balance: ExternalBalance) -> DispatchResult {
|
||||
let account = ensure_signed(origin)?;
|
||||
let origin = RawOrigin::Signed(GENESIS_LIQUIDITY_ACCOUNT.into());
|
||||
let supply = Supply::<T>::get(balance.coin).ok_or(Error::<T>::NotEnoughLiquidity)?;
|
||||
|
@ -297,7 +297,7 @@ pub mod pallet {
|
|||
|
||||
// remove liquidity from pool
|
||||
let prev_sri = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai);
|
||||
let prev_coin = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin);
|
||||
let prev_coin = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin.into());
|
||||
Dex::<T>::remove_liquidity(
|
||||
origin.clone().into(),
|
||||
balance.coin,
|
||||
|
@ -307,7 +307,8 @@ pub mod pallet {
|
|||
GENESIS_LIQUIDITY_ACCOUNT.into(),
|
||||
)?;
|
||||
let current_sri = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), Coin::Serai);
|
||||
let current_coin = Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin);
|
||||
let current_coin =
|
||||
Coins::<T>::balance(GENESIS_LIQUIDITY_ACCOUNT.into(), balance.coin.into());
|
||||
|
||||
// burn the SRI if necessary
|
||||
// TODO: take into consideration movement between pools.
|
||||
|
@ -333,7 +334,7 @@ pub mod pallet {
|
|||
Coins::<T>::transfer(
|
||||
origin.clone().into(),
|
||||
account,
|
||||
Balance { coin: balance.coin, amount: Amount(coin_out) },
|
||||
Balance { coin: balance.coin.into(), amount: Amount(coin_out) },
|
||||
)?;
|
||||
Coins::<T>::transfer(
|
||||
origin.into(),
|
||||
|
@ -366,7 +367,7 @@ pub mod pallet {
|
|||
Coins::<T>::transfer(
|
||||
origin.into(),
|
||||
account,
|
||||
Balance { coin: balance.coin, amount: Amount(existing.coins) },
|
||||
Balance { coin: balance.coin.into(), amount: Amount(existing.coins) },
|
||||
)?;
|
||||
|
||||
(
|
||||
|
@ -404,10 +405,10 @@ pub mod pallet {
|
|||
ensure_none(origin)?;
|
||||
|
||||
// set their relative values
|
||||
Oracle::<T>::set(Coin::Bitcoin, Some(10u64.pow(Coin::Bitcoin.decimals())));
|
||||
Oracle::<T>::set(Coin::Monero, Some(values.monero));
|
||||
Oracle::<T>::set(Coin::Ether, Some(values.ether));
|
||||
Oracle::<T>::set(Coin::Dai, Some(values.dai));
|
||||
Oracle::<T>::set(ExternalCoin::Bitcoin, Some(10u64.pow(ExternalCoin::Bitcoin.decimals())));
|
||||
Oracle::<T>::set(ExternalCoin::Monero, Some(values.monero));
|
||||
Oracle::<T>::set(ExternalCoin::Ether, Some(values.ether));
|
||||
Oracle::<T>::set(ExternalCoin::Dai, Some(values.dai));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use sp_io::hashing::blake2_256;
|
||||
|
||||
use serai_primitives::{BlockHash, NetworkId};
|
||||
use serai_primitives::*;
|
||||
|
||||
pub use in_instructions_primitives as primitives;
|
||||
use primitives::*;
|
||||
|
@ -23,8 +23,6 @@ pub mod pallet {
|
|||
use sp_runtime::traits::Zero;
|
||||
use sp_core::sr25519::Public;
|
||||
|
||||
use serai_primitives::{Coin, Amount, Balance};
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::{pallet_prelude::*, RawOrigin};
|
||||
|
||||
|
@ -34,7 +32,7 @@ pub mod pallet {
|
|||
};
|
||||
use dex_pallet::{Config as DexConfig, Pallet as Dex};
|
||||
use validator_sets_pallet::{
|
||||
primitives::{Session, ValidatorSet},
|
||||
primitives::{Session, ValidatorSet, ExternalValidatorSet},
|
||||
Config as ValidatorSetsConfig, Pallet as ValidatorSets,
|
||||
};
|
||||
|
||||
|
@ -60,9 +58,9 @@ pub mod pallet {
|
|||
#[pallet::event]
|
||||
#[pallet::generate_deposit(fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
Batch { network: NetworkId, id: u32, block: BlockHash, instructions_hash: [u8; 32] },
|
||||
InstructionFailure { network: NetworkId, id: u32, index: u32 },
|
||||
Halt { network: NetworkId },
|
||||
Batch { network: ExternalNetworkId, id: u32, block: BlockHash, instructions_hash: [u8; 32] },
|
||||
InstructionFailure { network: ExternalNetworkId, id: u32, index: u32 },
|
||||
Halt { network: ExternalNetworkId },
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
|
@ -77,23 +75,24 @@ pub mod pallet {
|
|||
// The ID of the last executed Batch for a network.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn batches)]
|
||||
pub(crate) type LastBatch<T: Config> = StorageMap<_, Identity, NetworkId, u32, OptionQuery>;
|
||||
pub(crate) type LastBatch<T: Config> =
|
||||
StorageMap<_, Identity, ExternalNetworkId, u32, OptionQuery>;
|
||||
|
||||
// The last Serai block in which this validator set included a batch
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn last_batch_block)]
|
||||
pub(crate) type LastBatchBlock<T: Config> =
|
||||
StorageMap<_, Identity, NetworkId, BlockNumberFor<T>, OptionQuery>;
|
||||
StorageMap<_, Identity, ExternalNetworkId, BlockNumberFor<T>, OptionQuery>;
|
||||
|
||||
// Halted networks.
|
||||
#[pallet::storage]
|
||||
pub(crate) type Halted<T: Config> = StorageMap<_, Identity, NetworkId, (), OptionQuery>;
|
||||
pub(crate) type Halted<T: Config> = StorageMap<_, Identity, ExternalNetworkId, (), OptionQuery>;
|
||||
|
||||
// The latest block a network has acknowledged as finalized
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn latest_network_block)]
|
||||
pub(crate) type LatestNetworkBlock<T: Config> =
|
||||
StorageMap<_, Identity, NetworkId, BlockHash, OptionQuery>;
|
||||
StorageMap<_, Identity, ExternalNetworkId, BlockHash, OptionQuery>;
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
// Use a dedicated transaction layer when executing this InInstruction
|
||||
|
@ -102,7 +101,7 @@ pub mod pallet {
|
|||
fn execute(instruction: InInstructionWithBalance) -> Result<(), DispatchError> {
|
||||
match instruction.instruction {
|
||||
InInstruction::Transfer(address) => {
|
||||
Coins::<T>::mint(address.into(), instruction.balance)?;
|
||||
Coins::<T>::mint(address.into(), instruction.balance.into())?;
|
||||
}
|
||||
InInstruction::Dex(call) => {
|
||||
// This will only be initiated by external chain transactions. That is why we only need
|
||||
|
@ -114,11 +113,11 @@ pub mod pallet {
|
|||
let coin = instruction.balance.coin;
|
||||
|
||||
// mint the given coin on the account
|
||||
Coins::<T>::mint(IN_INSTRUCTION_EXECUTOR.into(), instruction.balance)?;
|
||||
Coins::<T>::mint(IN_INSTRUCTION_EXECUTOR.into(), instruction.balance.into())?;
|
||||
|
||||
// swap half of it for SRI
|
||||
let half = instruction.balance.amount.0 / 2;
|
||||
let path = BoundedVec::try_from(vec![coin, Coin::Serai]).unwrap();
|
||||
let path = BoundedVec::try_from(vec![coin.into(), Coin::Serai]).unwrap();
|
||||
Dex::<T>::swap_exact_tokens_for_tokens(
|
||||
origin.clone().into(),
|
||||
path,
|
||||
|
@ -144,13 +143,13 @@ pub mod pallet {
|
|||
// TODO: minimums are set to 1 above to guarantee successful adding liq call.
|
||||
// Ideally we either get this info from user or send the leftovers back to user.
|
||||
// Let's send the leftovers back to user for now.
|
||||
let coin_balance = Coins::<T>::balance(IN_INSTRUCTION_EXECUTOR.into(), coin);
|
||||
let coin_balance = Coins::<T>::balance(IN_INSTRUCTION_EXECUTOR.into(), coin.into());
|
||||
let sri_balance = Coins::<T>::balance(IN_INSTRUCTION_EXECUTOR.into(), Coin::Serai);
|
||||
if coin_balance != Amount(0) {
|
||||
Coins::<T>::transfer_internal(
|
||||
IN_INSTRUCTION_EXECUTOR.into(),
|
||||
address.into(),
|
||||
Balance { coin, amount: coin_balance },
|
||||
Balance { coin: coin.into(), amount: coin_balance },
|
||||
)?;
|
||||
}
|
||||
if sri_balance != Amount(0) {
|
||||
|
@ -171,10 +170,10 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
// mint the given coin on our account
|
||||
Coins::<T>::mint(IN_INSTRUCTION_EXECUTOR.into(), instruction.balance)?;
|
||||
Coins::<T>::mint(IN_INSTRUCTION_EXECUTOR.into(), instruction.balance.into())?;
|
||||
|
||||
// get the path
|
||||
let mut path = vec![instruction.balance.coin, Coin::Serai];
|
||||
let mut path = vec![instruction.balance.coin.into(), Coin::Serai];
|
||||
if !native_coin {
|
||||
path.push(out_balance.coin);
|
||||
}
|
||||
|
@ -210,7 +209,10 @@ pub mod pallet {
|
|||
// TODO: Properly pass data. Replace address with an OutInstruction entirely?
|
||||
data: None,
|
||||
},
|
||||
balance: Balance { coin: out_balance.coin, amount: coin_balance },
|
||||
balance: ExternalBalance {
|
||||
coin: out_balance.coin.try_into().unwrap(),
|
||||
amount: coin_balance,
|
||||
},
|
||||
};
|
||||
Coins::<T>::burn_with_instruction(origin.into(), instruction)?;
|
||||
}
|
||||
|
@ -218,18 +220,18 @@ pub mod pallet {
|
|||
}
|
||||
}
|
||||
InInstruction::GenesisLiquidity(address) => {
|
||||
Coins::<T>::mint(GENESIS_LIQUIDITY_ACCOUNT.into(), instruction.balance)?;
|
||||
Coins::<T>::mint(GENESIS_LIQUIDITY_ACCOUNT.into(), instruction.balance.into())?;
|
||||
GenesisLiq::<T>::add_coin_liquidity(address.into(), instruction.balance)?;
|
||||
}
|
||||
InInstruction::SwapToStakedSRI(address, network) => {
|
||||
Coins::<T>::mint(POL_ACCOUNT.into(), instruction.balance)?;
|
||||
Coins::<T>::mint(POL_ACCOUNT.into(), instruction.balance.into())?;
|
||||
Emissions::<T>::swap_to_staked_sri(address.into(), network, instruction.balance)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn halt(network: NetworkId) -> Result<(), DispatchError> {
|
||||
pub fn halt(network: ExternalNetworkId) -> Result<(), DispatchError> {
|
||||
Halted::<T>::set(network, Some(()));
|
||||
Self::deposit_event(Event::Halt { network });
|
||||
Ok(())
|
||||
|
@ -237,13 +239,13 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
fn keys_for_network<T: Config>(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
) -> Result<(Session, Option<Public>, Option<Public>), InvalidTransaction> {
|
||||
// If there's no session set, and therefore no keys set, then this must be an invalid signature
|
||||
let Some(session) = ValidatorSets::<T>::session(network) else {
|
||||
let Some(session) = ValidatorSets::<T>::session(NetworkId::from(network)) else {
|
||||
Err(InvalidTransaction::BadProof)?
|
||||
};
|
||||
let mut set = ValidatorSet { session, network };
|
||||
let mut set = ExternalValidatorSet { network, session };
|
||||
let latest = ValidatorSets::<T>::keys(set).map(|keys| keys.0);
|
||||
let prior = if set.session.0 != 0 {
|
||||
set.session.0 -= 1;
|
||||
|
@ -303,12 +305,7 @@ pub mod pallet {
|
|||
if batch.batch.encode().len() > MAX_BATCH_SIZE {
|
||||
Err(InvalidTransaction::ExhaustsResources)?;
|
||||
}
|
||||
|
||||
let network = batch.batch.network;
|
||||
// Don't allow the Serai set to publish `Batch`s as-if Serai itself was an external network
|
||||
if network == NetworkId::Serai {
|
||||
Err(InvalidTransaction::Custom(0))?;
|
||||
}
|
||||
|
||||
// verify the signature
|
||||
let (current_session, prior, current) = keys_for_network::<T>(network)?;
|
||||
|
@ -336,7 +333,7 @@ pub mod pallet {
|
|||
// `Batch`s published by the prior key, meaning they are accepting the hand-over.
|
||||
if prior.is_some() && (!valid_by_prior) {
|
||||
ValidatorSets::<T>::retire_set(ValidatorSet {
|
||||
network,
|
||||
network: network.into(),
|
||||
session: Session(current_session.0 - 1),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use sp_std::vec::Vec;
|
|||
use sp_runtime::RuntimeDebug;
|
||||
|
||||
#[rustfmt::skip]
|
||||
use serai_primitives::{BlockHash, Balance, NetworkId, SeraiAddress, ExternalAddress, system_address};
|
||||
use serai_primitives::{BlockHash, Balance, ExternalNetworkId, NetworkId, SeraiAddress, ExternalBalance, ExternalAddress, system_address};
|
||||
|
||||
mod shorthand;
|
||||
pub use shorthand::*;
|
||||
|
@ -97,7 +97,7 @@ pub struct RefundableInInstruction {
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct InInstructionWithBalance {
|
||||
pub instruction: InInstruction,
|
||||
pub balance: Balance,
|
||||
pub balance: ExternalBalance,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||
|
@ -105,7 +105,7 @@ pub struct InInstructionWithBalance {
|
|||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Batch {
|
||||
pub network: NetworkId,
|
||||
pub network: ExternalNetworkId,
|
||||
pub id: u32,
|
||||
pub block: BlockHash,
|
||||
pub instructions: Vec<InInstructionWithBalance>,
|
||||
|
|
|
@ -9,7 +9,7 @@ use serde::{Serialize, Deserialize};
|
|||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::{Coin, Amount, SeraiAddress, ExternalAddress};
|
||||
use serai_primitives::{Amount, ExternalAddress, ExternalCoin, SeraiAddress};
|
||||
|
||||
use coins_primitives::OutInstruction;
|
||||
|
||||
|
@ -25,7 +25,7 @@ pub enum Shorthand {
|
|||
Raw(RefundableInInstruction),
|
||||
Swap {
|
||||
origin: Option<ExternalAddress>,
|
||||
coin: Coin,
|
||||
coin: ExternalCoin,
|
||||
minimum: Amount,
|
||||
out: OutInstruction,
|
||||
},
|
||||
|
|
|
@ -33,6 +33,22 @@ fn devnet_genesis(
|
|||
endowed_accounts: Vec<PublicKey>,
|
||||
) -> RuntimeGenesisConfig {
|
||||
let validators = validators.iter().map(|name| account_from_name(name)).collect::<Vec<_>>();
|
||||
let key_shares = NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::External(ExternalNetworkId::Bitcoin) => {
|
||||
(NetworkId::External(ExternalNetworkId::Bitcoin), Amount(1_000_000 * 10_u64.pow(8)))
|
||||
}
|
||||
NetworkId::External(ExternalNetworkId::Ethereum) => {
|
||||
(NetworkId::External(ExternalNetworkId::Ethereum), Amount(1_000_000 * 10_u64.pow(8)))
|
||||
}
|
||||
NetworkId::External(ExternalNetworkId::Monero) => {
|
||||
(NetworkId::External(ExternalNetworkId::Monero), Amount(100_000 * 10_u64.pow(8)))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
RuntimeGenesisConfig {
|
||||
system: SystemConfig { code: wasm_binary.to_vec(), _config: PhantomData },
|
||||
|
||||
|
@ -47,29 +63,10 @@ fn devnet_genesis(
|
|||
},
|
||||
|
||||
validator_sets: ValidatorSetsConfig {
|
||||
networks: serai_runtime::primitives::NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::Bitcoin => (NetworkId::Bitcoin, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Ethereum => (NetworkId::Ethereum, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Monero => (NetworkId::Monero, Amount(100_000 * 10_u64.pow(8))),
|
||||
})
|
||||
.collect(),
|
||||
participants: validators.clone(),
|
||||
},
|
||||
emissions: EmissionsConfig {
|
||||
networks: serai_runtime::primitives::NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::Bitcoin => (NetworkId::Bitcoin, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Ethereum => (NetworkId::Ethereum, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Monero => (NetworkId::Monero, Amount(100_000 * 10_u64.pow(8))),
|
||||
})
|
||||
.collect(),
|
||||
networks: key_shares.clone(),
|
||||
participants: validators.clone(),
|
||||
},
|
||||
emissions: EmissionsConfig { networks: key_shares, participants: validators.clone() },
|
||||
signals: SignalsConfig::default(),
|
||||
babe: BabeConfig {
|
||||
authorities: validators.iter().map(|validator| ((*validator).into(), 1)).collect(),
|
||||
|
@ -88,6 +85,21 @@ fn testnet_genesis(wasm_binary: &[u8], validators: Vec<&'static str>) -> Runtime
|
|||
.into_iter()
|
||||
.map(|validator| Public::decode(&mut hex::decode(validator).unwrap().as_slice()).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let key_shares = NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::External(ExternalNetworkId::Bitcoin) => {
|
||||
(NetworkId::External(ExternalNetworkId::Bitcoin), Amount(1_000_000 * 10_u64.pow(8)))
|
||||
}
|
||||
NetworkId::External(ExternalNetworkId::Ethereum) => {
|
||||
(NetworkId::External(ExternalNetworkId::Ethereum), Amount(1_000_000 * 10_u64.pow(8)))
|
||||
}
|
||||
NetworkId::External(ExternalNetworkId::Monero) => {
|
||||
(NetworkId::External(ExternalNetworkId::Monero), Amount(100_000 * 10_u64.pow(8)))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(validators.iter().collect::<HashSet<_>>().len(), validators.len());
|
||||
|
||||
|
@ -105,29 +117,10 @@ fn testnet_genesis(wasm_binary: &[u8], validators: Vec<&'static str>) -> Runtime
|
|||
},
|
||||
|
||||
validator_sets: ValidatorSetsConfig {
|
||||
networks: serai_runtime::primitives::NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::Bitcoin => (NetworkId::Bitcoin, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Ethereum => (NetworkId::Ethereum, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Monero => (NetworkId::Monero, Amount(100_000 * 10_u64.pow(8))),
|
||||
})
|
||||
.collect(),
|
||||
participants: validators.clone(),
|
||||
},
|
||||
emissions: EmissionsConfig {
|
||||
networks: serai_runtime::primitives::NETWORKS
|
||||
.iter()
|
||||
.map(|network| match network {
|
||||
NetworkId::Serai => (NetworkId::Serai, Amount(50_000 * 10_u64.pow(8))),
|
||||
NetworkId::Bitcoin => (NetworkId::Bitcoin, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Ethereum => (NetworkId::Ethereum, Amount(1_000_000 * 10_u64.pow(8))),
|
||||
NetworkId::Monero => (NetworkId::Monero, Amount(100_000 * 10_u64.pow(8))),
|
||||
})
|
||||
.collect(),
|
||||
networks: key_shares.clone(),
|
||||
participants: validators.clone(),
|
||||
},
|
||||
emissions: EmissionsConfig { networks: key_shares, participants: validators.clone() },
|
||||
signals: SignalsConfig::default(),
|
||||
babe: BabeConfig {
|
||||
authorities: validators.iter().map(|validator| ((*validator).into(), 1)).collect(),
|
||||
|
|
|
@ -28,6 +28,7 @@ sp-application-crypto = { git = "https://github.com/serai-dex/substrate", defaul
|
|||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
||||
|
||||
|
@ -35,7 +36,7 @@ frame-support = { git = "https://github.com/serai-dex/substrate", default-featur
|
|||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||
|
||||
[features]
|
||||
std = ["zeroize", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "frame-support/std"]
|
||||
std = ["zeroize", "scale/std", "borsh?/std", "serde?/std", "scale-info/std", "sp-core/std", "sp-runtime/std", "sp-std/std", "frame-support/std"]
|
||||
borsh = ["dep:borsh"]
|
||||
serde = ["dep:serde"]
|
||||
default = ["std"]
|
||||
|
|
|
@ -29,6 +29,8 @@ pub type SubstrateAmount = u64;
|
|||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Amount(pub SubstrateAmount);
|
||||
|
||||
// TODO: these impl shouldn't panic and return error to be dealt with.
|
||||
// Otherwise we might have a panic that stops the network.
|
||||
impl Add for Amount {
|
||||
type Output = Amount;
|
||||
fn add(self, other: Amount) -> Amount {
|
||||
|
|
|
@ -11,7 +11,7 @@ use serde::{Serialize, Deserialize};
|
|||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use crate::{Coin, Amount};
|
||||
use crate::{Amount, Coin, ExternalCoin};
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
|
@ -23,6 +23,34 @@ pub struct Balance {
|
|||
pub amount: Amount,
|
||||
}
|
||||
|
||||
/// The type used for balances (a Coin and Balance).
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalBalance {
|
||||
pub coin: ExternalCoin,
|
||||
pub amount: Amount,
|
||||
}
|
||||
|
||||
impl From<ExternalBalance> for Balance {
|
||||
fn from(balance: ExternalBalance) -> Self {
|
||||
Balance { coin: balance.coin.into(), amount: balance.amount }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Balance> for ExternalBalance {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(balance: Balance) -> Result<Self, Self::Error> {
|
||||
match balance.coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(coin) => Ok(ExternalBalance { coin, amount: balance.amount }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: these impl either should be removed or return errors in case of overflows
|
||||
impl Add<Amount> for Balance {
|
||||
type Output = Balance;
|
||||
fn add(self, other: Amount) -> Balance {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(feature = "std")]
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale::{Decode, Encode, EncodeLike, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
|
@ -10,54 +10,342 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_core::{ConstU32, bounded::BoundedVec};
|
||||
use sp_std::{vec, vec::Vec};
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
use crate::{borsh_serialize_bounded_vec, borsh_deserialize_bounded_vec};
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, PartialOrd, Ord, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
/// The type used to identify external networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum NetworkId {
|
||||
Serai,
|
||||
pub enum ExternalNetworkId {
|
||||
Bitcoin,
|
||||
Ethereum,
|
||||
Monero,
|
||||
}
|
||||
impl NetworkId {
|
||||
pub fn coins(&self) -> &'static [Coin] {
|
||||
|
||||
impl Encode for ExternalNetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Serai => &[Coin::Serai],
|
||||
Self::Bitcoin => &[Coin::Bitcoin],
|
||||
Self::Ethereum => &[Coin::Ether, Coin::Dai],
|
||||
Self::Monero => &[Coin::Monero],
|
||||
ExternalNetworkId::Bitcoin => vec![1],
|
||||
ExternalNetworkId::Ethereum => vec![2],
|
||||
ExternalNetworkId::Monero => vec![3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const NETWORKS: [NetworkId; 4] =
|
||||
[NetworkId::Serai, NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero];
|
||||
impl Decode for ExternalNetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
1 => Ok(Self::Bitcoin),
|
||||
2 => Ok(Self::Ethereum),
|
||||
3 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const COINS: [Coin; 5] = [Coin::Serai, Coin::Bitcoin, Coin::Ether, Coin::Dai, Coin::Monero];
|
||||
impl MaxEncodedLen for ExternalNetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encode, Decode, MaxEncodedLen, TypeInfo,
|
||||
)]
|
||||
impl EncodeLike for ExternalNetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalNetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalNetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalNetworkId::decode(&mut kind.as_slice())
|
||||
.map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify networks.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Coin {
|
||||
pub enum NetworkId {
|
||||
Serai,
|
||||
External(ExternalNetworkId),
|
||||
}
|
||||
|
||||
impl Encode for NetworkId {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
NetworkId::Serai => vec![0],
|
||||
NetworkId::External(network) => network.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for NetworkId {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalNetworkId::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for NetworkId {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for NetworkId {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for NetworkId {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for NetworkId {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
NetworkId::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalNetworkId {
|
||||
pub fn coins(&self) -> Vec<ExternalCoin> {
|
||||
match self {
|
||||
Self::Bitcoin => vec![ExternalCoin::Bitcoin],
|
||||
Self::Ethereum => vec![ExternalCoin::Ether, ExternalCoin::Dai],
|
||||
Self::Monero => vec![ExternalCoin::Monero],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkId {
|
||||
pub fn coins(&self) -> Vec<Coin> {
|
||||
match self {
|
||||
Self::Serai => vec![Coin::Serai],
|
||||
Self::External(network) => {
|
||||
network.coins().into_iter().map(core::convert::Into::into).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalNetworkId> for NetworkId {
|
||||
fn from(network: ExternalNetworkId) -> Self {
|
||||
NetworkId::External(network)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<NetworkId> for ExternalNetworkId {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(network: NetworkId) -> Result<Self, Self::Error> {
|
||||
match network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(n) => Ok(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const EXTERNAL_NETWORKS: [ExternalNetworkId; 3] =
|
||||
[ExternalNetworkId::Bitcoin, ExternalNetworkId::Ethereum, ExternalNetworkId::Monero];
|
||||
|
||||
pub const NETWORKS: [NetworkId; 4] = [
|
||||
NetworkId::Serai,
|
||||
NetworkId::External(ExternalNetworkId::Bitcoin),
|
||||
NetworkId::External(ExternalNetworkId::Ethereum),
|
||||
NetworkId::External(ExternalNetworkId::Monero),
|
||||
];
|
||||
|
||||
pub const EXTERNAL_COINS: [ExternalCoin; 4] =
|
||||
[ExternalCoin::Bitcoin, ExternalCoin::Ether, ExternalCoin::Dai, ExternalCoin::Monero];
|
||||
|
||||
pub const COINS: [Coin; 5] = [
|
||||
Coin::Serai,
|
||||
Coin::External(ExternalCoin::Bitcoin),
|
||||
Coin::External(ExternalCoin::Ether),
|
||||
Coin::External(ExternalCoin::Dai),
|
||||
Coin::External(ExternalCoin::Monero),
|
||||
];
|
||||
|
||||
/// The type used to identify external coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ExternalCoin {
|
||||
Bitcoin,
|
||||
Ether,
|
||||
Dai,
|
||||
Monero,
|
||||
}
|
||||
|
||||
impl Encode for ExternalCoin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => vec![4],
|
||||
ExternalCoin::Ether => vec![5],
|
||||
ExternalCoin::Dai => vec![6],
|
||||
ExternalCoin::Monero => vec![7],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for ExternalCoin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
4 => Ok(Self::Bitcoin),
|
||||
5 => Ok(Self::Ether),
|
||||
6 => Ok(Self::Dai),
|
||||
7 => Ok(Self::Monero),
|
||||
_ => Err(scale::Error::from("invalid format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MaxEncodedLen for ExternalCoin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for ExternalCoin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for ExternalCoin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for ExternalCoin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
ExternalCoin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type used to identify coins.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Coin {
|
||||
Serai,
|
||||
External(ExternalCoin),
|
||||
}
|
||||
|
||||
impl Encode for Coin {
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Coin::Serai => vec![0],
|
||||
Coin::External(ec) => ec.encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for Coin {
|
||||
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
|
||||
let kind = input.read_byte()?;
|
||||
match kind {
|
||||
0 => Ok(Self::Serai),
|
||||
_ => Ok(ExternalCoin::decode(&mut [kind].as_slice())?.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxEncodedLen for Coin {
|
||||
fn max_encoded_len() -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeLike for Coin {}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshSerialize for Coin {
|
||||
fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_all(&self.encode())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "borsh")]
|
||||
impl BorshDeserialize for Coin {
|
||||
fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
|
||||
let mut kind = [0; 1];
|
||||
reader.read_exact(&mut kind)?;
|
||||
Coin::decode(&mut kind.as_slice()).map_err(|_| std::io::Error::other("invalid format"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExternalCoin> for Coin {
|
||||
fn from(coin: ExternalCoin) -> Self {
|
||||
Coin::External(coin)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for ExternalCoin {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(coin: Coin) -> Result<Self, Self::Error> {
|
||||
match coin {
|
||||
Coin::Serai => Err(())?,
|
||||
Coin::External(c) => Ok(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalCoin {
|
||||
pub fn network(&self) -> ExternalNetworkId {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => ExternalNetworkId::Bitcoin,
|
||||
ExternalCoin::Ether | ExternalCoin::Dai => ExternalNetworkId::Ethereum,
|
||||
ExternalCoin::Monero => ExternalNetworkId::Monero,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "Bitcoin",
|
||||
ExternalCoin::Ether => "Ether",
|
||||
ExternalCoin::Dai => "Dai Stablecoin",
|
||||
ExternalCoin::Monero => "Monero",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
ExternalCoin::Bitcoin => "BTC",
|
||||
ExternalCoin::Ether => "ETH",
|
||||
ExternalCoin::Dai => "DAI",
|
||||
ExternalCoin::Monero => "XMR",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
ExternalCoin::Bitcoin | ExternalCoin::Ether | ExternalCoin::Dai => 8,
|
||||
ExternalCoin::Monero => 12,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
pub fn native() -> Coin {
|
||||
Coin::Serai
|
||||
|
@ -66,37 +354,28 @@ impl Coin {
|
|||
pub fn network(&self) -> NetworkId {
|
||||
match self {
|
||||
Coin::Serai => NetworkId::Serai,
|
||||
Coin::Bitcoin => NetworkId::Bitcoin,
|
||||
Coin::Ether | Coin::Dai => NetworkId::Ethereum,
|
||||
Coin::Monero => NetworkId::Monero,
|
||||
Coin::External(c) => c.network().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "Serai",
|
||||
Coin::Bitcoin => "Bitcoin",
|
||||
Coin::Ether => "Ether",
|
||||
Coin::Dai => "Dai Stablecoin",
|
||||
Coin::Monero => "Monero",
|
||||
Coin::External(c) => c.name(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
Coin::Serai => "SRI",
|
||||
Coin::Bitcoin => "BTC",
|
||||
Coin::Ether => "ETH",
|
||||
Coin::Dai => "DAI",
|
||||
Coin::Monero => "XMR",
|
||||
Coin::External(c) => c.symbol(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decimals(&self) -> u32 {
|
||||
match self {
|
||||
// Ether and DAI have 18 decimals, yet we only track 8 in order to fit them within u64s
|
||||
Coin::Serai | Coin::Bitcoin | Coin::Ether | Coin::Dai => 8,
|
||||
Coin::Monero => 12,
|
||||
Coin::Serai => 8,
|
||||
Coin::External(c) => c.decimals(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,8 +53,9 @@ use sp_runtime::{
|
|||
|
||||
#[allow(unused_imports)]
|
||||
use primitives::{
|
||||
NetworkId, PublicKey, AccountLookup, SubstrateAmount, Coin, NETWORKS, MEDIAN_PRICE_WINDOW_LENGTH,
|
||||
HOURS, DAYS, MINUTES, TARGET_BLOCK_TIME, BLOCK_SIZE, FAST_EPOCH_DURATION,
|
||||
NetworkId, PublicKey, AccountLookup, SubstrateAmount, Coin, EXTERNAL_NETWORKS,
|
||||
MEDIAN_PRICE_WINDOW_LENGTH, HOURS, DAYS, MINUTES, TARGET_BLOCK_TIME, BLOCK_SIZE,
|
||||
FAST_EPOCH_DURATION,
|
||||
};
|
||||
|
||||
use support::{
|
||||
|
@ -570,10 +571,7 @@ sp_api::impl_runtime_apis! {
|
|||
.map(|(id, _)| id.into_inner().0)
|
||||
.collect::<hashbrown::HashSet<_>>();
|
||||
let mut all = serai_validators;
|
||||
for network in NETWORKS {
|
||||
if network == NetworkId::Serai {
|
||||
continue;
|
||||
}
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
// Returning the latest-decided, not latest and active, means the active set
|
||||
// may fail to peer find if there isn't sufficient overlap. If a large amount reboot,
|
||||
// forcing some validators to successfully peer find in order for the threshold to become
|
||||
|
@ -581,7 +579,7 @@ sp_api::impl_runtime_apis! {
|
|||
//
|
||||
// This is assumed not to matter in real life, yet an interesting note.
|
||||
let participants =
|
||||
ValidatorSets::participants_for_latest_decided_set(network)
|
||||
ValidatorSets::participants_for_latest_decided_set(NetworkId::from(network))
|
||||
.map_or(vec![], BoundedVec::into_inner);
|
||||
for (participant, _) in participants {
|
||||
all.insert(participant.0);
|
||||
|
@ -610,25 +608,25 @@ sp_api::impl_runtime_apis! {
|
|||
|
||||
impl dex::DexApi<Block> for Runtime {
|
||||
fn quote_price_exact_tokens_for_tokens(
|
||||
asset1: Coin,
|
||||
asset2: Coin,
|
||||
coin1: Coin,
|
||||
coin2: Coin,
|
||||
amount: SubstrateAmount,
|
||||
include_fee: bool
|
||||
) -> Option<SubstrateAmount> {
|
||||
Dex::quote_price_exact_tokens_for_tokens(asset1, asset2, amount, include_fee)
|
||||
Dex::quote_price_exact_tokens_for_tokens(coin1, coin2, amount, include_fee)
|
||||
}
|
||||
|
||||
fn quote_price_tokens_for_exact_tokens(
|
||||
asset1: Coin,
|
||||
asset2: Coin,
|
||||
coin1: Coin,
|
||||
coin2: Coin,
|
||||
amount: SubstrateAmount,
|
||||
include_fee: bool
|
||||
) -> Option<SubstrateAmount> {
|
||||
Dex::quote_price_tokens_for_exact_tokens(asset1, asset2, amount, include_fee)
|
||||
Dex::quote_price_tokens_for_exact_tokens(coin1, coin2, amount, include_fee)
|
||||
}
|
||||
|
||||
fn get_reserves(asset1: Coin, asset2: Coin) -> Option<(SubstrateAmount, SubstrateAmount)> {
|
||||
Dex::get_reserves(&asset1, &asset2).ok()
|
||||
fn get_reserves(coin1: Coin, coin2: Coin) -> Option<(SubstrateAmount, SubstrateAmount)> {
|
||||
Dex::get_reserves(&coin1, &coin2).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use scale::{Encode, Decode, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
|
||||
use serai_primitives::NetworkId;
|
||||
use serai_primitives::ExternalNetworkId;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, TypeInfo)]
|
||||
#[cfg_attr(feature = "std", derive(zeroize::Zeroize))]
|
||||
|
@ -13,5 +13,5 @@ use serai_primitives::NetworkId;
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum SignalId {
|
||||
Retirement([u8; 32]),
|
||||
Halt(NetworkId),
|
||||
Halt(ExternalNetworkId),
|
||||
}
|
||||
|
|
|
@ -316,11 +316,13 @@ pub mod pallet {
|
|||
/// The generated key pair for a given validator set instance.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn keys)]
|
||||
pub type Keys<T: Config> = StorageMap<_, Twox64Concat, ValidatorSet, KeyPair, OptionQuery>;
|
||||
pub type Keys<T: Config> =
|
||||
StorageMap<_, Twox64Concat, ExternalValidatorSet, KeyPair, OptionQuery>;
|
||||
|
||||
/// The key for validator sets which can (and still need to) publish their slash reports.
|
||||
#[pallet::storage]
|
||||
pub type PendingSlashReport<T: Config> = StorageMap<_, Identity, NetworkId, Public, OptionQuery>;
|
||||
pub type PendingSlashReport<T: Config> =
|
||||
StorageMap<_, Identity, ExternalNetworkId, Public, OptionQuery>;
|
||||
|
||||
/// Disabled validators.
|
||||
#[pallet::storage]
|
||||
|
@ -343,7 +345,7 @@ pub mod pallet {
|
|||
removed: T::AccountId,
|
||||
},
|
||||
KeyGen {
|
||||
set: ValidatorSet,
|
||||
set: ExternalValidatorSet,
|
||||
key_pair: KeyPair,
|
||||
},
|
||||
AcceptedHandover {
|
||||
|
@ -600,14 +602,16 @@ pub mod pallet {
|
|||
amount: Amount,
|
||||
) -> Result<bool, DispatchError> {
|
||||
// Check it's safe to decrease this set's stake by this amount
|
||||
let new_total_staked = Self::total_allocated_stake(network)
|
||||
.unwrap()
|
||||
.0
|
||||
.checked_sub(amount.0)
|
||||
.ok_or(Error::<T>::NotEnoughAllocated)?;
|
||||
let required_stake = Self::required_stake_for_network(network);
|
||||
if new_total_staked < required_stake {
|
||||
Err(Error::<T>::DeallocationWouldRemoveEconomicSecurity)?;
|
||||
if let NetworkId::External(n) = network {
|
||||
let new_total_staked = Self::total_allocated_stake(NetworkId::from(n))
|
||||
.unwrap()
|
||||
.0
|
||||
.checked_sub(amount.0)
|
||||
.ok_or(Error::<T>::NotEnoughAllocated)?;
|
||||
let required_stake = Self::required_stake_for_network(n);
|
||||
if new_total_staked < required_stake {
|
||||
Err(Error::<T>::DeallocationWouldRemoveEconomicSecurity)?;
|
||||
}
|
||||
}
|
||||
|
||||
let old_allocation =
|
||||
|
@ -690,20 +694,23 @@ pub mod pallet {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Handover is automatically complete for Serai as it doesn't have a handover protocol
|
||||
if network == NetworkId::Serai {
|
||||
let NetworkId::External(n) = network else {
|
||||
// Handover is automatically complete for Serai as it doesn't have a handover protocol
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// The current session must have set keys for its handover to be completed
|
||||
if !Keys::<T>::contains_key(ValidatorSet { network, session }) {
|
||||
if !Keys::<T>::contains_key(ExternalValidatorSet { network: n, session }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This must be the first session (which has set keys) OR the prior session must have been
|
||||
// retired (signified by its keys no longer being present)
|
||||
(session.0 == 0) ||
|
||||
(!Keys::<T>::contains_key(ValidatorSet { network, session: Session(session.0 - 1) }))
|
||||
(!Keys::<T>::contains_key(ExternalValidatorSet {
|
||||
network: n,
|
||||
session: Session(session.0 - 1),
|
||||
}))
|
||||
}
|
||||
|
||||
fn new_session() {
|
||||
|
@ -733,19 +740,23 @@ pub mod pallet {
|
|||
// TODO: This is called retire_set, yet just starts retiring the set
|
||||
// Update the nomenclature within this function
|
||||
pub fn retire_set(set: ValidatorSet) {
|
||||
// If the prior prior set didn't report, emit they're retired now
|
||||
if PendingSlashReport::<T>::get(set.network).is_some() {
|
||||
Self::deposit_event(Event::SetRetired {
|
||||
set: ValidatorSet { network: set.network, session: Session(set.session.0 - 1) },
|
||||
});
|
||||
}
|
||||
|
||||
// Serai doesn't set keys and network slashes are handled by BABE/GRANDPA
|
||||
if set.network != NetworkId::Serai {
|
||||
if let NetworkId::External(n) = set.network {
|
||||
// If the prior prior set didn't report, emit they're retired now
|
||||
if PendingSlashReport::<T>::get(n).is_some() {
|
||||
Self::deposit_event(Event::SetRetired {
|
||||
set: ValidatorSet { network: set.network, session: Session(set.session.0 - 1) },
|
||||
});
|
||||
}
|
||||
|
||||
// This overwrites the prior value as the prior to-report set's stake presumably just
|
||||
// unlocked, making their report unenforceable
|
||||
let keys = Keys::<T>::take(set).unwrap();
|
||||
PendingSlashReport::<T>::set(set.network, Some(keys.0));
|
||||
let keys =
|
||||
Keys::<T>::take(ExternalValidatorSet { network: n, session: set.session }).unwrap();
|
||||
PendingSlashReport::<T>::set(n, Some(keys.0));
|
||||
} else {
|
||||
// emit the event for serai network
|
||||
Self::deposit_event(Event::SetRetired { set });
|
||||
}
|
||||
|
||||
// We're retiring this set because the set after it accepted the handover
|
||||
|
@ -817,7 +828,7 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
/// Returns the required stake in terms SRI for a given `Balance`.
|
||||
pub fn required_stake(balance: &Balance) -> SubstrateAmount {
|
||||
pub fn required_stake(balance: &ExternalBalance) -> SubstrateAmount {
|
||||
use dex_pallet::HigherPrecisionBalance;
|
||||
|
||||
// This is inclusive to an increase in accuracy
|
||||
|
@ -840,11 +851,11 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
/// Returns the current total required stake for a given `network`.
|
||||
pub fn required_stake_for_network(network: NetworkId) -> SubstrateAmount {
|
||||
pub fn required_stake_for_network(network: ExternalNetworkId) -> SubstrateAmount {
|
||||
let mut total_required = 0;
|
||||
for coin in network.coins() {
|
||||
let supply = Coins::<T>::supply(coin);
|
||||
total_required += Self::required_stake(&Balance { coin: *coin, amount: Amount(supply) });
|
||||
let supply = Coins::<T>::supply(Coin::from(coin));
|
||||
total_required += Self::required_stake(&ExternalBalance { coin, amount: Amount(supply) });
|
||||
}
|
||||
total_required
|
||||
}
|
||||
|
@ -940,7 +951,7 @@ pub mod pallet {
|
|||
#[pallet::weight(0)] // TODO
|
||||
pub fn set_keys(
|
||||
origin: OriginFor<T>,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
removed_participants: BoundedVec<Public, ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
key_pair: KeyPair,
|
||||
signature: Signature,
|
||||
|
@ -951,8 +962,8 @@ pub mod pallet {
|
|||
// (called by pre_dispatch) checks it
|
||||
let _ = signature;
|
||||
|
||||
let session = Self::session(network).unwrap();
|
||||
let set = ValidatorSet { network, session };
|
||||
let session = Self::session(NetworkId::from(network)).unwrap();
|
||||
let set = ExternalValidatorSet { network, session };
|
||||
|
||||
Keys::<T>::set(set, Some(key_pair.clone()));
|
||||
|
||||
|
@ -960,7 +971,7 @@ pub mod pallet {
|
|||
// We generally set TotalAllocatedStake when the prior set retires, and the new set is fully
|
||||
// active and liable. Since this is the first set, there is no prior set to wait to retire
|
||||
if session == Session(0) {
|
||||
Self::set_total_allocated_stake(network);
|
||||
Self::set_total_allocated_stake(NetworkId::from(network));
|
||||
}
|
||||
|
||||
// This does not remove from TotalAllocatedStake or InSet in order to:
|
||||
|
@ -970,7 +981,7 @@ pub mod pallet {
|
|||
// 2) Not allow parties removed to immediately deallocate, per commentary on deallocation
|
||||
// scheduling (https://github.com/serai-dex/serai/issues/394).
|
||||
for removed in removed_participants {
|
||||
Self::deposit_event(Event::ParticipantRemoved { set, removed });
|
||||
Self::deposit_event(Event::ParticipantRemoved { set: set.into(), removed });
|
||||
}
|
||||
Self::deposit_event(Event::KeyGen { set, key_pair });
|
||||
|
||||
|
@ -981,7 +992,7 @@ pub mod pallet {
|
|||
#[pallet::weight(0)] // TODO
|
||||
pub fn report_slashes(
|
||||
origin: OriginFor<T>,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
slashes: BoundedVec<(Public, u32), ConstU32<{ MAX_KEY_SHARES_PER_SET / 3 }>>,
|
||||
signature: Signature,
|
||||
) -> DispatchResult {
|
||||
|
@ -996,7 +1007,10 @@ pub mod pallet {
|
|||
|
||||
// Emit set retireed
|
||||
Pallet::<T>::deposit_event(Event::SetRetired {
|
||||
set: ValidatorSet { network, session: Session(Self::session(network).unwrap().0 - 1) },
|
||||
set: ValidatorSet {
|
||||
network: network.into(),
|
||||
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
@ -1062,17 +1076,12 @@ pub mod pallet {
|
|||
Call::set_keys { network, ref removed_participants, ref key_pair, ref signature } => {
|
||||
let network = *network;
|
||||
|
||||
// Don't allow the Serai set to set_keys, as they have no reason to do so
|
||||
if network == NetworkId::Serai {
|
||||
Err(InvalidTransaction::Custom(0))?;
|
||||
}
|
||||
|
||||
// Confirm this set has a session
|
||||
let Some(current_session) = Self::session(network) else {
|
||||
let Some(current_session) = Self::session(NetworkId::from(network)) else {
|
||||
Err(InvalidTransaction::Custom(1))?
|
||||
};
|
||||
|
||||
let set = ValidatorSet { network, session: current_session };
|
||||
let set = ExternalValidatorSet { network, session: current_session };
|
||||
|
||||
// Confirm it has yet to set keys
|
||||
if Keys::<T>::get(set).is_some() {
|
||||
|
@ -1081,7 +1090,7 @@ pub mod pallet {
|
|||
|
||||
// This is a needed precondition as this uses storage variables for the latest decided
|
||||
// session on this assumption
|
||||
assert_eq!(Pallet::<T>::latest_decided_session(network), Some(current_session));
|
||||
assert_eq!(Pallet::<T>::latest_decided_session(network.into()), Some(current_session));
|
||||
|
||||
// This does not slash the removed participants as that'll be done at the end of the
|
||||
// set's lifetime
|
||||
|
@ -1094,15 +1103,15 @@ pub mod pallet {
|
|||
removed.insert(participant.0);
|
||||
}
|
||||
|
||||
let participants =
|
||||
Participants::<T>::get(network).expect("session existed without participants");
|
||||
let participants = Participants::<T>::get(NetworkId::from(network))
|
||||
.expect("session existed without participants");
|
||||
|
||||
let mut all_key_shares = 0;
|
||||
let mut signers = vec![];
|
||||
let mut signing_key_shares = 0;
|
||||
for participant in participants {
|
||||
let participant = participant.0;
|
||||
let shares = InSet::<T>::get(network, participant)
|
||||
let shares = InSet::<T>::get(NetworkId::from(network), participant)
|
||||
.expect("participant from Participants wasn't InSet");
|
||||
all_key_shares += shares;
|
||||
|
||||
|
@ -1124,7 +1133,7 @@ pub mod pallet {
|
|||
// Verify the signature with the MuSig key of the signers
|
||||
// We theoretically don't need set_keys_message to bind to removed_participants, as the
|
||||
// key we're signing with effectively already does so, yet there's no reason not to
|
||||
if !musig_key(set, &signers)
|
||||
if !musig_key(set.into(), &signers)
|
||||
.verify(&set_keys_message(&set, removed_participants, key_pair), signature)
|
||||
{
|
||||
Err(InvalidTransaction::BadProof)?;
|
||||
|
@ -1138,17 +1147,16 @@ pub mod pallet {
|
|||
}
|
||||
Call::report_slashes { network, ref slashes, ref signature } => {
|
||||
let network = *network;
|
||||
// Don't allow Serai to publish a slash report as BABE/GRANDPA handles slashes directly
|
||||
if network == NetworkId::Serai {
|
||||
Err(InvalidTransaction::Custom(0))?;
|
||||
}
|
||||
let Some(key) = PendingSlashReport::<T>::take(network) else {
|
||||
// Assumed already published
|
||||
Err(InvalidTransaction::Stale)?
|
||||
};
|
||||
|
||||
// There must have been a previous session is PendingSlashReport is populated
|
||||
let set =
|
||||
ValidatorSet { network, session: Session(Self::session(network).unwrap().0 - 1) };
|
||||
let set = ExternalValidatorSet {
|
||||
network,
|
||||
session: Session(Self::session(NetworkId::from(network)).unwrap().0 - 1),
|
||||
};
|
||||
if !key.verify(&report_slashes_message(&set, slashes), signature) {
|
||||
Err(InvalidTransaction::BadProof)?;
|
||||
}
|
||||
|
@ -1173,13 +1181,14 @@ pub mod pallet {
|
|||
}
|
||||
|
||||
impl<T: Config> AllowMint for Pallet<T> {
|
||||
fn is_allowed(balance: &Balance) -> bool {
|
||||
fn is_allowed(balance: &ExternalBalance) -> bool {
|
||||
// get the required stake
|
||||
let current_required = Self::required_stake_for_network(balance.coin.network());
|
||||
let new_required = current_required + Self::required_stake(balance);
|
||||
|
||||
// get the total stake for the network & compare.
|
||||
let staked = Self::total_allocated_stake(balance.coin.network()).unwrap_or(Amount(0));
|
||||
let staked =
|
||||
Self::total_allocated_stake(NetworkId::from(balance.coin.network())).unwrap_or(Amount(0));
|
||||
staked.0 >= new_required
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use sp_core::{ConstU32, sr25519::Public, bounded::BoundedVec};
|
|||
#[cfg(not(feature = "std"))]
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
use serai_primitives::NetworkId;
|
||||
use serai_primitives::{ExternalNetworkId, NetworkId};
|
||||
|
||||
/// The maximum amount of key shares per set.
|
||||
pub const MAX_KEY_SHARES_PER_SET: u32 = 150;
|
||||
|
@ -43,6 +43,33 @@ pub struct ValidatorSet {
|
|||
pub network: NetworkId,
|
||||
}
|
||||
|
||||
/// The type used to identify a specific validator set during a specific session.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||
#[cfg_attr(feature = "std", derive(Zeroize))]
|
||||
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct ExternalValidatorSet {
|
||||
pub session: Session,
|
||||
pub network: ExternalNetworkId,
|
||||
}
|
||||
|
||||
impl From<ExternalValidatorSet> for ValidatorSet {
|
||||
fn from(set: ExternalValidatorSet) -> Self {
|
||||
ValidatorSet { session: set.session, network: set.network.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ValidatorSet> for ExternalValidatorSet {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(set: ValidatorSet) -> Result<Self, Self::Error> {
|
||||
match set.network {
|
||||
NetworkId::Serai => Err(())?,
|
||||
NetworkId::External(network) => Ok(ExternalValidatorSet { session: set.session, network }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MaxKeyLen = ConstU32<MAX_KEY_LEN>;
|
||||
/// The type representing a Key from an external network.
|
||||
pub type ExternalKey = BoundedVec<u8, MaxKeyLen>;
|
||||
|
@ -100,14 +127,14 @@ pub fn musig_key(set: ValidatorSet, set_keys: &[Public]) -> Public {
|
|||
|
||||
/// The message for the set_keys signature.
|
||||
pub fn set_keys_message(
|
||||
set: &ValidatorSet,
|
||||
set: &ExternalValidatorSet,
|
||||
removed_participants: &[Public],
|
||||
key_pair: &KeyPair,
|
||||
) -> Vec<u8> {
|
||||
(b"ValidatorSets-set_keys", set, removed_participants, key_pair).encode()
|
||||
}
|
||||
|
||||
pub fn report_slashes_message(set: &ValidatorSet, slashes: &[(Public, u32)]) -> Vec<u8> {
|
||||
pub fn report_slashes_message(set: &ExternalValidatorSet, slashes: &[(Public, u32)]) -> Vec<u8> {
|
||||
(b"ValidatorSets-report_slashes", set, slashes).encode()
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use ciphersuite::{
|
|||
Ciphersuite, Ristretto,
|
||||
};
|
||||
|
||||
use serai_client::primitives::NetworkId;
|
||||
use serai_client::primitives::ExternalNetworkId;
|
||||
|
||||
use messages::{
|
||||
coordinator::{SubstrateSignableId, SubstrateSignId, cosign_block_msg},
|
||||
|
@ -108,7 +108,7 @@ pub struct Handles {
|
|||
}
|
||||
|
||||
pub struct Processor {
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
|
||||
serai_rpc: String,
|
||||
#[allow(unused)]
|
||||
|
@ -132,7 +132,7 @@ impl Drop for Processor {
|
|||
impl Processor {
|
||||
pub async fn new(
|
||||
raw_i: u8,
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
ops: &DockerOperations,
|
||||
handles: Handles,
|
||||
processor_key: <Ristretto as Ciphersuite>::F,
|
||||
|
|
|
@ -16,7 +16,7 @@ use dkg::Participant;
|
|||
use scale::Encode;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NetworkId, BlockHash, Signature},
|
||||
primitives::{BlockHash, Signature},
|
||||
in_instructions::{
|
||||
primitives::{Batch, SignedBatch, batch_message},
|
||||
InInstructionsEvent,
|
||||
|
@ -274,7 +274,7 @@ async fn batch_test() {
|
|||
Session(0),
|
||||
&substrate_key,
|
||||
Batch {
|
||||
network: NetworkId::Bitcoin,
|
||||
network: ExternalNetworkId::Bitcoin,
|
||||
id: 0,
|
||||
block: BlockHash([0x22; 32]),
|
||||
instructions: vec![],
|
||||
|
|
|
@ -13,9 +13,8 @@ use ciphersuite::{
|
|||
use dkg::ThresholdParams;
|
||||
|
||||
use serai_client::{
|
||||
primitives::NetworkId,
|
||||
validator_sets::primitives::{ExternalValidatorSet, KeyPair, Session},
|
||||
Public,
|
||||
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
||||
};
|
||||
use messages::{key_gen::KeyGenId, CoordinatorMessage};
|
||||
|
||||
|
@ -28,7 +27,7 @@ pub async fn key_gen<C: Ciphersuite>(
|
|||
let coordinators = processors.len();
|
||||
let mut participant_is = vec![];
|
||||
|
||||
let set = ValidatorSet { session, network: NetworkId::Bitcoin };
|
||||
let set = ExternalValidatorSet { session, network: ExternalNetworkId::Bitcoin };
|
||||
let id = KeyGenId { session: set.session, attempt: 0 };
|
||||
|
||||
for (i, processor) in processors.iter_mut().enumerate() {
|
||||
|
|
|
@ -104,7 +104,7 @@ pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
|||
handles.insert(name, handle);
|
||||
}
|
||||
|
||||
let processor_key = message_queue_keys[&NetworkId::Bitcoin];
|
||||
let processor_key = message_queue_keys[&ExternalNetworkId::Bitcoin];
|
||||
|
||||
coordinators.push((
|
||||
Handles {
|
||||
|
@ -198,7 +198,7 @@ pub(crate) async fn new_test(test_body: impl TestBody, fast_epoch: bool) {
|
|||
processors.push(
|
||||
Processor::new(
|
||||
i.try_into().unwrap(),
|
||||
NetworkId::Bitcoin,
|
||||
ExternalNetworkId::Bitcoin,
|
||||
&outer_ops,
|
||||
handles.clone(),
|
||||
*key,
|
||||
|
|
|
@ -132,13 +132,13 @@ async fn set_rotation_test() {
|
|||
|
||||
// excluded participant
|
||||
let pair5 = insecure_pair_from_name("Eve");
|
||||
let network = NetworkId::Bitcoin;
|
||||
let network = ExternalNetworkId::Bitcoin;
|
||||
let amount = Amount(1_000_000 * 10_u64.pow(8));
|
||||
let serai = processors[0].serai().await;
|
||||
|
||||
// allocate now for the last participant so that it is guaranteed to be included into session
|
||||
// 1 set. This doesn't affect the genesis set at all since that is a predetermined set.
|
||||
allocate_stake(&serai, network, amount, &pair5, 0).await;
|
||||
allocate_stake(&serai, network.into(), amount, &pair5, 0).await;
|
||||
|
||||
// genesis keygen
|
||||
let _ = key_gen::<Secp256k1>(&mut processors, Session(0)).await;
|
||||
|
@ -151,12 +151,14 @@ async fn set_rotation_test() {
|
|||
}
|
||||
|
||||
// wait until next session to see the effect on coordinator
|
||||
wait_till_session_1(&serai, network).await;
|
||||
wait_till_session_1(&serai, network.into()).await;
|
||||
|
||||
// Ensure the new validator was included in the new set
|
||||
assert_eq!(
|
||||
most_recent_new_set_event(&serai, network).await,
|
||||
ValidatorSetsEvent::NewSet { set: ValidatorSet { session: Session(1), network } },
|
||||
most_recent_new_set_event(&serai, network.into()).await,
|
||||
ValidatorSetsEvent::NewSet {
|
||||
set: ValidatorSet { session: Session(1), network: network.into() }
|
||||
},
|
||||
);
|
||||
|
||||
// add the last participant & do the keygen
|
||||
|
|
|
@ -10,18 +10,17 @@ use ciphersuite::Secp256k1;
|
|||
use dkg::Participant;
|
||||
|
||||
use serai_client::{
|
||||
PairTrait,
|
||||
primitives::{
|
||||
NetworkId, Coin, Amount, Balance, BlockHash, SeraiAddress, ExternalAddress,
|
||||
insecure_pair_from_name,
|
||||
},
|
||||
coins::{
|
||||
primitives::{OutInstruction, OutInstructionWithBalance},
|
||||
CoinsEvent,
|
||||
},
|
||||
in_instructions::primitives::{InInstruction, InInstructionWithBalance, Batch},
|
||||
in_instructions::primitives::{Batch, InInstruction, InInstructionWithBalance},
|
||||
primitives::{
|
||||
insecure_pair_from_name, Amount, Balance, BlockHash, Coin, ExternalAddress, ExternalBalance,
|
||||
ExternalCoin, SeraiAddress,
|
||||
},
|
||||
validator_sets::primitives::Session,
|
||||
SeraiCoins,
|
||||
PairTrait, SeraiCoins,
|
||||
};
|
||||
use messages::{coordinator::PlanMeta, sign::SignId, SubstrateContext, CoordinatorMessage};
|
||||
|
||||
|
@ -202,7 +201,7 @@ async fn sign_test() {
|
|||
|
||||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
let amount = Amount(1_000_000_00);
|
||||
let balance = Balance { coin: Coin::Bitcoin, amount };
|
||||
let balance = ExternalBalance { coin: ExternalCoin::Bitcoin, amount };
|
||||
|
||||
let coin_block = BlockHash([0x33; 32]);
|
||||
let block_included_in = batch(
|
||||
|
@ -211,7 +210,7 @@ async fn sign_test() {
|
|||
Session(0),
|
||||
&substrate_key,
|
||||
Batch {
|
||||
network: NetworkId::Bitcoin,
|
||||
network: balance.coin.network(),
|
||||
id: 0,
|
||||
block: coin_block,
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
|
@ -236,10 +235,13 @@ async fn sign_test() {
|
|||
// Verify the mint occurred as expected
|
||||
assert_eq!(
|
||||
serai.mint_events().await.unwrap(),
|
||||
vec![CoinsEvent::Mint { to: serai_addr, balance }]
|
||||
vec![CoinsEvent::Mint { to: serai_addr, balance: balance.into() }]
|
||||
);
|
||||
assert_eq!(serai.coin_supply(ExternalCoin::Bitcoin.into()).await.unwrap(), amount);
|
||||
assert_eq!(
|
||||
serai.coin_balance(ExternalCoin::Bitcoin.into(), serai_addr).await.unwrap(),
|
||||
amount
|
||||
);
|
||||
assert_eq!(serai.coin_supply(Coin::Bitcoin).await.unwrap(), amount);
|
||||
assert_eq!(serai.coin_balance(Coin::Bitcoin, serai_addr).await.unwrap(), amount);
|
||||
}
|
||||
|
||||
// Trigger a burn
|
||||
|
@ -296,8 +298,11 @@ async fn sign_test() {
|
|||
let last_serai_block_hash = last_serai_block.hash();
|
||||
let serai = serai.as_of(last_serai_block_hash);
|
||||
let serai = serai.coins();
|
||||
assert_eq!(serai.coin_supply(Coin::Bitcoin).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_balance(Coin::Bitcoin, serai_addr).await.unwrap(), Amount(0));
|
||||
assert_eq!(serai.coin_supply(ExternalCoin::Bitcoin.into()).await.unwrap(), Amount(0));
|
||||
assert_eq!(
|
||||
serai.coin_balance(ExternalCoin::Bitcoin.into(), serai_addr).await.unwrap(),
|
||||
Amount(0)
|
||||
);
|
||||
|
||||
let mut plan_id = [0; 32];
|
||||
OsRng.fill_bytes(&mut plan_id);
|
||||
|
|
|
@ -9,12 +9,13 @@ use rand_core::{RngCore, OsRng};
|
|||
use scale::Encode;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
NetworkId, Coin, Amount, Balance, SeraiAddress, ExternalAddress, insecure_pair_from_name,
|
||||
},
|
||||
validator_sets::primitives::{Session, ValidatorSet},
|
||||
in_instructions::primitives::Shorthand,
|
||||
coins::primitives::{OutInstruction, OutInstructionWithBalance},
|
||||
in_instructions::primitives::Shorthand,
|
||||
primitives::{
|
||||
insecure_pair_from_name, Amount, Balance, Coin, ExternalAddress, ExternalBalance, ExternalCoin,
|
||||
SeraiAddress,
|
||||
},
|
||||
validator_sets::primitives::{ExternalValidatorSet, Session},
|
||||
PairTrait, SeraiCoins,
|
||||
};
|
||||
|
||||
|
@ -199,7 +200,7 @@ async fn mint_and_burn_test() {
|
|||
.await
|
||||
.unwrap()
|
||||
.validator_sets()
|
||||
.keys(ValidatorSet { network, session: Session(0) })
|
||||
.keys(ExternalValidatorSet { network, session: Session(0) })
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
|
@ -224,7 +225,10 @@ async fn mint_and_burn_test() {
|
|||
}
|
||||
};
|
||||
|
||||
(key_pair(false, NetworkId::Bitcoin).await, key_pair(true, NetworkId::Monero).await)
|
||||
(
|
||||
key_pair(false, ExternalNetworkId::Bitcoin).await,
|
||||
key_pair(true, ExternalNetworkId::Monero).await,
|
||||
)
|
||||
};
|
||||
|
||||
// Because the initial keys only become active when the network's time matches the Serai
|
||||
|
@ -439,8 +443,8 @@ async fn mint_and_burn_test() {
|
|||
);
|
||||
}
|
||||
};
|
||||
wait_for_batch(false, NetworkId::Bitcoin).await;
|
||||
wait_for_batch(true, NetworkId::Monero).await;
|
||||
wait_for_batch(false, ExternalNetworkId::Bitcoin).await;
|
||||
wait_for_batch(true, ExternalNetworkId::Monero).await;
|
||||
}
|
||||
|
||||
// TODO: Verify the mints
|
||||
|
@ -492,7 +496,7 @@ async fn mint_and_burn_test() {
|
|||
let serai_pair = &serai_pair;
|
||||
move |nonce, coin, amount, address| async move {
|
||||
let out_instruction = OutInstructionWithBalance {
|
||||
balance: Balance { coin, amount: Amount(amount) },
|
||||
balance: ExternalBalance { coin, amount: Amount(amount) },
|
||||
instruction: OutInstruction { address, data: None },
|
||||
};
|
||||
|
||||
|
@ -511,7 +515,7 @@ async fn mint_and_burn_test() {
|
|||
#[allow(clippy::inconsistent_digit_grouping)]
|
||||
burn(
|
||||
0,
|
||||
Coin::Bitcoin,
|
||||
ExternalCoin::Bitcoin,
|
||||
1_000_000_00,
|
||||
ExternalAddress::new(
|
||||
serai_client::networks::bitcoin::Address::new(bitcoin_addr.clone()).unwrap().into(),
|
||||
|
@ -522,7 +526,7 @@ async fn mint_and_burn_test() {
|
|||
|
||||
burn(
|
||||
1,
|
||||
Coin::Monero,
|
||||
ExternalCoin::Monero,
|
||||
1_000_000_000_000,
|
||||
ExternalAddress::new(
|
||||
serai_client::networks::monero::Address::new(monero_addr).unwrap().into(),
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::{sync::OnceLock, collections::HashMap};
|
|||
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use serai_client::primitives::NetworkId;
|
||||
use serai_client::primitives::ExternalNetworkId;
|
||||
|
||||
use dockertest::{
|
||||
LogAction, LogPolicy, LogSource, LogOptions, StartPolicy, TestBodySpecification,
|
||||
|
@ -56,15 +56,21 @@ pub(crate) async fn new_test(test_body: impl TestBody) {
|
|||
|
||||
let (coord_key, message_queue_keys, message_queue_composition) = message_queue_instance();
|
||||
|
||||
let (bitcoin_composition, bitcoin_port) = network_instance(NetworkId::Bitcoin);
|
||||
let mut bitcoin_processor_composition =
|
||||
processor_instance(NetworkId::Bitcoin, bitcoin_port, message_queue_keys[&NetworkId::Bitcoin]);
|
||||
let (bitcoin_composition, bitcoin_port) = network_instance(ExternalNetworkId::Bitcoin);
|
||||
let mut bitcoin_processor_composition = processor_instance(
|
||||
ExternalNetworkId::Bitcoin,
|
||||
bitcoin_port,
|
||||
message_queue_keys[&ExternalNetworkId::Bitcoin],
|
||||
);
|
||||
assert_eq!(bitcoin_processor_composition.len(), 1);
|
||||
let bitcoin_processor_composition = bitcoin_processor_composition.swap_remove(0);
|
||||
|
||||
let (monero_composition, monero_port) = network_instance(NetworkId::Monero);
|
||||
let mut monero_processor_composition =
|
||||
processor_instance(NetworkId::Monero, monero_port, message_queue_keys[&NetworkId::Monero]);
|
||||
let (monero_composition, monero_port) = network_instance(ExternalNetworkId::Monero);
|
||||
let mut monero_processor_composition = processor_instance(
|
||||
ExternalNetworkId::Monero,
|
||||
monero_port,
|
||||
message_queue_keys[&ExternalNetworkId::Monero],
|
||||
);
|
||||
assert_eq!(monero_processor_composition.len(), 1);
|
||||
let monero_processor_composition = monero_processor_composition.swap_remove(0);
|
||||
|
||||
|
|
|
@ -7,23 +7,25 @@ use ciphersuite::{
|
|||
Ciphersuite, Ristretto,
|
||||
};
|
||||
|
||||
use serai_primitives::NetworkId;
|
||||
use serai_primitives::{ExternalNetworkId, EXTERNAL_NETWORKS};
|
||||
|
||||
use dockertest::{
|
||||
PullPolicy, Image, LogAction, LogPolicy, LogSource, LogOptions, TestBodySpecification,
|
||||
};
|
||||
|
||||
pub type MessageQueuePrivateKey = <Ristretto as Ciphersuite>::F;
|
||||
pub fn instance(
|
||||
) -> (MessageQueuePrivateKey, HashMap<NetworkId, MessageQueuePrivateKey>, TestBodySpecification) {
|
||||
pub fn instance() -> (
|
||||
MessageQueuePrivateKey,
|
||||
HashMap<ExternalNetworkId, MessageQueuePrivateKey>,
|
||||
TestBodySpecification,
|
||||
) {
|
||||
serai_docker_tests::build("message-queue".to_string());
|
||||
|
||||
let coord_key = <Ristretto as Ciphersuite>::F::random(&mut OsRng);
|
||||
let priv_keys = HashMap::from([
|
||||
(NetworkId::Bitcoin, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||
(NetworkId::Ethereum, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||
(NetworkId::Monero, <Ristretto as Ciphersuite>::F::random(&mut OsRng)),
|
||||
]);
|
||||
let priv_keys = EXTERNAL_NETWORKS
|
||||
.into_iter()
|
||||
.map(|n| (n, <Ristretto as Ciphersuite>::F::random(&mut OsRng)))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let composition = TestBodySpecification::with_image(
|
||||
Image::with_repository("serai-dev-message-queue").pull_policy(PullPolicy::Never),
|
||||
|
@ -38,15 +40,15 @@ pub fn instance(
|
|||
("COORDINATOR_KEY".to_string(), hex::encode((Ristretto::generator() * coord_key).to_bytes())),
|
||||
(
|
||||
"BITCOIN_KEY".to_string(),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Bitcoin]).to_bytes()),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&ExternalNetworkId::Bitcoin]).to_bytes()),
|
||||
),
|
||||
(
|
||||
"ETHEREUM_KEY".to_string(),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Ethereum]).to_bytes()),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&ExternalNetworkId::Ethereum]).to_bytes()),
|
||||
),
|
||||
(
|
||||
"MONERO_KEY".to_string(),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&NetworkId::Monero]).to_bytes()),
|
||||
hex::encode((Ristretto::generator() * priv_keys[&ExternalNetworkId::Monero]).to_bytes()),
|
||||
),
|
||||
("DB_PATH".to_string(), "./message-queue-db".to_string()),
|
||||
("RUST_LOG".to_string(), "serai_message_queue=trace,".to_string()),
|
||||
|
@ -85,7 +87,7 @@ fn basic_functionality() {
|
|||
.queue(
|
||||
Metadata {
|
||||
from: Service::Coordinator,
|
||||
to: Service::Processor(NetworkId::Bitcoin),
|
||||
to: Service::Processor(ExternalNetworkId::Bitcoin),
|
||||
intent: b"intent".to_vec(),
|
||||
},
|
||||
b"Hello, World!".to_vec(),
|
||||
|
@ -98,7 +100,7 @@ fn basic_functionality() {
|
|||
.queue(
|
||||
Metadata {
|
||||
from: Service::Coordinator,
|
||||
to: Service::Processor(NetworkId::Bitcoin),
|
||||
to: Service::Processor(ExternalNetworkId::Bitcoin),
|
||||
intent: b"intent 2".to_vec(),
|
||||
},
|
||||
b"Hello, World, again!".to_vec(),
|
||||
|
@ -108,9 +110,9 @@ fn basic_functionality() {
|
|||
|
||||
// Successfully get it
|
||||
let bitcoin = MessageQueue::new(
|
||||
Service::Processor(NetworkId::Bitcoin),
|
||||
Service::Processor(ExternalNetworkId::Bitcoin),
|
||||
rpc.clone(),
|
||||
Zeroizing::new(priv_keys[&NetworkId::Bitcoin]),
|
||||
Zeroizing::new(priv_keys[&ExternalNetworkId::Bitcoin]),
|
||||
);
|
||||
let msg = bitcoin.next(Service::Coordinator).await;
|
||||
assert_eq!(msg.from, Service::Coordinator);
|
||||
|
@ -140,7 +142,7 @@ fn basic_functionality() {
|
|||
.queue(
|
||||
Metadata {
|
||||
from: Service::Coordinator,
|
||||
to: Service::Processor(NetworkId::Monero),
|
||||
to: Service::Processor(ExternalNetworkId::Monero),
|
||||
// Intents should be per-from-to, making this valid
|
||||
intent: b"intent".to_vec(),
|
||||
},
|
||||
|
@ -149,9 +151,9 @@ fn basic_functionality() {
|
|||
.await;
|
||||
|
||||
let monero = MessageQueue::new(
|
||||
Service::Processor(NetworkId::Monero),
|
||||
Service::Processor(ExternalNetworkId::Monero),
|
||||
rpc,
|
||||
Zeroizing::new(priv_keys[&NetworkId::Monero]),
|
||||
Zeroizing::new(priv_keys[&ExternalNetworkId::Monero]),
|
||||
);
|
||||
assert_eq!(monero.next(Service::Coordinator).await.id, 0);
|
||||
monero.ack(Service::Coordinator, 0).await;
|
||||
|
|
|
@ -7,7 +7,7 @@ use rand_core::{RngCore, OsRng};
|
|||
|
||||
use ciphersuite::{group::ff::PrimeField, Ciphersuite, Ristretto};
|
||||
|
||||
use serai_client::primitives::NetworkId;
|
||||
use serai_client::primitives::ExternalNetworkId;
|
||||
use messages::{ProcessorMessage, CoordinatorMessage};
|
||||
use serai_message_queue::{Service, Metadata, client::MessageQueue};
|
||||
|
||||
|
@ -25,7 +25,7 @@ mod tests;
|
|||
static UNIQUE_ID: OnceLock<Mutex<u16>> = OnceLock::new();
|
||||
|
||||
pub fn processor_instance(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
port: u32,
|
||||
message_queue_key: <Ristretto as Ciphersuite>::F,
|
||||
) -> Vec<TestBodySpecification> {
|
||||
|
@ -33,10 +33,9 @@ pub fn processor_instance(
|
|||
OsRng.fill_bytes(&mut entropy);
|
||||
|
||||
let network_str = match network {
|
||||
NetworkId::Serai => panic!("starting a processor for Serai"),
|
||||
NetworkId::Bitcoin => "bitcoin",
|
||||
NetworkId::Ethereum => "ethereum",
|
||||
NetworkId::Monero => "monero",
|
||||
ExternalNetworkId::Bitcoin => "bitcoin",
|
||||
ExternalNetworkId::Ethereum => "ethereum",
|
||||
ExternalNetworkId::Monero => "monero",
|
||||
};
|
||||
let image = format!("{network_str}-processor");
|
||||
serai_docker_tests::build(image.clone());
|
||||
|
@ -57,7 +56,7 @@ pub fn processor_instance(
|
|||
.into(),
|
||||
)];
|
||||
|
||||
if network == NetworkId::Ethereum {
|
||||
if network == ExternalNetworkId::Ethereum {
|
||||
serai_docker_tests::build("ethereum-relayer".to_string());
|
||||
res.push(
|
||||
TestBodySpecification::with_image(
|
||||
|
@ -80,7 +79,7 @@ pub fn processor_instance(
|
|||
|
||||
pub type Handles = (String, String, String, String);
|
||||
pub fn processor_stack(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
network_hostname_override: Option<String>,
|
||||
) -> (Handles, <Ristretto as Ciphersuite>::F, Vec<TestBodySpecification>) {
|
||||
let (network_composition, network_rpc_port) = network_instance(network);
|
||||
|
@ -106,10 +105,9 @@ pub fn processor_stack(
|
|||
for (name, composition) in [
|
||||
Some((
|
||||
match network {
|
||||
NetworkId::Serai => unreachable!(),
|
||||
NetworkId::Bitcoin => "bitcoin",
|
||||
NetworkId::Ethereum => "ethereum",
|
||||
NetworkId::Monero => "monero",
|
||||
ExternalNetworkId::Bitcoin => "bitcoin",
|
||||
ExternalNetworkId::Ethereum => "ethereum",
|
||||
ExternalNetworkId::Monero => "monero",
|
||||
},
|
||||
network_composition,
|
||||
)),
|
||||
|
@ -161,7 +159,7 @@ pub fn processor_stack(
|
|||
}
|
||||
|
||||
pub struct Coordinator {
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
|
||||
network_handle: String,
|
||||
#[allow(unused)]
|
||||
|
@ -177,7 +175,7 @@ pub struct Coordinator {
|
|||
|
||||
impl Coordinator {
|
||||
pub fn new(
|
||||
network: NetworkId,
|
||||
network: ExternalNetworkId,
|
||||
ops: &DockerOperations,
|
||||
handles: Handles,
|
||||
coord_key: <Ristretto as Ciphersuite>::F,
|
||||
|
@ -213,7 +211,7 @@ impl Coordinator {
|
|||
let mut iters = 0;
|
||||
while iters < 60 {
|
||||
match network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::rpc::Rpc;
|
||||
|
||||
// Bitcoin's Rpc::new will test the connection
|
||||
|
@ -221,7 +219,7 @@ impl Coordinator {
|
|||
break;
|
||||
}
|
||||
}
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
use std::sync::Arc;
|
||||
use ethereum_serai::{
|
||||
alloy::{
|
||||
|
@ -270,7 +268,7 @@ impl Coordinator {
|
|||
break;
|
||||
}
|
||||
}
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::rpc::Rpc;
|
||||
|
||||
|
@ -284,7 +282,6 @@ impl Coordinator {
|
|||
break;
|
||||
}
|
||||
}
|
||||
NetworkId::Serai => panic!("processor is booting with external network of Serai"),
|
||||
}
|
||||
|
||||
println!("external network RPC has yet to boot, waiting 1 sec, attempt {iters}");
|
||||
|
@ -337,7 +334,7 @@ impl Coordinator {
|
|||
pub async fn add_block(&self, ops: &DockerOperations) -> ([u8; 32], Vec<u8>) {
|
||||
let rpc_url = network_rpc(self.network, ops, &self.network_handle);
|
||||
match self.network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::{
|
||||
bitcoin::{consensus::Encodable, network::Network, Script, Address},
|
||||
rpc::Rpc,
|
||||
|
@ -360,7 +357,7 @@ impl Coordinator {
|
|||
block.consensus_encode(&mut block_buf).unwrap();
|
||||
(hash, block_buf)
|
||||
}
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
use ethereum_serai::alloy::{
|
||||
simple_request_transport::SimpleRequest,
|
||||
rpc_types::{BlockTransactionsKind, BlockNumberOrTag},
|
||||
|
@ -397,7 +394,7 @@ impl Coordinator {
|
|||
.into_bytes();
|
||||
(hash.into(), state)
|
||||
}
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::{rpc::Rpc, address::Network, ViewPair};
|
||||
|
@ -415,14 +412,13 @@ impl Coordinator {
|
|||
let hash = rpc.get_block_hash(rpc.get_height().await.unwrap() - 1).await.unwrap();
|
||||
(hash, rpc.get_block(hash).await.unwrap().serialize())
|
||||
}
|
||||
NetworkId::Serai => panic!("processor tests adding block to Serai"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sync(&self, ops: &DockerOperations, others: &[Coordinator]) {
|
||||
let rpc_url = network_rpc(self.network, ops, &self.network_handle);
|
||||
match self.network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::{bitcoin::consensus::Encodable, rpc::Rpc};
|
||||
|
||||
let rpc = Rpc::new(rpc_url).await.expect("couldn't connect to the Bitcoin RPC");
|
||||
|
@ -452,7 +448,7 @@ impl Coordinator {
|
|||
}
|
||||
}
|
||||
}
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
use ethereum_serai::alloy::{
|
||||
simple_request_transport::SimpleRequest,
|
||||
rpc_types::{BlockTransactionsKind, BlockNumberOrTag},
|
||||
|
@ -503,7 +499,7 @@ impl Coordinator {
|
|||
//assert_eq!(expected_number, new_number);
|
||||
}
|
||||
}
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::rpc::Rpc;
|
||||
|
||||
|
@ -534,14 +530,13 @@ impl Coordinator {
|
|||
}
|
||||
}
|
||||
}
|
||||
NetworkId::Serai => panic!("processors tests syncing Serai nodes"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn publish_transaction(&self, ops: &DockerOperations, tx: &[u8]) {
|
||||
let rpc_url = network_rpc(self.network, ops, &self.network_handle);
|
||||
match self.network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::{
|
||||
bitcoin::{consensus::Decodable, Transaction},
|
||||
rpc::Rpc,
|
||||
|
@ -551,7 +546,7 @@ impl Coordinator {
|
|||
Rpc::new(rpc_url).await.expect("couldn't connect to the coordinator's Bitcoin RPC");
|
||||
rpc.send_raw_transaction(&Transaction::consensus_decode(&mut &*tx).unwrap()).await.unwrap();
|
||||
}
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
use ethereum_serai::alloy::{
|
||||
simple_request_transport::SimpleRequest,
|
||||
rpc_client::ClientBuilder,
|
||||
|
@ -564,7 +559,7 @@ impl Coordinator {
|
|||
);
|
||||
let _ = provider.send_raw_transaction(tx).await.unwrap();
|
||||
}
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::{transaction::Transaction, rpc::Rpc};
|
||||
|
||||
|
@ -573,15 +568,15 @@ impl Coordinator {
|
|||
.expect("couldn't connect to the coordinator's Monero RPC");
|
||||
rpc.publish_transaction(&Transaction::read(&mut &*tx).unwrap()).await.unwrap();
|
||||
}
|
||||
NetworkId::Serai => panic!("processor tests broadcasting block to Serai"),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn publish_eventuality_completion(&self, ops: &DockerOperations, tx: &[u8]) {
|
||||
match self.network {
|
||||
NetworkId::Bitcoin | NetworkId::Monero => self.publish_transaction(ops, tx).await,
|
||||
NetworkId::Ethereum => (),
|
||||
NetworkId::Serai => panic!("processor tests broadcasting block to Serai"),
|
||||
ExternalNetworkId::Bitcoin | ExternalNetworkId::Monero => {
|
||||
self.publish_transaction(ops, tx).await
|
||||
}
|
||||
ExternalNetworkId::Ethereum => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,7 +587,7 @@ impl Coordinator {
|
|||
) -> Option<Vec<u8>> {
|
||||
let rpc_url = network_rpc(self.network, ops, &self.network_handle);
|
||||
match self.network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::{bitcoin::consensus::Encodable, rpc::Rpc};
|
||||
|
||||
let rpc =
|
||||
|
@ -614,7 +609,7 @@ impl Coordinator {
|
|||
None
|
||||
}
|
||||
}
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
/*
|
||||
let provider = RootProvider::<_, Ethereum>::new(
|
||||
ClientBuilder::default().transport(SimpleRequest::new(rpc_url.clone()), true),
|
||||
|
@ -664,7 +659,7 @@ impl Coordinator {
|
|||
|
||||
None
|
||||
}
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::rpc::Rpc;
|
||||
|
||||
|
@ -679,7 +674,6 @@ impl Coordinator {
|
|||
None
|
||||
}
|
||||
}
|
||||
NetworkId::Serai => panic!("processor tests broadcasting block to Serai"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ use rand_core::{RngCore, OsRng};
|
|||
use scale::Encode;
|
||||
|
||||
use serai_client::{
|
||||
primitives::{Amount, NetworkId, Coin, Balance, ExternalAddress},
|
||||
validator_sets::primitives::ExternalKey,
|
||||
in_instructions::primitives::{InInstruction, RefundableInInstruction, Shorthand},
|
||||
primitives::{Amount, ExternalAddress, ExternalBalance, ExternalCoin, ExternalNetworkId},
|
||||
validator_sets::primitives::ExternalKey,
|
||||
};
|
||||
|
||||
use dockertest::{PullPolicy, Image, StartPolicy, TestBodySpecification, DockerOperations};
|
||||
|
@ -52,37 +52,32 @@ pub fn monero_instance() -> (TestBodySpecification, u32) {
|
|||
(composition, XMR_PORT)
|
||||
}
|
||||
|
||||
pub fn network_instance(network: NetworkId) -> (TestBodySpecification, u32) {
|
||||
pub fn network_instance(network: ExternalNetworkId) -> (TestBodySpecification, u32) {
|
||||
match network {
|
||||
NetworkId::Bitcoin => bitcoin_instance(),
|
||||
NetworkId::Ethereum => ethereum_instance(),
|
||||
NetworkId::Monero => monero_instance(),
|
||||
NetworkId::Serai => {
|
||||
panic!("Serai is not a valid network to spawn an instance of for a processor")
|
||||
}
|
||||
ExternalNetworkId::Bitcoin => bitcoin_instance(),
|
||||
ExternalNetworkId::Ethereum => ethereum_instance(),
|
||||
ExternalNetworkId::Monero => monero_instance(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn network_rpc(network: NetworkId, ops: &DockerOperations, handle: &str) -> String {
|
||||
pub fn network_rpc(network: ExternalNetworkId, ops: &DockerOperations, handle: &str) -> String {
|
||||
let (ip, port) = ops
|
||||
.handle(handle)
|
||||
.host_port(match network {
|
||||
NetworkId::Bitcoin => BTC_PORT,
|
||||
NetworkId::Ethereum => ETH_PORT,
|
||||
NetworkId::Monero => XMR_PORT,
|
||||
NetworkId::Serai => panic!("getting port for external network yet it was Serai"),
|
||||
ExternalNetworkId::Bitcoin => BTC_PORT,
|
||||
ExternalNetworkId::Ethereum => ETH_PORT,
|
||||
ExternalNetworkId::Monero => XMR_PORT,
|
||||
})
|
||||
.unwrap();
|
||||
format!("http://{RPC_USER}:{RPC_PASS}@{ip}:{port}")
|
||||
}
|
||||
|
||||
pub fn confirmations(network: NetworkId) -> usize {
|
||||
pub fn confirmations(network: ExternalNetworkId) -> usize {
|
||||
use processor::networks::*;
|
||||
match network {
|
||||
NetworkId::Bitcoin => Bitcoin::CONFIRMATIONS,
|
||||
NetworkId::Ethereum => Ethereum::<serai_db::MemDb>::CONFIRMATIONS,
|
||||
NetworkId::Monero => Monero::CONFIRMATIONS,
|
||||
NetworkId::Serai => panic!("getting confirmations required for Serai"),
|
||||
ExternalNetworkId::Bitcoin => Bitcoin::CONFIRMATIONS,
|
||||
ExternalNetworkId::Ethereum => Ethereum::<serai_db::MemDb>::CONFIRMATIONS,
|
||||
ExternalNetworkId::Monero => Monero::CONFIRMATIONS,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,11 +103,11 @@ pub enum Wallet {
|
|||
|
||||
// TODO: Merge these functions with the processor's tests, which offers very similar functionality
|
||||
impl Wallet {
|
||||
pub async fn new(network: NetworkId, ops: &DockerOperations, handle: String) -> Wallet {
|
||||
pub async fn new(network: ExternalNetworkId, ops: &DockerOperations, handle: String) -> Wallet {
|
||||
let rpc_url = network_rpc(network, ops, &handle);
|
||||
|
||||
match network {
|
||||
NetworkId::Bitcoin => {
|
||||
ExternalNetworkId::Bitcoin => {
|
||||
use bitcoin_serai::{
|
||||
bitcoin::{
|
||||
secp256k1::{SECP256K1, SecretKey},
|
||||
|
@ -153,7 +148,7 @@ impl Wallet {
|
|||
Wallet::Bitcoin { private_key, public_key, input_tx: funds }
|
||||
}
|
||||
|
||||
NetworkId::Ethereum => {
|
||||
ExternalNetworkId::Ethereum => {
|
||||
use ciphersuite::{group::ff::Field, Secp256k1};
|
||||
use ethereum_serai::alloy::{
|
||||
primitives::{U256, Address},
|
||||
|
@ -185,7 +180,7 @@ impl Wallet {
|
|||
Wallet::Ethereum { rpc_url: rpc_url.clone(), key, nonce: 0 }
|
||||
}
|
||||
|
||||
NetworkId::Monero => {
|
||||
ExternalNetworkId::Monero => {
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
|
||||
use monero_simple_request_rpc::SimpleRequestRpc;
|
||||
use monero_wallet::{rpc::Rpc, address::Network, ViewPair};
|
||||
|
@ -210,7 +205,6 @@ impl Wallet {
|
|||
last_tx: (height, block.miner_transaction.hash()),
|
||||
}
|
||||
}
|
||||
NetworkId::Serai => panic!("creating a wallet for for Serai"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +213,7 @@ impl Wallet {
|
|||
ops: &DockerOperations,
|
||||
to: &ExternalKey,
|
||||
instruction: Option<InInstruction>,
|
||||
) -> (Vec<u8>, Balance) {
|
||||
) -> (Vec<u8>, ExternalBalance) {
|
||||
match self {
|
||||
Wallet::Bitcoin { private_key, public_key, ref mut input_tx } => {
|
||||
use bitcoin_serai::bitcoin::{
|
||||
|
@ -298,7 +292,7 @@ impl Wallet {
|
|||
let mut buf = vec![];
|
||||
tx.consensus_encode(&mut buf).unwrap();
|
||||
*input_tx = tx;
|
||||
(buf, Balance { coin: Coin::Bitcoin, amount: Amount(AMOUNT) })
|
||||
(buf, ExternalBalance { coin: ExternalCoin::Bitcoin, amount: Amount(AMOUNT) })
|
||||
}
|
||||
|
||||
Wallet::Ethereum { rpc_url, key, ref mut nonce } => {
|
||||
|
@ -400,7 +394,10 @@ impl Wallet {
|
|||
// We drop the bottom 10 decimals
|
||||
(
|
||||
bytes,
|
||||
Balance { coin: Coin::Ether, amount: Amount(u64::try_from(eight_decimals).unwrap()) },
|
||||
ExternalBalance {
|
||||
coin: ExternalCoin::Ether,
|
||||
amount: Amount(u64::try_from(eight_decimals).unwrap()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -417,7 +414,7 @@ impl Wallet {
|
|||
};
|
||||
use processor::{additional_key, networks::Monero};
|
||||
|
||||
let rpc_url = network_rpc(NetworkId::Monero, ops, handle);
|
||||
let rpc_url = network_rpc(ExternalNetworkId::Monero, ops, handle);
|
||||
let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
|
||||
|
||||
// Prepare inputs
|
||||
|
@ -485,7 +482,7 @@ impl Wallet {
|
|||
last_tx.0 = current_height;
|
||||
last_tx.1 = tx.hash();
|
||||
|
||||
(tx.serialize(), Balance { coin: Coin::Monero, amount: Amount(AMOUNT) })
|
||||
(tx.serialize(), ExternalBalance { coin: ExternalCoin::Monero, amount: Amount(AMOUNT) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@ use dkg::{Participant, tests::clone_without};
|
|||
use messages::{coordinator::*, SubstrateContext};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{
|
||||
BlockHash, Amount, Balance, crypto::RuntimePublic, PublicKey, SeraiAddress, NetworkId,
|
||||
},
|
||||
in_instructions::primitives::{
|
||||
InInstruction, InInstructionWithBalance, Batch, SignedBatch, batch_message,
|
||||
batch_message, Batch, InInstruction, InInstructionWithBalance, SignedBatch,
|
||||
},
|
||||
primitives::{
|
||||
crypto::RuntimePublic, Amount, BlockHash, ExternalBalance, ExternalNetworkId, PublicKey,
|
||||
SeraiAddress, EXTERNAL_NETWORKS,
|
||||
},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
@ -189,7 +190,7 @@ pub(crate) async fn substrate_block(
|
|||
|
||||
#[test]
|
||||
fn batch_test() {
|
||||
for network in [NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero] {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
let (coordinators, test) = new_test(network);
|
||||
|
||||
test.run(|ops| async move {
|
||||
|
@ -255,15 +256,14 @@ fn batch_test() {
|
|||
instructions: if let Some(instruction) = &instruction {
|
||||
vec![InInstructionWithBalance {
|
||||
instruction: instruction.clone(),
|
||||
balance: Balance {
|
||||
balance: ExternalBalance {
|
||||
coin: balance_sent.coin,
|
||||
amount: Amount(
|
||||
balance_sent.amount.0 -
|
||||
(2 * match network {
|
||||
NetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE,
|
||||
NetworkId::Ethereum => Ethereum::<MemDb>::COST_TO_AGGREGATE,
|
||||
NetworkId::Monero => Monero::COST_TO_AGGREGATE,
|
||||
NetworkId::Serai => panic!("minted for Serai?"),
|
||||
ExternalNetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE,
|
||||
ExternalNetworkId::Ethereum => Ethereum::<MemDb>::COST_TO_AGGREGATE,
|
||||
ExternalNetworkId::Monero => Monero::COST_TO_AGGREGATE,
|
||||
}),
|
||||
),
|
||||
},
|
||||
|
@ -322,7 +322,9 @@ fn batch_test() {
|
|||
},
|
||||
)
|
||||
.await;
|
||||
if instruction.is_some() || (instruction.is_none() && (network == NetworkId::Monero)) {
|
||||
if instruction.is_some() ||
|
||||
(instruction.is_none() && (network == ExternalNetworkId::Monero))
|
||||
{
|
||||
assert!(plans.is_empty());
|
||||
} else {
|
||||
// If no instruction was used, and the processor csn presume the origin, it'd have
|
||||
|
@ -335,7 +337,7 @@ fn batch_test() {
|
|||
// With the latter InInstruction not existing, we should've triggered a refund if the origin
|
||||
// was detectable
|
||||
// Check this is trying to sign a Plan
|
||||
if network != NetworkId::Monero {
|
||||
if network != ExternalNetworkId::Monero {
|
||||
let mut refund_id = None;
|
||||
for coordinator in &mut coordinators {
|
||||
match coordinator.recv_message().await {
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::{collections::HashMap, time::SystemTime};
|
|||
use dkg::{Participant, ThresholdParams, tests::clone_without};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{NetworkId, BlockHash, PublicKey},
|
||||
validator_sets::primitives::{Session, KeyPair},
|
||||
primitives::{BlockHash, PublicKey, EXTERNAL_NETWORKS},
|
||||
validator_sets::primitives::{KeyPair, Session},
|
||||
};
|
||||
|
||||
use messages::{SubstrateContext, key_gen::KeyGenId, CoordinatorMessage, ProcessorMessage};
|
||||
|
@ -144,7 +144,7 @@ pub(crate) async fn key_gen(coordinators: &mut [Coordinator]) -> KeyPair {
|
|||
|
||||
#[test]
|
||||
fn key_gen_test() {
|
||||
for network in [NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero] {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
let (coordinators, test) = new_test(network);
|
||||
|
||||
test.run(|ops| async move {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use ciphersuite::{Ciphersuite, Ristretto};
|
||||
|
||||
use serai_client::primitives::NetworkId;
|
||||
|
||||
use dockertest::DockerTest;
|
||||
|
||||
use crate::*;
|
||||
|
@ -17,7 +15,9 @@ mod send;
|
|||
pub(crate) const COORDINATORS: usize = 4;
|
||||
pub(crate) const THRESHOLD: usize = ((COORDINATORS * 2) / 3) + 1;
|
||||
|
||||
fn new_test(network: NetworkId) -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
|
||||
fn new_test(
|
||||
network: ExternalNetworkId,
|
||||
) -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)>, DockerTest) {
|
||||
let mut coordinators = vec![];
|
||||
let mut test = DockerTest::new().with_network(dockertest::Network::Isolated);
|
||||
let mut eth_handle = None;
|
||||
|
@ -25,7 +25,7 @@ fn new_test(network: NetworkId) -> (Vec<(Handles, <Ristretto as Ciphersuite>::F)
|
|||
let (handles, coord_key, compositions) = processor_stack(network, eth_handle.clone());
|
||||
// TODO: Remove this once https://github.com/foundry-rs/foundry/issues/7955
|
||||
// This has all processors share an Ethereum node until we can sync controlled nodes
|
||||
if network == NetworkId::Ethereum {
|
||||
if network == ExternalNetworkId::Ethereum {
|
||||
eth_handle = eth_handle.or_else(|| Some(handles.0.clone()));
|
||||
}
|
||||
coordinators.push((handles, coord_key));
|
||||
|
|
|
@ -8,9 +8,9 @@ use dkg::{Participant, tests::clone_without};
|
|||
use messages::{sign::SignId, SubstrateContext};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{BlockHash, NetworkId, Amount, Balance, SeraiAddress},
|
||||
coins::primitives::{OutInstruction, OutInstructionWithBalance},
|
||||
in_instructions::primitives::{InInstruction, InInstructionWithBalance, Batch},
|
||||
in_instructions::primitives::{Batch, InInstruction, InInstructionWithBalance},
|
||||
primitives::{Amount, BlockHash, ExternalBalance, SeraiAddress, EXTERNAL_NETWORKS},
|
||||
validator_sets::primitives::Session,
|
||||
};
|
||||
|
||||
|
@ -147,7 +147,7 @@ pub(crate) async fn sign_tx(
|
|||
|
||||
#[test]
|
||||
fn send_test() {
|
||||
for network in [NetworkId::Bitcoin, NetworkId::Ethereum, NetworkId::Monero] {
|
||||
for network in EXTERNAL_NETWORKS {
|
||||
let (coordinators, test) = new_test(network);
|
||||
|
||||
test.run(|ops| async move {
|
||||
|
@ -202,10 +202,9 @@ fn send_test() {
|
|||
let amount_minted = Amount(
|
||||
balance_sent.amount.0 -
|
||||
(2 * match network {
|
||||
NetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE,
|
||||
NetworkId::Ethereum => Ethereum::<MemDb>::COST_TO_AGGREGATE,
|
||||
NetworkId::Monero => Monero::COST_TO_AGGREGATE,
|
||||
NetworkId::Serai => panic!("minted for Serai?"),
|
||||
ExternalNetworkId::Bitcoin => Bitcoin::COST_TO_AGGREGATE,
|
||||
ExternalNetworkId::Ethereum => Ethereum::<MemDb>::COST_TO_AGGREGATE,
|
||||
ExternalNetworkId::Monero => Monero::COST_TO_AGGREGATE,
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -215,7 +214,7 @@ fn send_test() {
|
|||
block: BlockHash(block_with_tx.unwrap()),
|
||||
instructions: vec![InInstructionWithBalance {
|
||||
instruction,
|
||||
balance: Balance { coin: balance_sent.coin, amount: amount_minted },
|
||||
balance: ExternalBalance { coin: balance_sent.coin, amount: amount_minted },
|
||||
}],
|
||||
};
|
||||
|
||||
|
@ -245,7 +244,7 @@ fn send_test() {
|
|||
block: substrate_block_num,
|
||||
burns: vec![OutInstructionWithBalance {
|
||||
instruction: OutInstruction { address: wallet.address(), data: None },
|
||||
balance: Balance { coin: balance_sent.coin, amount: amount_minted },
|
||||
balance: ExternalBalance { coin: balance_sent.coin, amount: amount_minted },
|
||||
}],
|
||||
batches: vec![batch.batch.id],
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue