From 32523bcce78dbd20602b10f716804e5772d023ec Mon Sep 17 00:00:00 2001 From: David Bell <17103917+davidjohnbell@users.noreply.github.com> Date: Fri, 10 Nov 2023 12:36:03 +0400 Subject: [PATCH 1/3] chore: convert nonce_deicer to use create_db macro --- Cargo.lock | 1 + coordinator/Cargo.toml | 1 + coordinator/src/main.rs | 4 +- coordinator/src/tributary/handle.rs | 10 +- coordinator/src/tributary/nonce_decider.rs | 105 ++++++++------------- 5 files changed, 49 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8653f9fe3..a0b3f02ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7578,6 +7578,7 @@ dependencies = [ "serai-env", "serai-message-queue", "serai-processor-messages", + "serde", "serde_json", "sp-application-crypto", "sp-runtime", diff --git a/coordinator/Cargo.toml b/coordinator/Cargo.toml index 760dc0a8e..6b9388957 100644 --- a/coordinator/Cargo.toml +++ b/coordinator/Cargo.toml @@ -41,6 +41,7 @@ serai-client = { path = "../substrate/client", default-features = false, feature hex = { version = "0.4", default-features = false, features = ["std"] } bincode = { version = "1", default-features = false } +serde = "1" serde_json = { version = "1", default-features = false, features = ["std"] } log = { version = "0.4", default-features = false, features = ["std"] } diff --git a/coordinator/src/main.rs b/coordinator/src/main.rs index 8298040b3..4d09d3eff 100644 --- a/coordinator/src/main.rs +++ b/coordinator/src/main.rs @@ -37,7 +37,7 @@ use ::tributary::{ mod tributary; use crate::tributary::{ - TributarySpec, SignData, Transaction, TributaryDb, NonceDecider, scanner::RecognizedIdType, + TributarySpec, SignData, Transaction, TributaryDb, ItemNonceDb, scanner::RecognizedIdType, }; mod db; @@ -740,7 +740,7 @@ async fn handle_processor_message( let nonce = loop { let Some(nonce) = - NonceDecider::::nonce(&txn, genesis, &tx).expect("signed TX didn't have nonce") + ItemNonceDb::nonce(&txn, genesis, &tx).expect("signed TX didn't have nonce") else { // This can be None if the following events occur, in order: // 1) We scanned the relevant transaction(s) in a Tributary block diff --git a/coordinator/src/tributary/handle.rs b/coordinator/src/tributary/handle.rs index c234b7e8d..14368649b 100644 --- a/coordinator/src/tributary/handle.rs +++ b/coordinator/src/tributary/handle.rs @@ -29,7 +29,7 @@ use crate::{ tributary::{ Transaction, TributarySpec, Topic, DataSpecification, TributaryDb, DataSet, Accumulation, TributaryState, - nonce_decider::NonceDecider, + nonce_decider::ItemNonceDb, dkg_confirmer::DkgConfirmer, scanner::{RecognizedIdType, RIDTrait}, }, @@ -501,7 +501,7 @@ pub(crate) async fn handle_application_tx< Transaction::Batch(_, batch) => { // Because this Batch has achieved synchrony, its batch ID should be authorized TributaryDb::::recognize_topic(txn, genesis, Topic::Batch(batch)); - let nonce = NonceDecider::::handle_batch(txn, genesis, batch); + let nonce = ItemNonceDb::handle_batch(txn, genesis, batch); recognized_id(spec.set(), genesis, RecognizedIdType::Batch, batch.to_vec(), nonce).await; } @@ -511,7 +511,7 @@ pub(crate) async fn handle_application_tx< despite us not providing that transaction", ); - let nonces = NonceDecider::::handle_substrate_block(txn, genesis, &plan_ids); + let nonces = ItemNonceDb::handle_substrate_block(txn, genesis, &plan_ids); for (nonce, id) in nonces.into_iter().zip(plan_ids.into_iter()) { TributaryDb::::recognize_topic(txn, genesis, Topic::Sign(id)); recognized_id(spec.set(), genesis, RecognizedIdType::Plan, id.to_vec(), nonce).await; @@ -534,7 +534,7 @@ pub(crate) async fn handle_application_tx< ) { Accumulation::Ready(DataSet::Participating(mut preprocesses)) => { unflatten(spec, &mut preprocesses); - NonceDecider::::selected_for_signing_batch(txn, genesis, data.plan); + ItemNonceDb::selected_for_signing_batch(txn, genesis, data.plan); let key = TributaryDb::::key_pair(txn, spec.set()).unwrap().0 .0; processors .send( @@ -602,7 +602,7 @@ pub(crate) async fn handle_application_tx< ) { Accumulation::Ready(DataSet::Participating(mut preprocesses)) => { unflatten(spec, &mut preprocesses); - NonceDecider::::selected_for_signing_plan(txn, genesis, data.plan); + ItemNonceDb::selected_for_signing_plan(txn, genesis, data.plan); processors .send( spec.set().network, diff --git a/coordinator/src/tributary/nonce_decider.rs b/coordinator/src/tributary/nonce_decider.rs index d3d583d89..2f76c560e 100644 --- a/coordinator/src/tributary/nonce_decider.rs +++ b/coordinator/src/tributary/nonce_decider.rs @@ -1,89 +1,68 @@ -use core::marker::PhantomData; - -use serai_db::{Get, DbTxn, Db}; +use serai_db::{Get, DbTxn, create_db}; use crate::tributary::Transaction; -/// Decides the nonce which should be used for a transaction on a Tributary. -/// -/// Deterministically builds a list of nonces to use based on the on-chain events and expected -/// transactions in response. Enables rebooting/rebuilding validators with full safety. -pub struct NonceDecider(PhantomData); +use scale::Encode; const BATCH_CODE: u8 = 0; const BATCH_SIGNING_CODE: u8 = 1; const PLAN_CODE: u8 = 2; const PLAN_SIGNING_CODE: u8 = 3; -impl NonceDecider { - fn next_nonce_key(genesis: [u8; 32]) -> Vec { - D::key(b"coordinator_tributary_nonce", b"next", genesis) - } - fn allocate_nonce(txn: &mut D::Transaction<'_>, genesis: [u8; 32]) -> u32 { - let key = Self::next_nonce_key(genesis); - let next = - txn.get(&key).map(|bytes| u32::from_le_bytes(bytes.try_into().unwrap())).unwrap_or(3); - txn.put(key, (next + 1).to_le_bytes()); - next +// Decides the nonce which should be used for a transaction on a Tributary. +// Deterministically builds a list of nonces to use based on the on-chain events and expected +// transactions in response. Enables rebooting/rebuilding validators with full safety. +create_db!( + NonceDeciderDb { + NextNonceDb: (genesis: [u8; 32]) -> u32, + ItemNonceDb: (genesis: [u8; 32], code: u8, id: &[u8]) -> u32 } +); - fn item_nonce_key(genesis: [u8; 32], code: u8, id: &[u8]) -> Vec { - D::key( - b"coordinator_tributary_nonce", - b"item", - [genesis.as_slice(), [code].as_ref(), id].concat(), - ) - } - fn set_nonce(txn: &mut D::Transaction<'_>, genesis: [u8; 32], code: u8, id: &[u8], nonce: u32) { - txn.put(Self::item_nonce_key(genesis, code, id), nonce.to_le_bytes()) - } - fn db_nonce(getter: &G, genesis: [u8; 32], code: u8, id: &[u8]) -> Option { - getter - .get(Self::item_nonce_key(genesis, code, id)) - .map(|bytes| u32::from_le_bytes(bytes.try_into().unwrap())) +impl NextNonceDb { + pub fn allocate_nonce(txn: &mut impl DbTxn, genesis: [u8; 32]) -> u32 { + let next = Self::get(txn, genesis).unwrap_or(3); + Self::set(txn, genesis, &(next + 1)); + next } +} - pub fn handle_batch(txn: &mut D::Transaction<'_>, genesis: [u8; 32], batch: [u8; 5]) -> u32 { - let nonce_for = Self::allocate_nonce(txn, genesis); - Self::set_nonce(txn, genesis, BATCH_CODE, &batch, nonce_for); +impl ItemNonceDb { + pub fn handle_batch(txn: &mut impl DbTxn, genesis: [u8; 32], batch: [u8; 5]) -> u32 { + let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); + Self::set(txn, genesis, BATCH_CODE, &batch, &nonce_for); nonce_for } - // TODO: The processor won't yield shares for this if the signing protocol aborts. We need to - // detect when we're expecting shares for an aborted protocol and insert a dummy transaction - // there. - pub fn selected_for_signing_batch( - txn: &mut D::Transaction<'_>, - genesis: [u8; 32], - batch: [u8; 5], - ) { - let nonce_for = Self::allocate_nonce(txn, genesis); - Self::set_nonce(txn, genesis, BATCH_SIGNING_CODE, &batch, nonce_for); - } pub fn handle_substrate_block( - txn: &mut D::Transaction<'_>, + txn: &mut impl DbTxn, genesis: [u8; 32], plans: &[[u8; 32]], ) -> Vec { let mut res = Vec::with_capacity(plans.len()); for plan in plans { - let nonce_for = Self::allocate_nonce(txn, genesis); - Self::set_nonce(txn, genesis, PLAN_CODE, plan, nonce_for); + let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); + Self::set(txn, genesis, PLAN_CODE, plan, &nonce_for); res.push(nonce_for); } res } + + // TODO: The processor won't yield shares for this if the signing protocol aborts. We need to + // detect when we're expecting shares for an aborted protocol and insert a dummy transaction + // there. + pub fn selected_for_signing_batch(txn: &mut impl DbTxn, genesis: [u8; 32], batch: [u8; 5]) { + let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); + Self::set(txn, genesis, BATCH_SIGNING_CODE, &batch, &nonce_for); + } + // TODO: Same TODO as selected_for_signing_batch - pub fn selected_for_signing_plan( - txn: &mut D::Transaction<'_>, - genesis: [u8; 32], - plan: [u8; 32], - ) { - let nonce_for = Self::allocate_nonce(txn, genesis); - Self::set_nonce(txn, genesis, PLAN_SIGNING_CODE, &plan, nonce_for); + pub fn selected_for_signing_plan(txn: &mut impl DbTxn, genesis: [u8; 32], plan: [u8; 32]) { + let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); + Self::set(txn, genesis, PLAN_SIGNING_CODE, &plan, &nonce_for); } - pub fn nonce(getter: &G, genesis: [u8; 32], tx: &Transaction) -> Option> { + pub fn nonce(getter: &impl Get, genesis: [u8; 32], tx: &Transaction) -> Option> { match tx { Transaction::RemoveParticipant(_) => None, @@ -105,28 +84,24 @@ impl NonceDecider { assert_eq!(*attempt, 0); Some(Some(2)) } - Transaction::Batch(_, _) => None, Transaction::SubstrateBlock(_) => None, - Transaction::BatchPreprocess(data) => { assert_eq!(data.attempt, 0); - Some(Self::db_nonce(getter, genesis, BATCH_CODE, &data.plan)) + Some(Self::get(getter, genesis, BATCH_CODE, &data.plan)) } Transaction::BatchShare(data) => { assert_eq!(data.attempt, 0); - Some(Self::db_nonce(getter, genesis, BATCH_SIGNING_CODE, &data.plan)) + Some(Self::get(getter, genesis, BATCH_SIGNING_CODE, &data.plan)) } - Transaction::SignPreprocess(data) => { assert_eq!(data.attempt, 0); - Some(Self::db_nonce(getter, genesis, PLAN_CODE, &data.plan)) + Some(Self::get(getter, genesis, PLAN_CODE, &data.plan)) } Transaction::SignShare(data) => { assert_eq!(data.attempt, 0); - Some(Self::db_nonce(getter, genesis, PLAN_SIGNING_CODE, &data.plan)) + Some(Self::get(getter, genesis, PLAN_SIGNING_CODE, &data.plan)) } - Transaction::SignCompleted { .. } => None, } } From e005e07c4ae1a6f74fcda701c73472ac348c2582 Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 12 Nov 2023 08:33:43 -0500 Subject: [PATCH 2/3] Restore pub NonceDecider --- coordinator/src/main.rs | 4 ++-- coordinator/src/tributary/handle.rs | 10 ++++---- coordinator/src/tributary/nonce_decider.rs | 28 ++++++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/coordinator/src/main.rs b/coordinator/src/main.rs index 4d09d3eff..64c941d16 100644 --- a/coordinator/src/main.rs +++ b/coordinator/src/main.rs @@ -37,7 +37,7 @@ use ::tributary::{ mod tributary; use crate::tributary::{ - TributarySpec, SignData, Transaction, TributaryDb, ItemNonceDb, scanner::RecognizedIdType, + TributarySpec, SignData, Transaction, TributaryDb, NonceDecider, scanner::RecognizedIdType, }; mod db; @@ -740,7 +740,7 @@ async fn handle_processor_message( let nonce = loop { let Some(nonce) = - ItemNonceDb::nonce(&txn, genesis, &tx).expect("signed TX didn't have nonce") + NonceDecider::nonce(&txn, genesis, &tx).expect("signed TX didn't have nonce") else { // This can be None if the following events occur, in order: // 1) We scanned the relevant transaction(s) in a Tributary block diff --git a/coordinator/src/tributary/handle.rs b/coordinator/src/tributary/handle.rs index 14368649b..ba995c87d 100644 --- a/coordinator/src/tributary/handle.rs +++ b/coordinator/src/tributary/handle.rs @@ -29,7 +29,7 @@ use crate::{ tributary::{ Transaction, TributarySpec, Topic, DataSpecification, TributaryDb, DataSet, Accumulation, TributaryState, - nonce_decider::ItemNonceDb, + nonce_decider::NonceDecider, dkg_confirmer::DkgConfirmer, scanner::{RecognizedIdType, RIDTrait}, }, @@ -501,7 +501,7 @@ pub(crate) async fn handle_application_tx< Transaction::Batch(_, batch) => { // Because this Batch has achieved synchrony, its batch ID should be authorized TributaryDb::::recognize_topic(txn, genesis, Topic::Batch(batch)); - let nonce = ItemNonceDb::handle_batch(txn, genesis, batch); + let nonce = NonceDecider::handle_batch(txn, genesis, batch); recognized_id(spec.set(), genesis, RecognizedIdType::Batch, batch.to_vec(), nonce).await; } @@ -511,7 +511,7 @@ pub(crate) async fn handle_application_tx< despite us not providing that transaction", ); - let nonces = ItemNonceDb::handle_substrate_block(txn, genesis, &plan_ids); + let nonces = NonceDecider::handle_substrate_block(txn, genesis, &plan_ids); for (nonce, id) in nonces.into_iter().zip(plan_ids.into_iter()) { TributaryDb::::recognize_topic(txn, genesis, Topic::Sign(id)); recognized_id(spec.set(), genesis, RecognizedIdType::Plan, id.to_vec(), nonce).await; @@ -534,7 +534,7 @@ pub(crate) async fn handle_application_tx< ) { Accumulation::Ready(DataSet::Participating(mut preprocesses)) => { unflatten(spec, &mut preprocesses); - ItemNonceDb::selected_for_signing_batch(txn, genesis, data.plan); + NonceDecider::selected_for_signing_batch(txn, genesis, data.plan); let key = TributaryDb::::key_pair(txn, spec.set()).unwrap().0 .0; processors .send( @@ -602,7 +602,7 @@ pub(crate) async fn handle_application_tx< ) { Accumulation::Ready(DataSet::Participating(mut preprocesses)) => { unflatten(spec, &mut preprocesses); - ItemNonceDb::selected_for_signing_plan(txn, genesis, data.plan); + NonceDecider::selected_for_signing_plan(txn, genesis, data.plan); processors .send( spec.set().network, diff --git a/coordinator/src/tributary/nonce_decider.rs b/coordinator/src/tributary/nonce_decider.rs index 2f76c560e..5a7d4a952 100644 --- a/coordinator/src/tributary/nonce_decider.rs +++ b/coordinator/src/tributary/nonce_decider.rs @@ -9,13 +9,10 @@ const BATCH_SIGNING_CODE: u8 = 1; const PLAN_CODE: u8 = 2; const PLAN_SIGNING_CODE: u8 = 3; -// Decides the nonce which should be used for a transaction on a Tributary. -// Deterministically builds a list of nonces to use based on the on-chain events and expected -// transactions in response. Enables rebooting/rebuilding validators with full safety. create_db!( NonceDeciderDb { NextNonceDb: (genesis: [u8; 32]) -> u32, - ItemNonceDb: (genesis: [u8; 32], code: u8, id: &[u8]) -> u32 + ItemNonceDb: (genesis: [u8; 32], code: u8, id: &[u8]) -> u32, } ); @@ -27,10 +24,15 @@ impl NextNonceDb { } } -impl ItemNonceDb { +/// Decides the nonce which should be used for a transaction on a Tributary. +/// +/// Deterministically builds a list of nonces to use based on the on-chain events and expected +/// transactions in response. Enables rebooting/rebuilding validators with full safety. +pub struct NonceDecider; +impl NonceDecider { pub fn handle_batch(txn: &mut impl DbTxn, genesis: [u8; 32], batch: [u8; 5]) -> u32 { let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); - Self::set(txn, genesis, BATCH_CODE, &batch, &nonce_for); + ItemNonceDb::set(txn, genesis, BATCH_CODE, &batch, &nonce_for); nonce_for } @@ -42,7 +44,7 @@ impl ItemNonceDb { let mut res = Vec::with_capacity(plans.len()); for plan in plans { let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); - Self::set(txn, genesis, PLAN_CODE, plan, &nonce_for); + ItemNonceDb::set(txn, genesis, PLAN_CODE, plan, &nonce_for); res.push(nonce_for); } res @@ -53,13 +55,13 @@ impl ItemNonceDb { // there. pub fn selected_for_signing_batch(txn: &mut impl DbTxn, genesis: [u8; 32], batch: [u8; 5]) { let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); - Self::set(txn, genesis, BATCH_SIGNING_CODE, &batch, &nonce_for); + ItemNonceDb::set(txn, genesis, BATCH_SIGNING_CODE, &batch, &nonce_for); } // TODO: Same TODO as selected_for_signing_batch pub fn selected_for_signing_plan(txn: &mut impl DbTxn, genesis: [u8; 32], plan: [u8; 32]) { let nonce_for = NextNonceDb::allocate_nonce(txn, genesis); - Self::set(txn, genesis, PLAN_SIGNING_CODE, &plan, &nonce_for); + ItemNonceDb::set(txn, genesis, PLAN_SIGNING_CODE, &plan, &nonce_for); } pub fn nonce(getter: &impl Get, genesis: [u8; 32], tx: &Transaction) -> Option> { @@ -88,19 +90,19 @@ impl ItemNonceDb { Transaction::SubstrateBlock(_) => None, Transaction::BatchPreprocess(data) => { assert_eq!(data.attempt, 0); - Some(Self::get(getter, genesis, BATCH_CODE, &data.plan)) + Some(ItemNonceDb::get(getter, genesis, BATCH_CODE, &data.plan)) } Transaction::BatchShare(data) => { assert_eq!(data.attempt, 0); - Some(Self::get(getter, genesis, BATCH_SIGNING_CODE, &data.plan)) + Some(ItemNonceDb::get(getter, genesis, BATCH_SIGNING_CODE, &data.plan)) } Transaction::SignPreprocess(data) => { assert_eq!(data.attempt, 0); - Some(Self::get(getter, genesis, PLAN_CODE, &data.plan)) + Some(ItemNonceDb::get(getter, genesis, PLAN_CODE, &data.plan)) } Transaction::SignShare(data) => { assert_eq!(data.attempt, 0); - Some(Self::get(getter, genesis, PLAN_SIGNING_CODE, &data.plan)) + Some(ItemNonceDb::get(getter, genesis, PLAN_SIGNING_CODE, &data.plan)) } Transaction::SignCompleted { .. } => None, } From 50c7e683215a3af315db67c9a981c0a582b9ac6c Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 12 Nov 2023 09:29:36 -0500 Subject: [PATCH 3/3] Remove extraneous comma I forgot to run git commit --amend on the prior commit :/ --- coordinator/src/tributary/nonce_decider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coordinator/src/tributary/nonce_decider.rs b/coordinator/src/tributary/nonce_decider.rs index 5a7d4a952..74b8ebbf1 100644 --- a/coordinator/src/tributary/nonce_decider.rs +++ b/coordinator/src/tributary/nonce_decider.rs @@ -12,7 +12,7 @@ const PLAN_SIGNING_CODE: u8 = 3; create_db!( NonceDeciderDb { NextNonceDb: (genesis: [u8; 32]) -> u32, - ItemNonceDb: (genesis: [u8; 32], code: u8, id: &[u8]) -> u32, + ItemNonceDb: (genesis: [u8; 32], code: u8, id: &[u8]) -> u32 } );