diff --git a/coordinator/src/tributary/db.rs b/coordinator/src/tributary/db.rs index aa7fd006..998146b5 100644 --- a/coordinator/src/tributary/db.rs +++ b/coordinator/src/tributary/db.rs @@ -25,6 +25,14 @@ impl TributaryDb { self.0.get(Self::block_key(genesis)).unwrap_or(genesis.to_vec()).try_into().unwrap() } + // This shouldn't need genesis? Yet it's saner to have then quibble about. + fn batch_id_key(genesis: &[u8], ext_block: [u8; 32]) -> Vec { + Self::tributary_key(b"batch_id", [genesis, ext_block.as_ref()].concat()) + } + pub fn batch_id(getter: &G, genesis: [u8; 32], ext_block: [u8; 32]) -> Option<[u8; 32]> { + getter.get(Self::batch_id_key(&genesis, ext_block)).map(|bytes| bytes.try_into().unwrap()) + } + fn recognized_id_key(label: &'static str, genesis: [u8; 32], id: [u8; 32]) -> Vec { Self::tributary_key(b"recognized", [label.as_bytes(), genesis.as_ref(), id.as_ref()].concat()) } diff --git a/coordinator/src/tributary/scanner.rs b/coordinator/src/tributary/scanner.rs index 4b9b3168..1f872679 100644 --- a/coordinator/src/tributary/scanner.rs +++ b/coordinator/src/tributary/scanner.rs @@ -46,82 +46,86 @@ async fn handle_block( Sign, } - let mut handle = |zone, label, needed, id, attempt, mut bytes: Vec, signed: Signed| { - if zone == Zone::Dkg { - // Since Dkg doesn't have an ID, solely attempts, this should just be [0; 32] - assert_eq!(id, [0; 32], "DKG, which shouldn't have IDs, had a non-0 ID"); - } else if !TributaryDb::::recognized_id( - &txn, - match zone { - Zone::Dkg => panic!("zone was Dkg despite prior if clause handling Dkg"), + impl Zone { + fn label(&self) -> &'static str { + match self { + Zone::Dkg => { + panic!("getting the label for dkg despite dkg code paths not needing a label") + } Zone::Batch => "batch", Zone::Sign => "sign", - }, - tributary.genesis(), - id, - ) { - // TODO: Full slash - todo!(); + } } + } - // If they've already published a TX for this attempt, slash - if let Some(data) = - TributaryDb::::data(label, &txn, tributary.genesis(), id, attempt, &signed.signer) - { - if data != bytes { + let mut handle = + |zone: Zone, label, needed, id, attempt, mut bytes: Vec, signed: Signed| { + if zone == Zone::Dkg { + // Since Dkg doesn't have an ID, solely attempts, this should just be [0; 32] + assert_eq!(id, [0; 32], "DKG, which shouldn't have IDs, had a non-0 ID"); + } else if !TributaryDb::::recognized_id(&txn, zone.label(), tributary.genesis(), id) { // TODO: Full slash todo!(); } - // TODO: Slash - return None; - } + // If they've already published a TX for this attempt, slash + if let Some(data) = + TributaryDb::::data(label, &txn, tributary.genesis(), id, attempt, &signed.signer) + { + if data != bytes { + // TODO: Full slash + todo!(); + } - // If the attempt is lesser than the blockchain's, slash - let curr_attempt = TributaryDb::::attempt(&txn, tributary.genesis(), id); - if attempt < curr_attempt { - // TODO: Slash for being late - return None; - } - if attempt > curr_attempt { - // TODO: Full slash - todo!(); - } - - // Store this data - let received = TributaryDb::::set_data( - label, - &mut txn, - tributary.genesis(), - id, - attempt, - &signed.signer, - &bytes, - ); - - // If we have all the needed commitments/preprocesses/shares, tell the processor - if received == needed { - let mut data = HashMap::new(); - for validator in spec.validators().keys() { - data.insert( - spec.i(*validator).unwrap(), - if validator == &signed.signer { - bytes.split_off(0) - } else if let Some(data) = - TributaryDb::::data(label, &txn, tributary.genesis(), id, attempt, validator) - { - data - } else { - continue; - }, - ); + // TODO: Slash + return None; } - assert_eq!(data.len(), usize::from(needed)); - return Some(data); - } - None - }; + // If the attempt is lesser than the blockchain's, slash + let curr_attempt = TributaryDb::::attempt(&txn, tributary.genesis(), id); + if attempt < curr_attempt { + // TODO: Slash for being late + return None; + } + if attempt > curr_attempt { + // TODO: Full slash + todo!(); + } + + // Store this data + let received = TributaryDb::::set_data( + label, + &mut txn, + tributary.genesis(), + id, + attempt, + &signed.signer, + &bytes, + ); + + // If we have all the needed commitments/preprocesses/shares, tell the processor + if received == needed { + let mut data = HashMap::new(); + for validator in spec.validators().keys() { + data.insert( + spec.i(*validator).unwrap(), + if validator == &signed.signer { + bytes.split_off(0) + } else if let Some(data) = + TributaryDb::::data(label, &txn, tributary.genesis(), id, attempt, validator) + { + data + } else { + continue; + }, + ); + } + assert_eq!(data.len(), usize::from(needed)); + + return Some(data); + } + None + }; match tx { Transaction::DkgCommitments(attempt, bytes, signed) => { @@ -163,8 +167,25 @@ async fn handle_block( } } - // TODO - Transaction::ExternalBlock(..) => todo!(), + Transaction::ExternalBlock(block) => { + // Because this external block has been finalized, its batch ID should be authorized + + // If we didn't provide this transaction, we should halt until we do + // If we provided a distinct transaction, we should error + // If we did provide this transaction, we should've set the batch ID for the block + let batch_id = + TributaryDb::::batch_id(&txn, tributary.genesis(), block).expect(concat!( + "synced a tributary block finalizing a block in a provided transaction despite ", + "us not providing that transaction" + )); + + TributaryDb::::recognize_id( + &mut txn, + Zone::Batch.label(), + tributary.genesis(), + batch_id, + ); + } Transaction::SeraiBlock(..) => todo!(), Transaction::BatchPreprocess(data) => {