mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Add Batch messages from processor, verify Batchs published on-chain
Renames Update to SignedBatch. Checks Batch equality via a hash of the InInstructions. That prevents needing to keep the Batch in node state or TX introspect.
This commit is contained in:
@@ -10,7 +10,7 @@ use messages::{sign::SignId, SubstrateContext};
|
||||
use serai_client::{
|
||||
primitives::{BlockHash, crypto::RuntimePublic, PublicKey, SeraiAddress, NetworkId},
|
||||
in_instructions::primitives::{
|
||||
InInstruction, InInstructionWithBalance, SignedBatch, batch_message,
|
||||
InInstruction, InInstructionWithBalance, Batch, SignedBatch, batch_message,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::{*, tests::*};
|
||||
pub(crate) async fn recv_batch_preprocesses(
|
||||
coordinators: &mut [Coordinator],
|
||||
substrate_key: &[u8; 32],
|
||||
batch: &Batch,
|
||||
attempt: u32,
|
||||
) -> (SignId, HashMap<Participant, Vec<u8>>) {
|
||||
let mut id = None;
|
||||
@@ -27,8 +28,18 @@ pub(crate) async fn recv_batch_preprocesses(
|
||||
for (i, coordinator) in coordinators.iter_mut().enumerate() {
|
||||
let i = Participant::new(u16::try_from(i).unwrap() + 1).unwrap();
|
||||
|
||||
let msg = coordinator.recv_message().await;
|
||||
match msg {
|
||||
if attempt == 0 {
|
||||
match coordinator.recv_message().await {
|
||||
messages::ProcessorMessage::Substrate(messages::substrate::ProcessorMessage::Batch {
|
||||
batch: sent_batch,
|
||||
}) => {
|
||||
assert_eq!(&sent_batch, batch);
|
||||
}
|
||||
_ => panic!("processor didn't send batch"),
|
||||
}
|
||||
}
|
||||
|
||||
match coordinator.recv_message().await {
|
||||
messages::ProcessorMessage::Coordinator(
|
||||
messages::coordinator::ProcessorMessage::BatchPreprocess {
|
||||
id: this_id,
|
||||
@@ -122,9 +133,9 @@ pub(crate) async fn sign_batch(
|
||||
|
||||
if preprocesses.contains_key(&i) {
|
||||
match coordinator.recv_message().await {
|
||||
messages::ProcessorMessage::Substrate(messages::substrate::ProcessorMessage::Update {
|
||||
batch: this_batch,
|
||||
}) => {
|
||||
messages::ProcessorMessage::Substrate(
|
||||
messages::substrate::ProcessorMessage::SignedBatch { batch: this_batch },
|
||||
) => {
|
||||
if batch.is_none() {
|
||||
assert!(PublicKey::from_raw(key)
|
||||
.verify(&batch_message(&this_batch.batch), &this_batch.signature));
|
||||
@@ -231,9 +242,23 @@ fn batch_test() {
|
||||
// The scanner works on a 5s interval, so this leaves a few s for any processing/latency
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
|
||||
let expected_batch = Batch {
|
||||
network,
|
||||
id: i,
|
||||
block: BlockHash(block_with_tx.unwrap()),
|
||||
instructions: if let Some(instruction) = instruction {
|
||||
vec![InInstructionWithBalance { instruction, balance: balance_sent }]
|
||||
} else {
|
||||
// This shouldn't have an instruction as we didn't add any data into the TX we sent
|
||||
// Empty batches remain valuable as they let us achieve consensus on the block and spend
|
||||
// contained outputs
|
||||
vec![]
|
||||
},
|
||||
};
|
||||
|
||||
// Make sure the proceessors picked it up by checking they're trying to sign a batch for it
|
||||
let (mut id, mut preprocesses) =
|
||||
recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, 0).await;
|
||||
recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, &expected_batch, 0).await;
|
||||
// Trigger a random amount of re-attempts
|
||||
for attempt in 1 ..= u32::try_from(OsRng.next_u64() % 4).unwrap() {
|
||||
// TODO: Double check how the processor handles this ID field
|
||||
@@ -247,27 +272,15 @@ fn batch_test() {
|
||||
.await;
|
||||
}
|
||||
(id, preprocesses) =
|
||||
recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, attempt).await;
|
||||
recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, &expected_batch, attempt)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Continue with signing the batch
|
||||
let batch = sign_batch(&mut coordinators, key_pair.0 .0, id, preprocesses).await;
|
||||
|
||||
// Check it
|
||||
assert_eq!(batch.batch.network, network);
|
||||
assert_eq!(batch.batch.id, i);
|
||||
assert_eq!(batch.batch.block, BlockHash(block_with_tx.unwrap()));
|
||||
if let Some(instruction) = instruction {
|
||||
assert_eq!(
|
||||
batch.batch.instructions,
|
||||
vec![InInstructionWithBalance { instruction, balance: balance_sent }]
|
||||
);
|
||||
} else {
|
||||
// This shouldn't have an instruction as we didn't add any data into the TX we sent
|
||||
// Empty batches remain valuable as they let us achieve consensus on the block and spend
|
||||
// contained outputs
|
||||
assert!(batch.batch.instructions.is_empty());
|
||||
}
|
||||
assert_eq!(batch.batch, expected_batch);
|
||||
|
||||
// Fire a SubstrateBlock
|
||||
let serai_time =
|
||||
|
||||
@@ -9,6 +9,7 @@ use messages::{sign::SignId, SubstrateContext};
|
||||
|
||||
use serai_client::{
|
||||
primitives::{BlockHash, NetworkId},
|
||||
in_instructions::primitives::Batch,
|
||||
tokens::primitives::{OutInstruction, OutInstructionWithBalance},
|
||||
};
|
||||
|
||||
@@ -186,17 +187,18 @@ fn send_test() {
|
||||
// The scanner works on a 5s interval, so this leaves a few s for any processing/latency
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
|
||||
let expected_batch =
|
||||
Batch { network, id: 0, block: BlockHash(block_with_tx.unwrap()), instructions: vec![] };
|
||||
|
||||
// Make sure the proceessors picked it up by checking they're trying to sign a batch for it
|
||||
let (id, preprocesses) = recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, 0).await;
|
||||
let (id, preprocesses) =
|
||||
recv_batch_preprocesses(&mut coordinators, &key_pair.0 .0, &expected_batch, 0).await;
|
||||
|
||||
// Continue with signing the batch
|
||||
let batch = sign_batch(&mut coordinators, key_pair.0 .0, id, preprocesses).await;
|
||||
|
||||
// Check it
|
||||
assert_eq!(batch.batch.network, network);
|
||||
assert_eq!(batch.batch.id, 0);
|
||||
assert_eq!(batch.batch.block, BlockHash(block_with_tx.unwrap()));
|
||||
assert!(batch.batch.instructions.is_empty());
|
||||
assert_eq!(batch.batch, expected_batch);
|
||||
|
||||
// Fire a SubstrateBlock with a burn
|
||||
let substrate_block_num = (OsRng.next_u64() % 4_000_000_000u64) + 1;
|
||||
|
||||
Reference in New Issue
Block a user