mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 12:09:37 +00:00
Define a proper Topic (Zone + ID)
Removes the inefficiency of recognized_topic for attempt returning None if the topic isn't recognized.
This commit is contained in:
parent
2ad6b38be9
commit
fddc605c65
2 changed files with 52 additions and 80 deletions
|
@ -8,20 +8,19 @@ use serai_client::validator_sets::primitives::{ValidatorSet, KeyPair};
|
||||||
|
|
||||||
pub use serai_db::*;
|
pub use serai_db::*;
|
||||||
|
|
||||||
// Used to determine if an ID is acceptable
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub enum Zone {
|
pub enum Topic {
|
||||||
Dkg,
|
Dkg,
|
||||||
Batch,
|
Batch([u8; 32]),
|
||||||
Sign,
|
Sign([u8; 32]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zone {
|
impl Topic {
|
||||||
fn label(&self) -> &'static str {
|
fn as_key(&self, genesis: [u8; 32]) -> Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
Zone::Dkg => "dkg",
|
Topic::Dkg => [genesis.as_slice(), b"dkg".as_ref()].concat(),
|
||||||
Zone::Batch => "batch",
|
Topic::Batch(id) => [genesis.as_slice(), b"batch".as_ref(), id.as_ref()].concat(),
|
||||||
Zone::Sign => "sign",
|
Topic::Sign(id) => [genesis.as_slice(), b"sign".as_ref(), id.as_ref()].concat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,9 +28,8 @@ impl Zone {
|
||||||
// A struct to refer to a piece of data all validators will presumably provide a value for.
|
// A struct to refer to a piece of data all validators will presumably provide a value for.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct DataSpecification {
|
pub struct DataSpecification {
|
||||||
pub zone: Zone,
|
pub topic: Topic,
|
||||||
pub label: &'static str,
|
pub label: &'static str,
|
||||||
pub id: [u8; 32],
|
|
||||||
pub attempt: u32,
|
pub attempt: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +37,8 @@ impl DataSpecification {
|
||||||
fn as_key(&self, genesis: [u8; 32]) -> Vec<u8> {
|
fn as_key(&self, genesis: [u8; 32]) -> Vec<u8> {
|
||||||
// TODO: Use a proper transcript here to avoid conflicts?
|
// TODO: Use a proper transcript here to avoid conflicts?
|
||||||
[
|
[
|
||||||
genesis.as_ref(),
|
self.topic.as_key(genesis).as_ref(),
|
||||||
self.zone.label().as_bytes(),
|
|
||||||
self.label.as_bytes(),
|
self.label.as_bytes(),
|
||||||
self.id.as_ref(),
|
|
||||||
self.attempt.to_le_bytes().as_ref(),
|
self.attempt.to_le_bytes().as_ref(),
|
||||||
]
|
]
|
||||||
.concat()
|
.concat()
|
||||||
|
@ -60,18 +56,24 @@ impl<D: Db> TributaryDb<D> {
|
||||||
D::key(b"coordinator_tributary", dst, key)
|
D::key(b"coordinator_tributary", dst, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_key(genesis: [u8; 32]) -> Vec<u8> {
|
// Last block scanned
|
||||||
|
fn last_block_key(genesis: [u8; 32]) -> Vec<u8> {
|
||||||
Self::tributary_key(b"block", genesis)
|
Self::tributary_key(b"block", genesis)
|
||||||
}
|
}
|
||||||
pub fn set_last_block(&mut self, genesis: [u8; 32], block: [u8; 32]) {
|
pub fn set_last_block(&mut self, genesis: [u8; 32], block: [u8; 32]) {
|
||||||
let mut txn = self.0.txn();
|
let mut txn = self.0.txn();
|
||||||
txn.put(Self::block_key(genesis), block);
|
txn.put(Self::last_block_key(genesis), block);
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
pub fn last_block(&self, genesis: [u8; 32]) -> [u8; 32] {
|
pub fn last_block(&self, genesis: [u8; 32]) -> [u8; 32] {
|
||||||
self.0.get(Self::block_key(genesis)).map(|last| last.try_into().unwrap()).unwrap_or(genesis)
|
self
|
||||||
|
.0
|
||||||
|
.get(Self::last_block_key(genesis))
|
||||||
|
.map(|last| last.try_into().unwrap())
|
||||||
|
.unwrap_or(genesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a validator has been fatally slashed
|
||||||
fn fatal_slash_key(genesis: [u8; 32]) -> Vec<u8> {
|
fn fatal_slash_key(genesis: [u8; 32]) -> Vec<u8> {
|
||||||
Self::tributary_key(b"fatal_slash", genesis)
|
Self::tributary_key(b"fatal_slash", genesis)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +81,7 @@ impl<D: Db> TributaryDb<D> {
|
||||||
let key = Self::fatal_slash_key(genesis);
|
let key = Self::fatal_slash_key(genesis);
|
||||||
let mut existing = txn.get(&key).unwrap_or(vec![]);
|
let mut existing = txn.get(&key).unwrap_or(vec![]);
|
||||||
|
|
||||||
// don't append if we already have it.
|
// Don't append if we already have it
|
||||||
if existing.chunks(32).any(|ex_id| ex_id == id) {
|
if existing.chunks(32).any(|ex_id| ex_id == id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -88,6 +90,7 @@ impl<D: Db> TributaryDb<D> {
|
||||||
txn.put(key, existing);
|
txn.put(key, existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The plan IDs associated with a Substrate block
|
||||||
fn plan_ids_key(genesis: &[u8], block: u64) -> Vec<u8> {
|
fn plan_ids_key(genesis: &[u8], block: u64) -> Vec<u8> {
|
||||||
Self::tributary_key(b"plan_ids", [genesis, block.to_le_bytes().as_ref()].concat())
|
Self::tributary_key(b"plan_ids", [genesis, block.to_le_bytes().as_ref()].concat())
|
||||||
}
|
}
|
||||||
|
@ -112,6 +115,7 @@ impl<D: Db> TributaryDb<D> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The key pair which we're actively working on completing
|
||||||
fn currently_completing_key_pair_key(genesis: [u8; 32]) -> Vec<u8> {
|
fn currently_completing_key_pair_key(genesis: [u8; 32]) -> Vec<u8> {
|
||||||
Self::tributary_key(b"currently_completing_key_pair", genesis)
|
Self::tributary_key(b"currently_completing_key_pair", genesis)
|
||||||
}
|
}
|
||||||
|
@ -128,6 +132,7 @@ impl<D: Db> TributaryDb<D> {
|
||||||
.map(|bytes| KeyPair::decode(&mut bytes.as_slice()).unwrap())
|
.map(|bytes| KeyPair::decode(&mut bytes.as_slice()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The key pair confirmed for this Tributary
|
||||||
pub fn key_pair_key(set: ValidatorSet) -> Vec<u8> {
|
pub fn key_pair_key(set: ValidatorSet) -> Vec<u8> {
|
||||||
Self::tributary_key(b"key_pair", set.encode())
|
Self::tributary_key(b"key_pair", set.encode())
|
||||||
}
|
}
|
||||||
|
@ -138,33 +143,27 @@ impl<D: Db> TributaryDb<D> {
|
||||||
Some(KeyPair::decode(&mut getter.get(Self::key_pair_key(set))?.as_slice()).unwrap())
|
Some(KeyPair::decode(&mut getter.get(Self::key_pair_key(set))?.as_slice()).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recognized_id_key(genesis: [u8; 32], zone: Zone, id: [u8; 32]) -> Vec<u8> {
|
// The current attempt to resolve a topic
|
||||||
Self::tributary_key(
|
fn attempt_key(genesis: [u8; 32], topic: Topic) -> Vec<u8> {
|
||||||
b"recognized",
|
Self::tributary_key(b"attempt", topic.as_key(genesis))
|
||||||
[genesis.as_ref(), zone.label().as_bytes(), id.as_ref()].concat(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
pub fn recognized_id<G: Get>(getter: &G, genesis: [u8; 32], zone: Zone, id: [u8; 32]) -> bool {
|
pub fn recognize_topic(txn: &mut D::Transaction<'_>, genesis: [u8; 32], topic: Topic) {
|
||||||
getter.get(Self::recognized_id_key(genesis, zone, id)).is_some()
|
txn.put(Self::attempt_key(genesis, topic), 0u32.to_le_bytes())
|
||||||
}
|
}
|
||||||
pub fn recognize_id(txn: &mut D::Transaction<'_>, genesis: [u8; 32], zone: Zone, id: [u8; 32]) {
|
pub fn attempt<G: Get>(getter: &G, genesis: [u8; 32], topic: Topic) -> Option<u32> {
|
||||||
txn.put(Self::recognized_id_key(genesis, zone, id), [])
|
let attempt_bytes = getter.get(Self::attempt_key(genesis, topic));
|
||||||
|
// DKGs start when the chain starts
|
||||||
|
if attempt_bytes.is_none() && (topic == Topic::Dkg) {
|
||||||
|
return Some(0);
|
||||||
}
|
}
|
||||||
|
Some(u32::from_le_bytes(attempt_bytes?.try_into().unwrap()))
|
||||||
fn attempt_key(genesis: [u8; 32], id: [u8; 32]) -> Vec<u8> {
|
|
||||||
let genesis_ref: &[u8] = genesis.as_ref();
|
|
||||||
Self::tributary_key(b"attempt", [genesis_ref, id.as_ref()].concat())
|
|
||||||
}
|
|
||||||
pub fn attempt<G: Get>(getter: &G, genesis: [u8; 32], id: [u8; 32]) -> u32 {
|
|
||||||
u32::from_le_bytes(
|
|
||||||
getter.get(Self::attempt_key(genesis, id)).unwrap_or(vec![0; 4]).try_into().unwrap(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key for the amount of instances received thus far
|
// Key for the amount of instances received thus far
|
||||||
fn data_received_key(genesis: [u8; 32], data_spec: &DataSpecification) -> Vec<u8> {
|
fn data_received_key(genesis: [u8; 32], data_spec: &DataSpecification) -> Vec<u8> {
|
||||||
Self::tributary_key(b"data_received", data_spec.as_key(genesis))
|
Self::tributary_key(b"data_received", data_spec.as_key(genesis))
|
||||||
}
|
}
|
||||||
|
// Key for an instance of data from a specific validator
|
||||||
fn data_key(
|
fn data_key(
|
||||||
genesis: [u8; 32],
|
genesis: [u8; 32],
|
||||||
data_spec: &DataSpecification,
|
data_spec: &DataSpecification,
|
||||||
|
|
|
@ -36,7 +36,7 @@ use serai_db::{Get, Db};
|
||||||
use crate::{
|
use crate::{
|
||||||
processors::Processors,
|
processors::Processors,
|
||||||
tributary::{
|
tributary::{
|
||||||
Transaction, TributarySpec, Zone, DataSpecification, TributaryDb, scanner::RecognizedIdType,
|
Transaction, TributarySpec, Topic, DataSpecification, TributaryDb, scanner::RecognizedIdType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ pub fn generated_key_pair<D: Db>(
|
||||||
txn,
|
txn,
|
||||||
spec,
|
spec,
|
||||||
key,
|
key,
|
||||||
&DataSpecification { zone: Zone::Dkg, label: DKG_CONFIRMATION_NONCES, id: [0; 32], attempt },
|
&DataSpecification { topic: Topic::Dkg, label: DKG_CONFIRMATION_NONCES, attempt },
|
||||||
spec.n(),
|
spec.n(),
|
||||||
) else {
|
) else {
|
||||||
panic!("wasn't a participant in confirming a key pair");
|
panic!("wasn't a participant in confirming a key pair");
|
||||||
|
@ -243,13 +243,10 @@ pub async fn handle_application_tx<
|
||||||
let genesis = spec.genesis();
|
let genesis = spec.genesis();
|
||||||
|
|
||||||
let handle = |txn: &mut _, data_spec: &DataSpecification, bytes: Vec<u8>, signed: &Signed| {
|
let handle = |txn: &mut _, data_spec: &DataSpecification, bytes: Vec<u8>, signed: &Signed| {
|
||||||
if data_spec.zone == Zone::Dkg {
|
let Some(curr_attempt) = TributaryDb::<D>::attempt(txn, genesis, data_spec.topic) else {
|
||||||
// Since Dkg doesn't have an ID, solely attempts, this should just be [0; 32]
|
|
||||||
assert_eq!(data_spec.id, [0; 32], "DKG, which shouldn't have IDs, had a non-0 ID");
|
|
||||||
} else if !TributaryDb::<D>::recognized_id(txn, genesis, data_spec.zone, data_spec.id) {
|
|
||||||
// TODO: Full slash
|
// TODO: Full slash
|
||||||
todo!();
|
todo!();
|
||||||
}
|
};
|
||||||
|
|
||||||
// If they've already published a TX for this attempt, slash
|
// If they've already published a TX for this attempt, slash
|
||||||
if let Some(data) = TributaryDb::<D>::data(txn, genesis, data_spec, signed.signer) {
|
if let Some(data) = TributaryDb::<D>::data(txn, genesis, data_spec, signed.signer) {
|
||||||
|
@ -263,7 +260,6 @@ pub async fn handle_application_tx<
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the attempt is lesser than the blockchain's, slash
|
// If the attempt is lesser than the blockchain's, slash
|
||||||
let curr_attempt = TributaryDb::<D>::attempt(txn, genesis, data_spec.id);
|
|
||||||
if data_spec.attempt < curr_attempt {
|
if data_spec.attempt < curr_attempt {
|
||||||
// TODO: Slash for being late
|
// TODO: Slash for being late
|
||||||
return None;
|
return None;
|
||||||
|
@ -283,7 +279,7 @@ pub async fn handle_application_tx<
|
||||||
|
|
||||||
// If we have all the needed commitments/preprocesses/shares, tell the processor
|
// If we have all the needed commitments/preprocesses/shares, tell the processor
|
||||||
// TODO: This needs to be coded by weight, not by validator count
|
// TODO: This needs to be coded by weight, not by validator count
|
||||||
let needed = if data_spec.zone == Zone::Dkg { spec.n() } else { spec.t() };
|
let needed = if data_spec.topic == Topic::Dkg { spec.n() } else { spec.t() };
|
||||||
if received == needed {
|
if received == needed {
|
||||||
return Some(read_known_to_exist_data::<D, _>(txn, spec, key, data_spec, needed));
|
return Some(read_known_to_exist_data::<D, _>(txn, spec, key, data_spec, needed));
|
||||||
}
|
}
|
||||||
|
@ -294,7 +290,7 @@ pub async fn handle_application_tx<
|
||||||
Transaction::DkgCommitments(attempt, bytes, signed) => {
|
Transaction::DkgCommitments(attempt, bytes, signed) => {
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification { zone: Zone::Dkg, label: "commitments", id: [0; 32], attempt },
|
&DataSpecification { topic: Topic::Dkg, label: "commitments", attempt },
|
||||||
bytes,
|
bytes,
|
||||||
&signed,
|
&signed,
|
||||||
) {
|
) {
|
||||||
|
@ -343,18 +339,13 @@ pub async fn handle_application_tx<
|
||||||
|
|
||||||
let confirmation_nonces = handle(
|
let confirmation_nonces = handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification { topic: Topic::Dkg, label: DKG_CONFIRMATION_NONCES, attempt },
|
||||||
zone: Zone::Dkg,
|
|
||||||
label: DKG_CONFIRMATION_NONCES,
|
|
||||||
id: [0; 32],
|
|
||||||
attempt,
|
|
||||||
},
|
|
||||||
confirmation_nonces.to_vec(),
|
confirmation_nonces.to_vec(),
|
||||||
&signed,
|
&signed,
|
||||||
);
|
);
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification { zone: Zone::Dkg, label: "shares", id: [0; 32], attempt },
|
&DataSpecification { topic: Topic::Dkg, label: "shares", attempt },
|
||||||
bytes,
|
bytes,
|
||||||
&signed,
|
&signed,
|
||||||
) {
|
) {
|
||||||
|
@ -379,12 +370,7 @@ pub async fn handle_application_tx<
|
||||||
Transaction::DkgConfirmed(attempt, shares, signed) => {
|
Transaction::DkgConfirmed(attempt, shares, signed) => {
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification { topic: Topic::Dkg, label: DKG_CONFIRMATION_SHARES, attempt },
|
||||||
zone: Zone::Dkg,
|
|
||||||
label: DKG_CONFIRMATION_SHARES,
|
|
||||||
id: [0; 32],
|
|
||||||
attempt,
|
|
||||||
},
|
|
||||||
shares.to_vec(),
|
shares.to_vec(),
|
||||||
&signed,
|
&signed,
|
||||||
) {
|
) {
|
||||||
|
@ -395,12 +381,7 @@ pub async fn handle_application_tx<
|
||||||
txn,
|
txn,
|
||||||
spec,
|
spec,
|
||||||
key,
|
key,
|
||||||
&DataSpecification {
|
&DataSpecification { topic: Topic::Dkg, label: DKG_CONFIRMATION_NONCES, attempt },
|
||||||
zone: Zone::Dkg,
|
|
||||||
label: DKG_CONFIRMATION_NONCES,
|
|
||||||
id: [0; 32],
|
|
||||||
attempt,
|
|
||||||
},
|
|
||||||
spec.n(),
|
spec.n(),
|
||||||
) else {
|
) else {
|
||||||
panic!("wasn't a participant in DKG confirmation nonces");
|
panic!("wasn't a participant in DKG confirmation nonces");
|
||||||
|
@ -432,7 +413,7 @@ pub async fn handle_application_tx<
|
||||||
|
|
||||||
Transaction::Batch(_, batch) => {
|
Transaction::Batch(_, batch) => {
|
||||||
// Because this Batch has achieved synchrony, its batch ID should be authorized
|
// Because this Batch has achieved synchrony, its batch ID should be authorized
|
||||||
TributaryDb::<D>::recognize_id(txn, genesis, Zone::Batch, batch);
|
TributaryDb::<D>::recognize_topic(txn, genesis, Topic::Batch(batch));
|
||||||
recognized_id(spec.set().network, genesis, RecognizedIdType::Batch, batch).await;
|
recognized_id(spec.set().network, genesis, RecognizedIdType::Batch, batch).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,7 +424,7 @@ pub async fn handle_application_tx<
|
||||||
);
|
);
|
||||||
|
|
||||||
for id in plan_ids {
|
for id in plan_ids {
|
||||||
TributaryDb::<D>::recognize_id(txn, genesis, Zone::Sign, id);
|
TributaryDb::<D>::recognize_topic(txn, genesis, Topic::Sign(id));
|
||||||
recognized_id(spec.set().network, genesis, RecognizedIdType::Plan, id).await;
|
recognized_id(spec.set().network, genesis, RecognizedIdType::Plan, id).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,9 +433,8 @@ pub async fn handle_application_tx<
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification {
|
||||||
zone: Zone::Batch,
|
topic: Topic::Batch(data.plan),
|
||||||
label: "preprocess",
|
label: "preprocess",
|
||||||
id: data.plan,
|
|
||||||
attempt: data.attempt,
|
attempt: data.attempt,
|
||||||
},
|
},
|
||||||
data.data,
|
data.data,
|
||||||
|
@ -479,9 +459,8 @@ pub async fn handle_application_tx<
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification {
|
||||||
zone: Zone::Batch,
|
topic: Topic::Batch(data.plan),
|
||||||
label: "share",
|
label: "share",
|
||||||
id: data.plan,
|
|
||||||
attempt: data.attempt,
|
attempt: data.attempt,
|
||||||
},
|
},
|
||||||
data.data,
|
data.data,
|
||||||
|
@ -511,9 +490,8 @@ pub async fn handle_application_tx<
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification {
|
||||||
zone: Zone::Sign,
|
topic: Topic::Sign(data.plan),
|
||||||
label: "preprocess",
|
label: "preprocess",
|
||||||
id: data.plan,
|
|
||||||
attempt: data.attempt,
|
attempt: data.attempt,
|
||||||
},
|
},
|
||||||
data.data,
|
data.data,
|
||||||
|
@ -545,12 +523,7 @@ pub async fn handle_application_tx<
|
||||||
let key_pair = TributaryDb::<D>::key_pair(txn, spec.set());
|
let key_pair = TributaryDb::<D>::key_pair(txn, spec.set());
|
||||||
match handle(
|
match handle(
|
||||||
txn,
|
txn,
|
||||||
&DataSpecification {
|
&DataSpecification { topic: Topic::Sign(data.plan), label: "share", attempt: data.attempt },
|
||||||
zone: Zone::Sign,
|
|
||||||
label: "share",
|
|
||||||
id: data.plan,
|
|
||||||
attempt: data.attempt,
|
|
||||||
},
|
|
||||||
data.data,
|
data.data,
|
||||||
&data.signed,
|
&data.signed,
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Reference in a new issue