mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 20:19:24 +00:00
Differentiate BlockHeader from Block
This commit is contained in:
parent
2b47feafed
commit
951872b026
5 changed files with 53 additions and 48 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:?}"))?,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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<()>);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue