Have the processor verify the published Batches match expectations

This commit is contained in:
Luke Parker
2024-12-30 05:21:26 -05:00
parent 1d50792eed
commit e67e301fc2
14 changed files with 124 additions and 67 deletions

View File

@@ -8,9 +8,17 @@ use borsh::{BorshSerialize, BorshDeserialize};
use serai_db::{Get, DbTxn, create_db};
use serai_primitives::Balance;
use serai_validator_sets_primitives::Session;
use crate::{ScannerFeed, KeyFor, AddressFor};
#[derive(BorshSerialize, BorshDeserialize)]
pub(crate) struct BatchInfo {
pub(crate) block_number: u64,
pub(crate) publisher: Session,
pub(crate) in_instructions_hash: [u8; 32],
}
create_db!(
ScannerReport {
// The next block to potentially report
@@ -18,10 +26,11 @@ create_db!(
// The next Batch ID to use
NextBatchId: () -> u32,
// The block number which caused a batch
BlockNumberForBatch: (batch: u32) -> u64,
// The information needed to verify a batch
InfoForBatch: (batch: u32) -> BatchInfo,
// The external key for the session which should sign a batch
// TODO: Merge this with InfoForBatch
ExternalKeyForSessionToSignBatch: (batch: u32) -> Vec<u8>,
// The return addresses for the InInstructions within a Batch
@@ -46,15 +55,24 @@ impl<S: ScannerFeed> ReportDb<S> {
NextToPotentiallyReportBlock::get(getter)
}
pub(crate) fn acquire_batch_id(txn: &mut impl DbTxn, block_number: u64) -> u32 {
pub(crate) fn acquire_batch_id(txn: &mut impl DbTxn) -> u32 {
let id = NextBatchId::get(txn).unwrap_or(0);
NextBatchId::set(txn, &(id + 1));
BlockNumberForBatch::set(txn, id, &block_number);
id
}
pub(crate) fn take_block_number_for_batch(txn: &mut impl DbTxn, id: u32) -> Option<u64> {
BlockNumberForBatch::take(txn, id)
pub(crate) fn save_batch_info(
txn: &mut impl DbTxn,
id: u32,
block_number: u64,
publisher: Session,
in_instructions_hash: [u8; 32],
) {
InfoForBatch::set(txn, id, &BatchInfo { block_number, publisher, in_instructions_hash });
}
pub(crate) fn take_info_for_batch(txn: &mut impl DbTxn, id: u32) -> Option<BatchInfo> {
InfoForBatch::take(txn, id)
}
pub(crate) fn save_external_key_for_session_to_sign_batch(

View File

@@ -1,28 +1,28 @@
use core::{marker::PhantomData, future::Future};
use blake2::{digest::typenum::U32, Digest, Blake2b};
use scale::Encode;
use serai_db::{DbTxn, Db};
use serai_primitives::BlockHash;
use serai_in_instructions_primitives::{MAX_BATCH_SIZE, Batch};
use primitives::task::ContinuallyRan;
use crate::{
db::{Returnable, ScannerGlobalDb, InInstructionData, ScanToReportDb, Batches, BatchesToSign},
index,
scan::next_to_scan_for_outputs_block,
ScannerFeed, KeyFor,
};
mod db;
pub(crate) use db::ReturnInformation;
pub(crate) use db::{BatchInfo, ReturnInformation};
use db::ReportDb;
pub(crate) fn take_block_number_for_batch<S: ScannerFeed>(
pub(crate) fn take_info_for_batch<S: ScannerFeed>(
txn: &mut impl DbTxn,
id: u32,
) -> Option<u64> {
ReportDb::<S>::take_block_number_for_batch(txn, id)
) -> Option<BatchInfo> {
ReportDb::<S>::take_info_for_batch(txn, id)
}
pub(crate) fn take_external_key_for_session_to_sign_batch<S: ScannerFeed>(
@@ -88,33 +88,28 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
let next_to_potentially_report = ReportDb::<S>::next_to_potentially_report_block(&self.db)
.expect("ReportTask run before writing the start block");
for b in next_to_potentially_report ..= highest_reportable {
for block_number in next_to_potentially_report ..= highest_reportable {
let mut txn = self.db.txn();
// Receive the InInstructions for this block
// We always do this as we can't trivially tell if we should recv InInstructions before we
// do
let InInstructionData {
session_to_sign_batch,
external_key_for_session_to_sign_batch,
returnable_in_instructions: in_instructions,
} = ScanToReportDb::<S>::recv_in_instructions(&mut txn, b);
let notable = ScannerGlobalDb::<S>::is_block_notable(&txn, b);
} = ScanToReportDb::<S>::recv_in_instructions(&mut txn, block_number);
let notable = ScannerGlobalDb::<S>::is_block_notable(&txn, block_number);
if !notable {
assert!(in_instructions.is_empty(), "block wasn't notable yet had InInstructions");
}
// If this block is notable, create the Batch(s) for it
if notable {
let network = S::NETWORK;
let block_hash = index::block_id(&txn, b);
let mut batch_id = ReportDb::<S>::acquire_batch_id(&mut txn, b);
let mut batch_id = ReportDb::<S>::acquire_batch_id(&mut txn);
// start with empty batch
let mut batches = vec![Batch {
network,
id: batch_id,
block: BlockHash(block_hash),
instructions: vec![],
}];
let mut batches = vec![Batch { network, id: batch_id, instructions: vec![] }];
// We also track the return information for the InInstructions within a Batch in case
// they error
let mut return_information = vec![vec![]];
@@ -131,15 +126,10 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
let in_instruction = batch.instructions.pop().unwrap();
// bump the id for the new batch
batch_id = ReportDb::<S>::acquire_batch_id(&mut txn, b);
batch_id = ReportDb::<S>::acquire_batch_id(&mut txn);
// make a new batch with this instruction included
batches.push(Batch {
network,
id: batch_id,
block: BlockHash(block_hash),
instructions: vec![in_instruction],
});
batches.push(Batch { network, id: batch_id, instructions: vec![in_instruction] });
// Since we're allocating a new batch, allocate a new set of return addresses for it
return_information.push(vec![]);
}
@@ -152,10 +142,17 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
.push(return_address.map(|address| ReturnInformation { address, balance }));
}
// Save the return addresses to the database
// Now that we've finalized the Batches, save the information for each to the database
assert_eq!(batches.len(), return_information.len());
for (batch, return_information) in batches.iter().zip(&return_information) {
assert_eq!(batch.instructions.len(), return_information.len());
ReportDb::<S>::save_batch_info(
&mut txn,
batch.id,
block_number,
session_to_sign_batch,
Blake2b::<U32>::digest(batch.instructions.encode()).into(),
);
ReportDb::<S>::save_external_key_for_session_to_sign_batch(
&mut txn,
batch.id,
@@ -171,7 +168,7 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for ReportTask<D, S> {
}
// Update the next to potentially report block
ReportDb::<S>::set_next_to_potentially_report_block(&mut txn, b + 1);
ReportDb::<S>::set_next_to_potentially_report_block(&mut txn, block_number + 1);
txn.commit();
}