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 std::io;
use group::GroupEncoding;
use group::{Group, GroupEncoding};
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>;
}
/// A block from an external network.
#[async_trait::async_trait]
pub trait Block: Send + Sync + Sized + Clone + Debug {
/// A block header from an external network.
pub trait BlockHeader: Send + Sync + Sized + Clone + Debug {
/// The type used to identify blocks.
type Id: 'static + Id;
/// The ID of this block.
@ -148,6 +147,31 @@ pub trait Block: Send + Sync + Sized + Clone + Debug {
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.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
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 crate::ScannerFeed;
use crate::{ScannerFeed, BlockIdFor, KeyFor, OutputFor};
// The DB macro doesn't support `BorshSerialize + BorshDeserialize` as a bound, hence this.
trait Borshy: BorshSerialize + BorshDeserialize {}
@ -64,25 +64,25 @@ create_db!(
pub(crate) struct ScannerDb<S: ScannerFeed>(PhantomData<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);
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)
}
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)
}
// activation_block_number is inclusive, so the key will be scanned for starting at the specified
// 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
NotableBlock::set(txn, activation_block_number, &());
// 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 {
if key == key_i.key.0 {
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
// specified block
pub(crate) fn retire_key(txn: &mut impl DbTxn, retirement_block_number: u64, key: S::Key) {
let mut keys: Vec<SeraiKey<BorshG<S::Key>>> =
pub(crate) fn retire_key(txn: &mut impl DbTxn, retirement_block_number: u64, key: KeyFor<S>) {
let mut keys: Vec<SeraiKey<BorshG<KeyFor<S>>>> =
ActiveKeys::get(txn).expect("retiring key yet no active keys");
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")
}
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)
}
pub(crate) fn set_start_block(
txn: &mut impl DbTxn,
start_block: u64,
id: <S::Block as Block>::Id,
) {
pub(crate) fn set_start_block(txn: &mut impl DbTxn, start_block: u64, id: BlockIdFor<S>) {
Self::set_block(txn, start_block, id);
LatestFinalizedBlock::set(txn, &start_block);
LatestScannableBlock::set(txn, &start_block);
@ -189,11 +185,7 @@ impl<S: ScannerFeed> ScannerDb<S> {
HighestAcknowledgedBlock::get(getter)
}
pub(crate) fn set_outputs(
txn: &mut impl DbTxn,
block_number: u64,
outputs: Vec<impl ReceivedOutput<S::Key, S::Address>>,
) {
pub(crate) fn set_outputs(txn: &mut impl DbTxn, block_number: u64, outputs: Vec<OutputFor<S>>) {
if outputs.is_empty() {
return;
}

View file

@ -1,6 +1,6 @@
use serai_db::{Db, DbTxn};
use primitives::{Id, Block};
use primitives::{Id, BlockHeader};
// TODO: Localize to IndexDb?
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
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,
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 primitives::{ReceivedOutput, Block};
use primitives::{ReceivedOutput, BlockHeader, Block};
mod db;
mod index;
@ -21,15 +21,6 @@ pub trait ScannerFeed: Send + Sync {
/// This value must be at least `1`.
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.
///
/// 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)
}
/// 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.
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.
#[derive(Clone)]
pub(crate) struct RunNowHandle(mpsc::Sender<()>);

View file

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