Differentiate BlockHeader from Block

This commit is contained in:
Luke Parker 2024-08-20 16:51:58 -04:00
parent 2b47feafed
commit 951872b026
5 changed files with 53 additions and 48 deletions

View file

@ -5,7 +5,7 @@
use core::fmt::Debug; use core::fmt::Debug;
use std::io; use std::io;
use group::GroupEncoding; use group::{Group, GroupEncoding};
use serai_primitives::Balance; use serai_primitives::Balance;
@ -137,9 +137,8 @@ pub trait ReceivedOutput<K: GroupEncoding, A>:
fn read<R: io::Read>(reader: &mut R) -> io::Result<Self>; fn read<R: io::Read>(reader: &mut R) -> io::Result<Self>;
} }
/// A block from an external network. /// A block header from an external network.
#[async_trait::async_trait] pub trait BlockHeader: Send + Sync + Sized + Clone + Debug {
pub trait Block: Send + Sync + Sized + Clone + Debug {
/// The type used to identify blocks. /// The type used to identify blocks.
type Id: 'static + Id; type Id: 'static + Id;
/// The ID of this block. /// The ID of this block.
@ -148,6 +147,31 @@ pub trait Block: Send + Sync + Sized + Clone + Debug {
fn parent(&self) -> Self::Id; fn parent(&self) -> Self::Id;
} }
/// A block from an external network.
///
/// A block is defined as a consensus event associated with a set of transactions. It is not
/// necessary to literally define it as whatever the external network defines as a block. For
/// external networks which finalize block(s), this block type should be a representation of all
/// transactions within a period finalization (whether block or epoch).
#[async_trait::async_trait]
pub trait Block: Send + Sync + Sized + Clone + Debug {
/// The type used for this block's header.
type Header: BlockHeader;
/// The type used to represent keys on this external network.
type Key: Group + GroupEncoding;
/// The type used to represent addresses on this external network.
type Address;
/// The type used to represent received outputs on this external network.
type Output: ReceivedOutput<Self::Key, Self::Address>;
/// The ID of this block.
fn id(&self) -> <Self::Header as BlockHeader>::Id;
/// Scan all outputs within this block to find the outputs spendable by this key.
fn scan_for_outputs(&self, key: Self::Key) -> Vec<Self::Output>;
}
/// A wrapper for a group element which implements the borsh traits. /// A wrapper for a group element which implements the borsh traits.
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct BorshG<G: GroupEncoding>(pub G); pub struct BorshG<G: GroupEncoding>(pub G);

View file

@ -5,7 +5,7 @@ use serai_db::{Get, DbTxn, create_db};
use primitives::{Id, ReceivedOutput, Block, BorshG}; use primitives::{Id, ReceivedOutput, Block, BorshG};
use crate::ScannerFeed; use crate::{ScannerFeed, BlockIdFor, KeyFor, OutputFor};
// The DB macro doesn't support `BorshSerialize + BorshDeserialize` as a bound, hence this. // The DB macro doesn't support `BorshSerialize + BorshDeserialize` as a bound, hence this.
trait Borshy: BorshSerialize + BorshDeserialize {} trait Borshy: BorshSerialize + BorshDeserialize {}
@ -64,25 +64,25 @@ create_db!(
pub(crate) struct ScannerDb<S: ScannerFeed>(PhantomData<S>); pub(crate) struct ScannerDb<S: ScannerFeed>(PhantomData<S>);
impl<S: ScannerFeed> ScannerDb<S> { impl<S: ScannerFeed> ScannerDb<S> {
pub(crate) fn set_block(txn: &mut impl DbTxn, number: u64, id: <S::Block as Block>::Id) { pub(crate) fn set_block(txn: &mut impl DbTxn, number: u64, id: BlockIdFor<S>) {
BlockId::set(txn, number, &id); BlockId::set(txn, number, &id);
BlockNumber::set(txn, id, &number); BlockNumber::set(txn, id, &number);
} }
pub(crate) fn block_id(getter: &impl Get, number: u64) -> Option<<S::Block as Block>::Id> { pub(crate) fn block_id(getter: &impl Get, number: u64) -> Option<BlockIdFor<S>> {
BlockId::get(getter, number) BlockId::get(getter, number)
} }
pub(crate) fn block_number(getter: &impl Get, id: <S::Block as Block>::Id) -> Option<u64> { pub(crate) fn block_number(getter: &impl Get, id: BlockIdFor<S>) -> Option<u64> {
BlockNumber::get(getter, id) BlockNumber::get(getter, id)
} }
// activation_block_number is inclusive, so the key will be scanned for starting at the specified // activation_block_number is inclusive, so the key will be scanned for starting at the specified
// block // block
pub(crate) fn queue_key(txn: &mut impl DbTxn, activation_block_number: u64, key: S::Key) { pub(crate) fn queue_key(txn: &mut impl DbTxn, activation_block_number: u64, key: KeyFor<S>) {
// Set this block as notable // Set this block as notable
NotableBlock::set(txn, activation_block_number, &()); NotableBlock::set(txn, activation_block_number, &());
// Push the key // Push the key
let mut keys: Vec<SeraiKey<BorshG<S::Key>>> = ActiveKeys::get(txn).unwrap_or(vec![]); let mut keys: Vec<SeraiKey<BorshG<KeyFor<S>>>> = ActiveKeys::get(txn).unwrap_or(vec![]);
for key_i in &keys { for key_i in &keys {
if key == key_i.key.0 { if key == key_i.key.0 {
panic!("queueing a key prior queued"); panic!("queueing a key prior queued");
@ -97,8 +97,8 @@ impl<S: ScannerFeed> ScannerDb<S> {
} }
// retirement_block_number is inclusive, so the key will no longer be scanned for as of the // retirement_block_number is inclusive, so the key will no longer be scanned for as of the
// specified block // specified block
pub(crate) fn retire_key(txn: &mut impl DbTxn, retirement_block_number: u64, key: S::Key) { pub(crate) fn retire_key(txn: &mut impl DbTxn, retirement_block_number: u64, key: KeyFor<S>) {
let mut keys: Vec<SeraiKey<BorshG<S::Key>>> = let mut keys: Vec<SeraiKey<BorshG<KeyFor<S>>>> =
ActiveKeys::get(txn).expect("retiring key yet no active keys"); ActiveKeys::get(txn).expect("retiring key yet no active keys");
assert!(keys.len() > 1, "retiring our only key"); assert!(keys.len() > 1, "retiring our only key");
@ -118,15 +118,11 @@ impl<S: ScannerFeed> ScannerDb<S> {
} }
panic!("retiring key yet not present in keys") panic!("retiring key yet not present in keys")
} }
pub(crate) fn keys(getter: &impl Get) -> Option<Vec<SeraiKey<BorshG<S::Key>>>> { pub(crate) fn keys(getter: &impl Get) -> Option<Vec<SeraiKey<BorshG<KeyFor<S>>>>> {
ActiveKeys::get(getter) ActiveKeys::get(getter)
} }
pub(crate) fn set_start_block( pub(crate) fn set_start_block(txn: &mut impl DbTxn, start_block: u64, id: BlockIdFor<S>) {
txn: &mut impl DbTxn,
start_block: u64,
id: <S::Block as Block>::Id,
) {
Self::set_block(txn, start_block, id); Self::set_block(txn, start_block, id);
LatestFinalizedBlock::set(txn, &start_block); LatestFinalizedBlock::set(txn, &start_block);
LatestScannableBlock::set(txn, &start_block); LatestScannableBlock::set(txn, &start_block);
@ -189,11 +185,7 @@ impl<S: ScannerFeed> ScannerDb<S> {
HighestAcknowledgedBlock::get(getter) HighestAcknowledgedBlock::get(getter)
} }
pub(crate) fn set_outputs( pub(crate) fn set_outputs(txn: &mut impl DbTxn, block_number: u64, outputs: Vec<OutputFor<S>>) {
txn: &mut impl DbTxn,
block_number: u64,
outputs: Vec<impl ReceivedOutput<S::Key, S::Address>>,
) {
if outputs.is_empty() { if outputs.is_empty() {
return; return;
} }

View file

@ -1,6 +1,6 @@
use serai_db::{Db, DbTxn}; use serai_db::{Db, DbTxn};
use primitives::{Id, Block}; use primitives::{Id, BlockHeader};
// TODO: Localize to IndexDb? // TODO: Localize to IndexDb?
use crate::{db::ScannerDb, ScannerFeed, ContinuallyRan}; use crate::{db::ScannerDb, ScannerFeed, ContinuallyRan};
@ -43,7 +43,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for IndexFinalizedTask<D, S> {
// Index the hashes of all blocks until the latest finalized block // Index the hashes of all blocks until the latest finalized block
for b in (our_latest_finalized + 1) ..= latest_finalized { for b in (our_latest_finalized + 1) ..= latest_finalized {
let block = match self.feed.block_by_number(b).await { let block = match self.feed.block_header_by_number(b).await {
Ok(block) => block, Ok(block) => block,
Err(e) => Err(format!("couldn't fetch block {b}: {e:?}"))?, Err(e) => Err(format!("couldn't fetch block {b}: {e:?}"))?,
}; };

View file

@ -2,7 +2,7 @@ use core::{fmt::Debug, time::Duration};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use primitives::{ReceivedOutput, Block}; use primitives::{ReceivedOutput, BlockHeader, Block};
mod db; mod db;
mod index; mod index;
@ -21,15 +21,6 @@ pub trait ScannerFeed: Send + Sync {
/// This value must be at least `1`. /// This value must be at least `1`.
const CONFIRMATIONS: u64; const CONFIRMATIONS: u64;
/// The type of the key used to receive coins on this blockchain.
type Key: group::Group + group::GroupEncoding;
/// The type of the address used to specify who to send coins to on this blockchain.
type Address;
/// The type representing a received (and spendable) output.
type Output: ReceivedOutput<Self::Key, Self::Address>;
/// The representation of a block for this blockchain. /// The representation of a block for this blockchain.
/// ///
/// A block is defined as a consensus event associated with a set of transactions. It is not /// A block is defined as a consensus event associated with a set of transactions. It is not
@ -58,17 +49,20 @@ pub trait ScannerFeed: Send + Sync {
Ok(self.latest_block_number().await? - Self::CONFIRMATIONS) Ok(self.latest_block_number().await? - Self::CONFIRMATIONS)
} }
/// Fetch a block header by its number.
async fn block_header_by_number(
&self,
number: u64,
) -> Result<<Self::Block as Block>::Header, Self::EphemeralError>;
/// Fetch a block by its number. /// Fetch a block by its number.
async fn block_by_number(&self, number: u64) -> Result<Self::Block, Self::EphemeralError>; async fn block_by_number(&self, number: u64) -> Result<Self::Block, Self::EphemeralError>;
/// Scan a block for its outputs.
async fn scan_for_outputs(
&self,
block: &Self::Block,
key: Self::Key,
) -> Result<Vec<Self::Output>, Self::EphemeralError>;
} }
type BlockIdFor<S> = <<<S as ScannerFeed>::Block as Block>::Header as BlockHeader>::Id;
type KeyFor<S> = <<S as ScannerFeed>::Block as Block>::Key;
type OutputFor<S> = <<S as ScannerFeed>::Block as Block>::Output;
/// A handle to immediately run an iteration of a task. /// A handle to immediately run an iteration of a task.
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct RunNowHandle(mpsc::Sender<()>); pub(crate) struct RunNowHandle(mpsc::Sender<()>);

View file

@ -60,12 +60,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ScanForOutputsTask<D, S> {
continue; continue;
} }
for output in self for output in block.scan_for_outputs(key.key.0) {
.feed
.scan_for_outputs(&block, key.key.0)
.await
.map_err(|e| format!("failed to scan block {b}: {e:?}"))?
{
assert_eq!(output.key(), key.key.0); assert_eq!(output.key(), key.key.0);
// TODO: Check for dust // TODO: Check for dust
outputs.push(output); outputs.push(output);