Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement block emissions #551

Merged
merged 35 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c1bcb0f
add genesis liquidity implementation
Mar 27, 2024
da51543
Merge branch 'develop' of https://github.com/serai-dex/serai into emi…
Mar 27, 2024
b9df73e
add missing deposit event
Mar 27, 2024
2b32fe9
fix CI issues
Mar 28, 2024
207f2bd
minor fixes
Mar 29, 2024
c320402
make math safer
Apr 8, 2024
8be0538
fix fmt
Apr 9, 2024
1cd9868
implement block emissions
Apr 9, 2024
df774d1
Merge branch 'develop' of https://github.com/serai-dex/serai into emi…
Apr 17, 2024
7041dbe
make remove liquidity an authorized call
Apr 17, 2024
826f998
implement setting initial values for coins
Apr 20, 2024
926ddd0
add genesis liquidity test & misc fixes
Apr 29, 2024
7ecbfde
Merge branch 'develop' of https://github.com/serai-dex/serai into emi…
Apr 30, 2024
20cf4c9
updato develop latest
Apr 30, 2024
90a5232
Merge branch 'develop' of https://github.com/serai-dex/serai into emi…
May 3, 2024
04fcb2b
fix rotation test
May 3, 2024
33fcd27
Merge branch 'emissions' of https://github.com/akildemir/serai into b…
May 3, 2024
3ed6bd3
fix licencing
May 5, 2024
1d5a53d
add fast-epoch feature
May 5, 2024
971b93b
only create the pool when adding liquidity first time
May 10, 2024
30df837
add initial reward era test
May 10, 2024
317b929
test whole pre ec security emissions
May 10, 2024
b8a9d20
fix clippy
May 10, 2024
929d0bf
Merge branch 'develop' of https://github.com/akildemir/serai into emi…
May 16, 2024
904c6dd
Merge branch 'emissions' of https://github.com/akildemir/serai into b…
May 16, 2024
929e66c
add swap-to-staked-sri feature
May 16, 2024
d40df53
rebase to develop latest
Jul 19, 2024
84b534a
rebase changes
Jul 19, 2024
8eb7e66
fix tests
Jul 26, 2024
a23e7f6
Merge branch 'develop' into HEAD
kayabaNerve Jul 29, 2024
3cb7386
Remove accidentally commited ETH ABI files
kayabaNerve Jul 29, 2024
e1b4a9a
fix some pr comments
Jul 30, 2024
bb5fd37
Finish up fixing pr comments
Aug 1, 2024
dd9aeb1
exclude SRI from is_allowed check
Aug 2, 2024
c620d8f
Misc changes
kayabaNerve Aug 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ exceptions = [
{ allow = ["AGPL-3.0"], name = "serai-dex-pallet" },

{ allow = ["AGPL-3.0"], name = "serai-genesis-liquidity-pallet" },
akildemir marked this conversation as resolved.
Show resolved Hide resolved
{ allow = ["AGPL-3.0"], name = "serai-emissions-pallet" },

{ allow = ["AGPL-3.0"], name = "serai-in-instructions-pallet" },

Expand Down
2 changes: 2 additions & 0 deletions substrate/abi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ serai-primitives = { path = "../primitives", version = "0.1", default-features =
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
serai-validator-sets-primitives = { path = "../validator-sets/primitives", version = "0.1", default-features = false }
serai-genesis-liquidity-primitives = { path = "../genesis-liquidity/primitives", version = "0.1", default-features = false }
serai-emissions-primitives = { path = "../emissions/primitives", version = "0.1", default-features = false }
serai-in-instructions-primitives = { path = "../in-instructions/primitives", version = "0.1", default-features = false }
serai-signals-primitives = { path = "../signals/primitives", version = "0.1", default-features = false }

Expand All @@ -57,6 +58,7 @@ std = [
"serai-coins-primitives/std",
"serai-validator-sets-primitives/std",
"serai-genesis-liquidity-primitives/std",
"serai-emissions-primitives/std",
"serai-in-instructions-primitives/std",
"serai-signals-primitives/std",
]
Expand Down
1 change: 1 addition & 0 deletions substrate/abi/src/emissions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use serai_emissions_primitives as primitives;
12 changes: 8 additions & 4 deletions substrate/abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ pub mod liquidity_tokens;
pub mod dex;

pub mod validator_sets;
pub mod in_instructions;
pub mod signals;

pub mod genesis_liquidity;
pub mod emissions;

pub mod in_instructions;

pub mod signals;

pub mod babe;
pub mod grandpa;
Expand All @@ -32,8 +35,8 @@ pub enum Call {
Coins(coins::Call),
LiquidityTokens(liquidity_tokens::Call),
Dex(dex::Call),
GenesisLiquidity(genesis_liquidity::Call),
ValidatorSets(validator_sets::Call),
GenesisLiquidity(genesis_liquidity::Call),
InInstructions(in_instructions::Call),
Signals(signals::Call),
Babe(babe::Call),
Expand All @@ -54,8 +57,9 @@ pub enum Event {
Coins(coins::Event),
LiquidityTokens(liquidity_tokens::Event),
Dex(dex::Event),
GenesisLiquidity(genesis_liquidity::Event),
ValidatorSets(validator_sets::Event),
GenesisLiquidity(genesis_liquidity::Event),
Emissions,
InInstructions(in_instructions::Event),
Signals(signals::Event),
Babe,
Expand Down
23 changes: 8 additions & 15 deletions substrate/client/src/serai/dex.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use sp_core::bounded_vec::BoundedVec;
use serai_abi::primitives::{SeraiAddress, Amount, Coin};

use scale::{decode_from_bytes, Encode};

use crate::{Serai, SeraiError, TemporalSerai};
use crate::{SeraiError, TemporalSerai};

pub type DexEvent = serai_abi::dex::Event;

const PALLET: &str = "Dex";

#[derive(Clone, Copy)]
pub struct SeraiDex<'a>(pub(crate) &'a TemporalSerai<'a>);
impl<'a> SeraiDex<'a> {
Expand Down Expand Up @@ -62,17 +62,10 @@ impl<'a> SeraiDex<'a> {

/// Returns the reserves of `coin:SRI` pool.
pub async fn get_reserves(&self, coin: Coin) -> Result<Option<(Amount, Amount)>, SeraiError> {
let reserves = self
.0
.serai
.call(
"state_call",
["DexApi_get_reserves".to_string(), hex::encode((coin, Coin::Serai).encode())],
)
.await?;
let bytes = Serai::hex_decode(reserves)?;
let result = decode_from_bytes::<Option<(u64, u64)>>(bytes.into())
.map_err(|e| SeraiError::ErrorInResponse(e.to_string()))?;
Ok(result.map(|amounts| (Amount(amounts.0), Amount(amounts.1))))
self.0.runtime_api("DexApi_get_reserves", (coin, Coin::Serai)).await
}

pub async fn oracle_value(&self, coin: Coin) -> Result<Option<Amount>, SeraiError> {
self.0.storage(PALLET, "SecurityOracleValue", coin).await
}
}
5 changes: 5 additions & 0 deletions substrate/client/src/serai/genesis_liquidity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ impl<'a> SeraiGenesisLiquidity<'a> {
pub async fn supply(&self, coin: Coin) -> Result<LiquidityAmount, SeraiError> {
Ok(self.0.storage(PALLET, "Supply", coin).await?.unwrap_or(LiquidityAmount::zero()))
}

pub async fn genesis_complete(&self) -> Result<bool, SeraiError> {
let result: Option<()> = self.0.storage(PALLET, "GenesisComplete", ()).await?;
Ok(result.is_some())
}
}
33 changes: 22 additions & 11 deletions substrate/client/src/serai/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,6 @@ impl Serai {
Ok(())
}

// TODO: move this into substrate/client/src/validator_sets.rs
async fn active_network_validators(&self, network: NetworkId) -> Result<Vec<Public>, SeraiError> {
let validators: String = self
.call("state_call", ["SeraiRuntimeApi_validators".to_string(), hex::encode(network.encode())])
.await?;
let bytes = Self::hex_decode(validators)?;
let r = Vec::<Public>::decode(&mut bytes.as_slice())
.map_err(|e| SeraiError::ErrorInResponse(e.to_string()))?;
Ok(r)
}

pub async fn latest_finalized_block_hash(&self) -> Result<[u8; 32], SeraiError> {
let hash: String = self.call("chain_getFinalizedHead", ()).await?;
Self::hex_decode(hash)?.try_into().map_err(|_| {
Expand Down Expand Up @@ -378,6 +367,28 @@ impl<'a> TemporalSerai<'a> {
})?))
}

async fn runtime_api<P: Encode, R: Decode>(
&self,
method: &'static str,
params: P,
) -> Result<R, SeraiError> {
let result: String = self
.serai
.call(
"state_call",
[method.to_string(), hex::encode(params.encode()), hex::encode(self.block)],
)
.await?;

let bytes = Serai::hex_decode(result.clone())?;
R::decode(&mut bytes.as_slice()).map_err(|_| {
SeraiError::InvalidRuntime(format!(
"different type than what is expected to be returned, raw value: {}",
hex::encode(result)
))
})
}

pub fn coins(&'a self) -> SeraiCoins<'a> {
SeraiCoins(self)
}
Expand Down
10 changes: 9 additions & 1 deletion substrate/client/src/serai/validator_sets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ impl<'a> SeraiValidatorSets<'a> {
&self,
network: NetworkId,
) -> Result<Vec<Public>, SeraiError> {
self.0.serai.active_network_validators(network).await
self.0.runtime_api("SeraiRuntimeApi_validators", network).await
}

// TODO: Store these separately since we almost never need both at once?
Expand All @@ -178,6 +178,14 @@ impl<'a> SeraiValidatorSets<'a> {
self.0.storage(PALLET, "PendingSlashReport", network).await
}

pub async fn session_begin_block(
&self,
network: NetworkId,
session: Session,
) -> Result<Option<u64>, SeraiError> {
self.0.storage(PALLET, "SessionBeginBlock", (network, session)).await
}

pub fn set_keys(
network: NetworkId,
removed_participants: sp_runtime::BoundedVec<
Expand Down
115 changes: 115 additions & 0 deletions substrate/client/tests/common/genesis_liquidity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::collections::HashMap;

use rand_core::{RngCore, OsRng};
use zeroize::Zeroizing;

use ciphersuite::{Ciphersuite, Ristretto};
use frost::dkg::musig::musig;
use schnorrkel::Schnorrkel;

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},
primitives::{
Amount, NetworkId, Coin, Balance, BlockHash, SeraiAddress, insecure_pair_from_name,
},
};

use serai_client::{Serai, SeraiGenesisLiquidity};

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>) {
// make accounts with amounts
let mut accounts = HashMap::new();
for coin in coins {
// make 5 accounts per coin
let mut values = vec![];
for _ in 0 .. 5 {
let mut address = SeraiAddress::new([0; 32]);
OsRng.fill_bytes(&mut address.0);
values.push((address, Amount(OsRng.next_u64() % 10u64.pow(coin.decimals()))));
}
accounts.insert(*coin, values);
}

// send a batch per coin
let mut batch_ids: HashMap<NetworkId, u32> = HashMap::new();
for coin in coins {
// set up instructions
let instructions = accounts[coin]
.iter()
.map(|(addr, amount)| InInstructionWithBalance {
instruction: InInstruction::GenesisLiquidity(*addr),
balance: Balance { coin: *coin, amount: *amount },
})
.collect::<Vec<_>>();

// set up bloch hash
let mut block = BlockHash([0; 32]);
OsRng.fill_bytes(&mut block.0);

// set up batch id
batch_ids
.entry(coin.network())
.and_modify(|v| {
*v += 1;
})
.or_insert(0);

let batch =
Batch { network: coin.network(), id: batch_ids[&coin.network()], block, instructions };
provide_batch(serai, batch).await;
}

// 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] };
set_values(serai, &values).await;

(accounts, batch_ids)
}

#[allow(dead_code)]
async fn set_values(serai: &Serai, values: &Values) {
// prepare a Musig tx to oraclize the relative values
let pair = insecure_pair_from_name("Alice");
let public = pair.public();
// we publish the tx in set 1
let set = ValidatorSet { session: Session(1), network: NetworkId::Serai };

let public_key = <Ristretto as Ciphersuite>::read_G::<&[u8]>(&mut public.0.as_ref()).unwrap();
let secret_key = <Ristretto as Ciphersuite>::read_F::<&[u8]>(
&mut pair.as_ref().secret.to_bytes()[.. 32].as_ref(),
)
.unwrap();

assert_eq!(Ristretto::generator() * secret_key, public_key);
let threshold_keys =
musig::<Ristretto>(&musig_context(set), &Zeroizing::new(secret_key), &[public_key]).unwrap();

let sig = frost::tests::sign_without_caching(
&mut OsRng,
frost::tests::algorithm_machines(
&mut OsRng,
&Schnorrkel::new(b"substrate"),
&HashMap::from([(threshold_keys.params().i(), threshold_keys.into())]),
),
&oraclize_values_message(&set, values),
);

// oraclize values
let _ =
publish_tx(serai, &SeraiGenesisLiquidity::oraclize_values(*values, Signature(sig.to_bytes())))
.await;
}
12 changes: 6 additions & 6 deletions substrate/client/tests/common/in_instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use sp_core::Pair;

use serai_client::{
primitives::{insecure_pair_from_name, BlockHash, NetworkId, Balance, SeraiAddress},
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
validator_sets::primitives::{ValidatorSet, KeyPair},
in_instructions::{
primitives::{Batch, SignedBatch, batch_message, InInstruction, InInstructionWithBalance},
InInstructionsEvent,
Expand All @@ -22,12 +22,12 @@ use crate::common::{tx::publish_tx, validator_sets::set_keys};

#[allow(dead_code)]
pub async fn provide_batch(serai: &Serai, batch: Batch) -> [u8; 32] {
// TODO: Get the latest session
let set = ValidatorSet { session: Session(0), network: batch.network };
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 pair = insecure_pair_from_name(&format!("ValidatorSet {set:?}"));
let keys = if let Some(keys) =
serai.as_of_latest_finalized_block().await.unwrap().validator_sets().keys(set).await.unwrap()
{
let keys = if let Some(keys) = serai_latest.validator_sets().keys(set).await.unwrap() {
keys
} else {
let keys = KeyPair(pair.public(), vec![].try_into().unwrap());
Expand Down
1 change: 1 addition & 0 deletions substrate/client/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod tx;
pub mod validator_sets;
pub mod in_instructions;
pub mod dex;
pub mod genesis_liquidity;

#[macro_export]
macro_rules! serai_test {
Expand Down
Loading
Loading