diff --git a/processor/src/substrate_signer.rs b/processor/src/substrate_signer.rs index f54f7de7..f541f4b2 100644 --- a/processor/src/substrate_signer.rs +++ b/processor/src/substrate_signer.rs @@ -4,6 +4,7 @@ use std::collections::{VecDeque, HashMap}; use rand_core::OsRng; use scale::Encode; +use transcript::{Transcript, RecommendedTranscript}; use ciphersuite::group::GroupEncoding; use frost::{ @@ -36,11 +37,21 @@ impl SubstrateSignerDb { D::key(b"SUBSTRATE_SIGNER", dst, key) } + fn sign_id_key(id: u32) -> Vec { + Self::sign_key(b"sign_id", id.to_le_bytes()) + } + fn save_sign_id(txn: &mut D::Transaction<'_>, id: u32, sign_id: [u8; 32]) { + txn.put(Self::sign_id_key(id), sign_id); + } + fn sign_id(txn: &mut D::Transaction<'_>, id: u32) -> [u8; 32] { + txn.get(Self::sign_id_key(id)).expect("completing a batch we never started").try_into().unwrap() + } + fn completed_key(id: [u8; 32]) -> Vec { Self::sign_key(b"completed", id) } fn complete(txn: &mut D::Transaction<'_>, id: [u8; 32]) { - txn.put(Self::completed_key(id), [1]); + txn.put(Self::completed_key(id), []); } fn completed(getter: &G, id: [u8; 32]) -> bool { getter.get(Self::completed_key(id)).is_some() @@ -203,9 +214,27 @@ impl SubstrateSigner { } pub async fn sign(&mut self, txn: &mut D::Transaction<'_>, batch: Batch) { - // Use the batch id as the ID - let mut id = [0u8; 32]; - id[.. 4].copy_from_slice(&batch.id.to_le_bytes()); + // Generate a unique ID to sign with + let id = { + // TODO: Add this to in-instructions primitives + let mut transcript = RecommendedTranscript::new(b"Serai Processor Batch ID"); + transcript.domain_separate(b"header"); + transcript.append_message(b"network", batch.network.encode()); + transcript.append_message(b"id", batch.id.to_le_bytes()); + transcript.append_message(b"block", batch.block.0); + + transcript.domain_separate(b"instructions"); + for instruction in &batch.instructions { + // TODO: Either properly transcript this or simply encode the entire batch (since SCALE is + // canonical) + transcript.append_message(b"instruction", instruction.encode()); + } + + let mut res = [0; 32]; + res.copy_from_slice(&transcript.challenge(b"id")[.. 32]); + res + }; + SubstrateSignerDb::::save_sign_id(txn, batch.id, id); if SubstrateSignerDb::::completed(txn, id) { debug!("Sign batch order for ID we've already completed signing"); @@ -336,19 +365,20 @@ impl SubstrateSigner { } } - pub fn batch_signed(&mut self, txn: &mut D::Transaction<'_>, batch_id: u32) { - // Convert into 32-byte ID - // TODO: Add a BatchSignId so we don't have this inefficiency - let mut id = [0u8; 32]; - id[.. 4].copy_from_slice(&batch_id.to_le_bytes()); + pub fn batch_signed(&mut self, txn: &mut D::Transaction<'_>, id: u32) { + // Safe since SubstrateSigner won't be told of the completion until the Scanner recognizes the + // block behind it, which will trigger starting the Batch + // TODO: There is a race condition between the Scanner recognizing the block and the Batch + // having signing started + let sign_id = SubstrateSignerDb::::sign_id(txn, id); // Stop trying to sign for this batch - SubstrateSignerDb::::complete(txn, id); + SubstrateSignerDb::::complete(txn, sign_id); - self.signable.remove(&id); - self.attempt.remove(&id); - self.preprocessing.remove(&id); - self.signing.remove(&id); + self.signable.remove(&sign_id); + self.attempt.remove(&sign_id); + self.preprocessing.remove(&sign_id); + self.signing.remove(&sign_id); // This doesn't emit SignedBatch because it doesn't have access to the SignedBatch // This function is expected to only be called once Substrate acknowledges this block, diff --git a/processor/src/tests/substrate_signer.rs b/processor/src/tests/substrate_signer.rs index 592ce1d7..2d1effc8 100644 --- a/processor/src/tests/substrate_signer.rs +++ b/processor/src/tests/substrate_signer.rs @@ -25,11 +25,9 @@ async fn test_substrate_signer() { let participant_one = Participant::new(1).unwrap(); let id: u32 = 5; - let mut id_arr = [0u8; 32]; - id_arr[.. 4].copy_from_slice(&id.to_le_bytes()); let block = BlockHash([0xaa; 32]); - let actual_id = - SignId { key: keys[&participant_one].group_key().to_bytes().to_vec(), id: id_arr, attempt: 0 }; + let mut actual_id = + SignId { key: keys[&participant_one].group_key().to_bytes().to_vec(), id: [0; 32], attempt: 0 }; let batch = Batch { network: NetworkId::Monero, @@ -88,6 +86,9 @@ async fn test_substrate_signer() { preprocess, }) = signers.get_mut(&i).unwrap().events.pop_front().unwrap() { + if actual_id.id == [0; 32] { + actual_id.id = id.id; + } assert_eq!(id, actual_id); assert_eq!(batch_block, block); if signing_set.contains(&i) {