2024-08-20 11:57:56 -04:00
|
|
|
use serai_db::{Db, DbTxn};
|
|
|
|
|
|
|
|
|
|
use primitives::{Id, Block};
|
|
|
|
|
|
|
|
|
|
// TODO: Localize to ScanDb?
|
|
|
|
|
use crate::{db::ScannerDb, ScannerFeed};
|
|
|
|
|
|
|
|
|
|
struct ScanForOutputsTask<D: Db, S: ScannerFeed> {
|
|
|
|
|
db: D,
|
|
|
|
|
feed: S,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait::async_trait]
|
|
|
|
|
impl<D: Db, S: ScannerFeed> ContinuallyRan for ScanForOutputsTask<D, S> {
|
2024-08-20 16:24:18 -04:00
|
|
|
async fn run_iteration(&mut self) -> Result<bool, String> {
|
2024-08-20 11:57:56 -04:00
|
|
|
// Fetch the safe to scan block
|
|
|
|
|
let latest_scannable = ScannerDb::<S>::latest_scannable_block(&self.db).expect("ScanForOutputsTask run before writing the start block");
|
|
|
|
|
// Fetch the next block to scan
|
|
|
|
|
let next_to_scan = ScannerDb::<S>::next_to_scan_for_outputs_block(&self.db).expect("ScanForOutputsTask run before writing the start block");
|
|
|
|
|
|
|
|
|
|
for b in next_to_scan ..= latest_scannable {
|
|
|
|
|
let block = match self.feed.block_by_number(b).await {
|
|
|
|
|
Ok(block) => block,
|
|
|
|
|
Err(e) => Err(format!("couldn't fetch block {b}: {e:?}"))?,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Check the ID of this block is the expected ID
|
|
|
|
|
{
|
|
|
|
|
let expected = ScannerDb::<S>::block_id(b).expect("scannable block didn't have its ID saved");
|
|
|
|
|
if block.id() != expected {
|
|
|
|
|
panic!("finalized chain reorganized from {} to {} at {}", hex::encode(expected), hex::encode(block.id()), b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log::info!("scanning block: {} ({b})", hex::encode(block.id()));
|
|
|
|
|
|
|
|
|
|
let keys = ScannerDb::<S>::keys(&self.db).expect("scanning for a blockchain without any keys set");
|
|
|
|
|
// Remove all the retired keys
|
|
|
|
|
while let Some(retire_at) = keys[0].retirement_block_number {
|
|
|
|
|
if retire_at <= b {
|
|
|
|
|
keys.remove(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert!(keys.len() <= 2);
|
|
|
|
|
|
2024-08-20 16:24:18 -04:00
|
|
|
let mut outputs = vec![];
|
2024-08-20 11:57:56 -04:00
|
|
|
// Scan for each key
|
|
|
|
|
for key in keys {
|
|
|
|
|
// If this key has yet to active, skip it
|
|
|
|
|
if key.activation_block_number > b {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for output in network.scan_for_outputs(&block, key).awaits {
|
|
|
|
|
assert_eq!(output.key(), key);
|
|
|
|
|
// TODO: Check for dust
|
|
|
|
|
outputs.push(output);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut txn = self.db.txn();
|
2024-08-20 16:24:18 -04:00
|
|
|
// Save the outputs
|
|
|
|
|
ScannerDb::<S>::set_outputs(&mut txn, b, outputs);
|
|
|
|
|
// Update the next to scan block
|
2024-08-20 11:57:56 -04:00
|
|
|
ScannerDb::<S>::set_next_to_scan_for_outputs_block(&mut txn, b + 1);
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-20 16:24:18 -04:00
|
|
|
// Run dependents if we successfully scanned any blocks
|
|
|
|
|
Ok(next_to_scan <= latest_scannable)
|
2024-08-20 11:57:56 -04:00
|
|
|
}
|
|
|
|
|
}
|