From 05d8c32be833504b1215d79c1fba1d72faab6799 Mon Sep 17 00:00:00 2001 From: econsta <143191603+econsta@users.noreply.github.com> Date: Tue, 21 Nov 2023 06:40:54 +0400 Subject: [PATCH] Multisig db (#444) * implement db macro for processor/multisigs/db.rs * ran fmt * cargo +nightly fmt --------- Co-authored-by: Luke Parker --- processor/src/multisigs/db.rs | 188 +++++++++++++-------------------- processor/src/multisigs/mod.rs | 39 ++++--- 2 files changed, 93 insertions(+), 134 deletions(-) diff --git a/processor/src/multisigs/db.rs b/processor/src/multisigs/db.rs index 202ce4ee..57e134bc 100644 --- a/processor/src/multisigs/db.rs +++ b/processor/src/multisigs/db.rs @@ -1,56 +1,39 @@ -use core::marker::PhantomData; - use ciphersuite::Ciphersuite; - pub use serai_db::*; use scale::{Encode, Decode}; use serai_client::{primitives::Balance, in_instructions::primitives::InInstructionWithBalance}; use crate::{ - Get, Db, Plan, + Get, Plan, networks::{Transaction, Network}, }; -#[derive(Debug)] -pub struct MultisigsDb(PhantomData, PhantomData); -impl MultisigsDb { - fn multisigs_key(dst: &'static [u8], key: impl AsRef<[u8]>) -> Vec { - D::key(b"MULTISIGS", dst, key) +create_db!( + MultisigsDb { + NextBatchDb: () -> u32, + PlanDb: (id: &[u8]) -> Vec, + PlansFromScanningDb: (block_number: u64) -> Vec, + OperatingCostsDb: () -> u64, + ResolvedDb: (tx: &[u8]) -> [u8; 32], + SigningDb: (key: &[u8]) -> Vec, + ForwardedOutputDb: (balance: Balance) -> Vec, + DelayedOutputDb: () -> Vec } +); - fn next_batch_key() -> Vec { - Self::multisigs_key(b"next_batch", []) - } - // Set the next batch ID to use - pub fn set_next_batch_id(txn: &mut D::Transaction<'_>, batch: u32) { - txn.put(Self::next_batch_key(), batch.to_le_bytes()); - } - // Get the next batch ID - pub fn next_batch_id(getter: &G) -> u32 { - getter.get(Self::next_batch_key()).map_or(0, |v| u32::from_le_bytes(v.try_into().unwrap())) - } - - fn plan_key(id: &[u8]) -> Vec { - Self::multisigs_key(b"plan", id) - } - fn resolved_key(tx: &[u8]) -> Vec { - Self::multisigs_key(b"resolved", tx) - } - fn signing_key(key: &[u8]) -> Vec { - Self::multisigs_key(b"signing", key) - } - pub fn save_active_plan( - txn: &mut D::Transaction<'_>, +impl PlanDb { + pub fn save_active_plan( + txn: &mut impl DbTxn, key: &[u8], - block_number: u64, + block_number: usize, plan: &Plan, operating_costs_at_time: u64, ) { let id = plan.id(); { - let mut signing = txn.get(Self::signing_key(key)).unwrap_or(vec![]); + let mut signing = SigningDb::get(txn, key).unwrap_or_default(); // If we've already noted we're signing this, return assert_eq!(signing.len() % 32, 0); @@ -61,25 +44,25 @@ impl MultisigsDb { } signing.extend(&id); - txn.put(Self::signing_key(key), id); + SigningDb::set(txn, key, &id); } { let mut buf = block_number.to_le_bytes().to_vec(); plan.write(&mut buf).unwrap(); buf.extend(&operating_costs_at_time.to_le_bytes()); - txn.put(Self::plan_key(&id), &buf); + Self::set(txn, &id, &buf); } } - pub fn active_plans(getter: &G, key: &[u8]) -> Vec<(u64, Plan, u64)> { - let signing = getter.get(Self::signing_key(key)).unwrap_or(vec![]); + pub fn active_plans(getter: &impl Get, key: &[u8]) -> Vec<(u64, Plan, u64)> { + let signing = SigningDb::get(getter, key).unwrap_or_default(); let mut res = vec![]; assert_eq!(signing.len() % 32, 0); for i in 0 .. (signing.len() / 32) { let id = &signing[(i * 32) .. ((i + 1) * 32)]; - let buf = getter.get(Self::plan_key(id)).unwrap(); + let buf = Self::get(getter, id).unwrap(); let block_number = u64::from_le_bytes(buf[.. 8].try_into().unwrap()); let plan = Plan::::read::<&[u8]>(&mut &buf[8 ..]).unwrap(); @@ -87,50 +70,41 @@ impl MultisigsDb { let operating_costs = u64::from_le_bytes(buf[(buf.len() - 8) ..].try_into().unwrap()); res.push((block_number, plan, operating_costs)); } - res } - fn operating_costs_key() -> Vec { - Self::multisigs_key(b"operating_costs", []) - } - pub fn take_operating_costs(txn: &mut D::Transaction<'_>) -> u64 { - let existing = txn - .get(Self::operating_costs_key()) - .map(|bytes| u64::from_le_bytes(bytes.try_into().unwrap())) - .unwrap_or(0); - txn.del(Self::operating_costs_key()); - existing - } - pub fn set_operating_costs(txn: &mut D::Transaction<'_>, amount: u64) { - if amount != 0 { - txn.put(Self::operating_costs_key(), amount.to_le_bytes()); - } - } - - pub fn resolved_plan( - getter: &G, - tx: >::Id, - ) -> Option<[u8; 32]> { - getter.get(Self::resolved_key(tx.as_ref())).map(|id| id.try_into().unwrap()) - } - pub fn plan_by_key_with_self_change( - getter: &G, + pub fn plan_by_key_with_self_change( + getter: &impl Get, key: ::G, id: [u8; 32], ) -> bool { - let plan = - Plan::::read::<&[u8]>(&mut &getter.get(Self::plan_key(&id)).unwrap()[8 ..]).unwrap(); + let plan = Plan::::read::<&[u8]>(&mut &Self::get(getter, &id).unwrap()[8 ..]).unwrap(); assert_eq!(plan.id(), id); (key == plan.key) && (Some(N::change_address(plan.key)) == plan.change) } - pub fn resolve_plan( - txn: &mut D::Transaction<'_>, +} + +impl OperatingCostsDb { + pub fn take_operating_costs(txn: &mut impl DbTxn) -> u64 { + let existing = Self::get(txn).unwrap_or_default(); + txn.del(Self::key()); + existing + } + pub fn set_operating_costs(txn: &mut impl DbTxn, amount: u64) { + if amount != 0 { + Self::set(txn, &amount); + } + } +} + +impl ResolvedDb { + pub fn resolve_plan( + txn: &mut impl DbTxn, key: &[u8], plan: [u8; 32], resolution: >::Id, ) { - let mut signing = txn.get(Self::signing_key(key)).unwrap_or(vec![]); + let mut signing = SigningDb::get(txn, key).unwrap_or_default(); assert_eq!(signing.len() % 32, 0); let mut found = false; @@ -147,17 +121,14 @@ impl MultisigsDb { if !found { log::warn!("told to finish signing {} yet wasn't actively signing it", hex::encode(plan)); } - - txn.put(Self::signing_key(key), signing); - - txn.put(Self::resolved_key(resolution.as_ref()), plan); + SigningDb::set(txn, key, &signing); + Self::set(txn, resolution.as_ref(), &plan); } +} - fn plans_from_scanning_key(block_number: usize) -> Vec { - Self::multisigs_key(b"plans_from_scanning", u32::try_from(block_number).unwrap().to_le_bytes()) - } - pub fn set_plans_from_scanning( - txn: &mut D::Transaction<'_>, +impl PlansFromScanningDb { + pub fn set_plans_from_scanning( + txn: &mut impl DbTxn, block_number: usize, plans: Vec>, ) { @@ -165,14 +136,15 @@ impl MultisigsDb { for plan in plans { plan.write(&mut buf).unwrap(); } - txn.put(Self::plans_from_scanning_key(block_number), buf); + Self::set(txn, block_number.try_into().unwrap(), &buf); } - pub fn take_plans_from_scanning( - txn: &mut D::Transaction<'_>, + + pub fn take_plans_from_scanning( + txn: &mut impl DbTxn, block_number: usize, ) -> Option>> { - let key = Self::plans_from_scanning_key(block_number); - let res = txn.get(&key).map(|plans| { + let block_number = u64::try_from(block_number).unwrap(); + let res = Self::get(txn, block_number).map(|plans| { let mut plans_ref = plans.as_slice(); let mut res = vec![]; while !plans_ref.is_empty() { @@ -181,56 +153,46 @@ impl MultisigsDb { res }); if res.is_some() { - txn.del(key); + txn.del(Self::key(block_number)); } res } +} - fn forwarded_output_key(balance: Balance) -> Vec { - Self::multisigs_key(b"forwarded_output", balance.encode()) - } - pub fn save_forwarded_output( - txn: &mut D::Transaction<'_>, - instruction: InInstructionWithBalance, - ) { - let key = Self::forwarded_output_key(instruction.balance); - let mut existing = txn.get(&key).unwrap_or(vec![]); +impl ForwardedOutputDb { + pub fn save_forwarded_output(txn: &mut impl DbTxn, instruction: InInstructionWithBalance) { + let mut existing = Self::get(txn, instruction.balance).unwrap_or_default(); existing.extend(instruction.encode()); - txn.put(key, existing); + Self::set(txn, instruction.balance, &existing); } + pub fn take_forwarded_output( - txn: &mut D::Transaction<'_>, + txn: &mut impl DbTxn, balance: Balance, ) -> Option { - let key = Self::forwarded_output_key(balance); - - let outputs = txn.get(&key)?; + let outputs = Self::get(txn, balance)?; let mut outputs_ref = outputs.as_slice(); - let res = InInstructionWithBalance::decode(&mut outputs_ref).unwrap(); assert!(outputs_ref.len() < outputs.len()); if outputs_ref.is_empty() { - txn.del(&key); + txn.del(&Self::key(balance)); } else { - txn.put(&key, outputs_ref); + Self::set(txn, balance, &outputs); } Some(res) } +} - fn delayed_output_keys() -> Vec { - Self::multisigs_key(b"delayed_outputs", []) - } - pub fn save_delayed_output(txn: &mut D::Transaction<'_>, instruction: InInstructionWithBalance) { - let key = Self::delayed_output_keys(); - let mut existing = txn.get(&key).unwrap_or(vec![]); +impl DelayedOutputDb { + pub fn save_delayed_output(txn: &mut impl DbTxn, instruction: InInstructionWithBalance) { + let mut existing = Self::get(txn).unwrap_or_default(); existing.extend(instruction.encode()); - txn.put(key, existing); + Self::set(txn, &existing); } - pub fn take_delayed_outputs(txn: &mut D::Transaction<'_>) -> Vec { - let key = Self::delayed_output_keys(); - let Some(outputs) = txn.get(&key) else { return vec![] }; - txn.del(key); + pub fn take_delayed_outputs(txn: &mut impl DbTxn) -> Vec { + let Some(outputs) = Self::get(txn) else { return vec![] }; + txn.del(Self::key()); let mut outputs_ref = outputs.as_slice(); let mut res = vec![]; diff --git a/processor/src/multisigs/mod.rs b/processor/src/multisigs/mod.rs index 26d8a782..264c373f 100644 --- a/processor/src/multisigs/mod.rs +++ b/processor/src/multisigs/mod.rs @@ -26,7 +26,7 @@ pub mod scanner; use scanner::{ScannerEvent, ScannerHandle, Scanner}; mod db; -use db::MultisigsDb; +use db::*; #[cfg(not(test))] mod scheduler; @@ -174,9 +174,7 @@ impl MultisigManager { // Load any TXs being actively signed let key = key.to_bytes(); - for (block_number, plan, operating_costs) in - MultisigsDb::::active_plans(raw_db, key.as_ref()) - { + for (block_number, plan, operating_costs) in PlanDb::active_plans::(raw_db, key.as_ref()) { let block_number = block_number.try_into().unwrap(); let id = plan.id(); @@ -557,12 +555,12 @@ impl MultisigManager { } OutputType::Change => { // If the TX containing this output resolved an Eventuality... - if let Some(plan) = MultisigsDb::::resolved_plan(txn, output.tx_id()) { + if let Some(plan) = ResolvedDb::get(txn, output.tx_id().as_ref()) { // And the Eventuality had change... // We need this check as Eventualities have a race condition and can't be relied // on, as extensively detailed above. Eventualities explicitly with change do have // a safe timing window however - if MultisigsDb::::plan_by_key_with_self_change( + if PlanDb::plan_by_key_with_self_change::( txn, // Pass the key so the DB checks the Plan's key is this multisig's, preventing a // potential issue where the new multisig creates a Plan with change *and a @@ -608,7 +606,7 @@ impl MultisigManager { block_number { // Load plans crated when we scanned the block - plans = MultisigsDb::::take_plans_from_scanning(txn, block_number).unwrap(); + plans = PlansFromScanningDb::take_plans_from_scanning::(txn, block_number).unwrap(); for plan in &plans { plans_from_scanning.insert(plan.id()); } @@ -719,12 +717,12 @@ impl MultisigManager { let key_bytes = key.to_bytes(); let (tx, post_fee_branches) = { - let running_operating_costs = MultisigsDb::::take_operating_costs(txn); + let running_operating_costs = OperatingCostsDb::take_operating_costs(txn); - MultisigsDb::::save_active_plan( + PlanDb::save_active_plan::( txn, key_bytes.as_ref(), - block_number.try_into().unwrap(), + block_number, &plan, running_operating_costs, ); @@ -752,7 +750,7 @@ impl MultisigManager { operating_costs += running_operating_costs; } - MultisigsDb::::set_operating_costs(txn, operating_costs); + OperatingCostsDb::set_operating_costs(txn, operating_costs); (tx, post_fee_branches) }; @@ -824,8 +822,7 @@ impl MultisigManager { continue; } - if let Some(instruction) = - MultisigsDb::::take_forwarded_output(txn, output.balance()) + if let Some(instruction) = ForwardedOutputDb::take_forwarded_output(txn, output.balance()) { instructions.push(instruction); } @@ -876,7 +873,7 @@ impl MultisigManager { // letting it die out if let Some(tx) = &tx { instruction.balance.amount.0 -= tx.0.fee(); - MultisigsDb::::save_forwarded_output(txn, instruction); + ForwardedOutputDb::save_forwarded_output(txn, instruction); } } else if let Some(refund_to) = refund_to { if let Ok(refund_to) = refund_to.consume().try_into() { @@ -925,7 +922,7 @@ impl MultisigManager { if Some(output.key()) == self.new.as_ref().map(|new| new.key) { match step { RotationStep::UseExisting => { - MultisigsDb::::save_delayed_output(txn, instruction); + DelayedOutputDb::save_delayed_output(txn, instruction); continue; } RotationStep::NewAsChange | @@ -940,7 +937,7 @@ impl MultisigManager { // Save the plans created while scanning // TODO: Should we combine all of these plans to reduce the fees incurred from their // execution? They're refunds and forwards. Neither should need isolate Plan/Eventualities. - MultisigsDb::::set_plans_from_scanning(txn, block_number, plans); + PlansFromScanningDb::set_plans_from_scanning(txn, block_number, plans); // If any outputs were delayed, append them into this block match step { @@ -948,13 +945,13 @@ impl MultisigManager { RotationStep::NewAsChange | RotationStep::ForwardFromExisting | RotationStep::ClosingExisting => { - instructions.extend(MultisigsDb::::take_delayed_outputs(txn)); + instructions.extend(DelayedOutputDb::take_delayed_outputs(txn)); } } let mut block_hash = [0; 32]; block_hash.copy_from_slice(block.as_ref()); - let mut batch_id = MultisigsDb::::next_batch_id(txn); + let mut batch_id = NextBatchDb::get(txn).unwrap_or_default(); // start with empty batch let mut batches = vec![Batch { @@ -987,7 +984,7 @@ impl MultisigManager { } // Save the next batch ID - MultisigsDb::::set_next_batch_id(txn, batch_id + 1); + NextBatchDb::set(txn, &(batch_id + 1)); ( block_number, @@ -1006,7 +1003,7 @@ impl MultisigManager { // within the block. Unknown Eventualities may have their Completed events emitted after // ScannerEvent::Block however. ScannerEvent::Completed(key, block_number, id, tx) => { - MultisigsDb::::resolve_plan(txn, &key, id, tx.id()); + ResolvedDb::resolve_plan::(txn, &key, id, tx.id()); (block_number, MultisigEvent::Completed(key, id, tx)) } }; @@ -1028,7 +1025,7 @@ impl MultisigManager { let scheduler_is_empty = closing && existing.scheduler.empty(); // Nothing is still being signed let no_active_plans = scheduler_is_empty && - MultisigsDb::::active_plans(txn, existing.key.to_bytes().as_ref()).is_empty(); + PlanDb::active_plans::(txn, existing.key.to_bytes().as_ref()).is_empty(); self .scanner