mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 03:59:22 +00:00
Add a UID function to messages
When we receive messages, we're provided with a message ID we can use to prevent handling an item multiple times. That doesn't prevent us from *sending* an item multiple times though. Thanks to the UID system, we can now not send if already present. Alternatively, we can remove the ordered message ID for just the UID, allowing duplicates to be sent without issue, and handled on the receiving end.
This commit is contained in:
parent
09d96822ca
commit
cc531d630e
5 changed files with 173 additions and 12 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -6655,6 +6655,7 @@ dependencies = [
|
||||||
name = "processor-messages"
|
name = "processor-messages"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
"dkg",
|
"dkg",
|
||||||
"in-instructions-primitives",
|
"in-instructions-primitives",
|
||||||
"serai-primitives",
|
"serai-primitives",
|
||||||
|
|
|
@ -17,6 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||||
zeroize = { version = "1", features = ["derive"] }
|
zeroize = { version = "1", features = ["derive"] }
|
||||||
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
bincode = "1"
|
||||||
|
|
||||||
dkg = { path = "../../crypto/dkg", features = ["serde"] }
|
dkg = { path = "../../crypto/dkg", features = ["serde"] }
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use dkg::{Participant, ThresholdParams};
|
use dkg::{Participant, ThresholdParams};
|
||||||
|
|
||||||
use serai_primitives::BlockHash;
|
use serai_primitives::{BlockHash, NetworkId};
|
||||||
use in_instructions_primitives::SignedBatch;
|
use in_instructions_primitives::SignedBatch;
|
||||||
use tokens_primitives::OutInstructionWithBalance;
|
use tokens_primitives::OutInstructionWithBalance;
|
||||||
use validator_sets_primitives::{ValidatorSet, KeyPair};
|
use validator_sets_primitives::{ValidatorSet, KeyPair};
|
||||||
|
@ -76,16 +76,6 @@ pub mod sign {
|
||||||
Completed { key: Vec<u8>, id: [u8; 32], tx: Vec<u8> },
|
Completed { key: Vec<u8>, id: [u8; 32], tx: Vec<u8> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, Serialize, Deserialize)]
|
|
||||||
pub enum ProcessorMessage {
|
|
||||||
// Created preprocess for the specified signing protocol.
|
|
||||||
Preprocess { id: SignId, preprocess: Vec<u8> },
|
|
||||||
// Signed share for the specified signing protocol.
|
|
||||||
Share { id: SignId, share: Vec<u8> },
|
|
||||||
// Completed a signing protocol already.
|
|
||||||
Completed { key: Vec<u8>, id: [u8; 32], tx: Vec<u8> },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CoordinatorMessage {
|
impl CoordinatorMessage {
|
||||||
pub fn required_block(&self) -> Option<BlockHash> {
|
pub fn required_block(&self) -> Option<BlockHash> {
|
||||||
None
|
None
|
||||||
|
@ -100,6 +90,17 @@ pub mod sign {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, Serialize, Deserialize)]
|
||||||
|
pub enum ProcessorMessage {
|
||||||
|
// Created preprocess for the specified signing protocol.
|
||||||
|
Preprocess { id: SignId, preprocess: Vec<u8> },
|
||||||
|
// Signed share for the specified signing protocol.
|
||||||
|
Share { id: SignId, share: Vec<u8> },
|
||||||
|
// Completed a signing protocol already.
|
||||||
|
// TODO: Move this to SignId
|
||||||
|
Completed { key: Vec<u8>, id: [u8; 32], tx: Vec<u8> },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod coordinator {
|
pub mod coordinator {
|
||||||
|
@ -134,7 +135,7 @@ pub mod coordinator {
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, Serialize, Deserialize)]
|
||||||
pub enum ProcessorMessage {
|
pub enum ProcessorMessage {
|
||||||
SubstrateBlockAck { block: u64, plans: Vec<[u8; 32]> },
|
SubstrateBlockAck { network: NetworkId, block: u64, plans: Vec<[u8; 32]> },
|
||||||
BatchPreprocess { id: SignId, preprocess: Vec<u8> },
|
BatchPreprocess { id: SignId, preprocess: Vec<u8> },
|
||||||
BatchShare { id: SignId, share: [u8; 32] },
|
BatchShare { id: SignId, share: [u8; 32] },
|
||||||
}
|
}
|
||||||
|
@ -152,6 +153,7 @@ pub mod substrate {
|
||||||
},
|
},
|
||||||
SubstrateBlock {
|
SubstrateBlock {
|
||||||
context: SubstrateContext,
|
context: SubstrateContext,
|
||||||
|
network: NetworkId,
|
||||||
block: u64,
|
block: u64,
|
||||||
key: Vec<u8>,
|
key: Vec<u8>,
|
||||||
burns: Vec<OutInstructionWithBalance>,
|
burns: Vec<OutInstructionWithBalance>,
|
||||||
|
@ -207,3 +209,153 @@ pub enum ProcessorMessage {
|
||||||
Coordinator(coordinator::ProcessorMessage),
|
Coordinator(coordinator::ProcessorMessage),
|
||||||
Substrate(substrate::ProcessorMessage),
|
Substrate(substrate::ProcessorMessage),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COORDINATOR_UID: u8 = 0;
|
||||||
|
const PROCESSSOR_UID: u8 = 1;
|
||||||
|
|
||||||
|
const TYPE_KEY_GEN_UID: u8 = 2;
|
||||||
|
const TYPE_SIGN_UID: u8 = 3;
|
||||||
|
const TYPE_COORDINATOR_UID: u8 = 4;
|
||||||
|
const TYPE_SUBSTRATE_UID: u8 = 5;
|
||||||
|
|
||||||
|
impl CoordinatorMessage {
|
||||||
|
/// A unique ID for this message, which should be unique across the validator's entire system,
|
||||||
|
/// including all of its processors.
|
||||||
|
///
|
||||||
|
/// This doesn't use H(msg.serialize()) as it's meant to be unique to intent, not unique to
|
||||||
|
/// values. While the values should be consistent per intent, that assumption isn't required
|
||||||
|
/// here.
|
||||||
|
// TODO: Should this use borsh intead of bincode?
|
||||||
|
pub fn uid(&self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
CoordinatorMessage::KeyGen(msg) => {
|
||||||
|
// Unique since key gen ID embeds the validator set and attempt
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
key_gen::CoordinatorMessage::GenerateKey { id, .. } => (0, id),
|
||||||
|
key_gen::CoordinatorMessage::Commitments { id, .. } => (1, id),
|
||||||
|
key_gen::CoordinatorMessage::Shares { id, .. } => (2, id),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![COORDINATOR_UID, TYPE_KEY_GEN_UID, sub];
|
||||||
|
res.extend(&bincode::serialize(id).unwrap());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
CoordinatorMessage::Sign(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since SignId includes a hash of the coin, and specific transaction info
|
||||||
|
sign::CoordinatorMessage::Preprocesses { id, .. } => (0, bincode::serialize(id).unwrap()),
|
||||||
|
sign::CoordinatorMessage::Shares { id, .. } => (1, bincode::serialize(id).unwrap()),
|
||||||
|
sign::CoordinatorMessage::Reattempt { id } => (2, bincode::serialize(id).unwrap()),
|
||||||
|
// TODO: This doesn't embed the attempt. Accordingly, multiple distinct completions will
|
||||||
|
// be flattened. This isn't acceptable.
|
||||||
|
sign::CoordinatorMessage::Completed { id, .. } => (3, id.to_vec()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![COORDINATOR_UID, TYPE_SIGN_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
CoordinatorMessage::Coordinator(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since this embeds the batch ID (hash of it, including its network) and attempt
|
||||||
|
coordinator::CoordinatorMessage::BatchPreprocesses { id, .. } => {
|
||||||
|
(0, bincode::serialize(id).unwrap())
|
||||||
|
}
|
||||||
|
coordinator::CoordinatorMessage::BatchShares { id, .. } => {
|
||||||
|
(1, bincode::serialize(id).unwrap())
|
||||||
|
}
|
||||||
|
coordinator::CoordinatorMessage::BatchReattempt { id, .. } => {
|
||||||
|
(2, bincode::serialize(id).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![COORDINATOR_UID, TYPE_COORDINATOR_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
CoordinatorMessage::Substrate(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since there's only one key pair for a set
|
||||||
|
substrate::CoordinatorMessage::ConfirmKeyPair { set, .. } => {
|
||||||
|
(0, bincode::serialize(set).unwrap())
|
||||||
|
}
|
||||||
|
substrate::CoordinatorMessage::SubstrateBlock { network, block, .. } => {
|
||||||
|
(1, bincode::serialize(&(network, block)).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![COORDINATOR_UID, TYPE_SUBSTRATE_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessorMessage {
|
||||||
|
/// A unique ID for this message, which should be unique across the validator's entire system,
|
||||||
|
/// including all of its processors.
|
||||||
|
///
|
||||||
|
/// This doesn't use H(msg.serialize()) as it's meant to be unique to intent, not unique to
|
||||||
|
/// values. While the values should be consistent per intent, that assumption isn't required
|
||||||
|
/// here.
|
||||||
|
pub fn uid(&self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
ProcessorMessage::KeyGen(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since KeyGenId
|
||||||
|
key_gen::ProcessorMessage::Commitments { id, .. } => (0, id),
|
||||||
|
key_gen::ProcessorMessage::Shares { id, .. } => (1, id),
|
||||||
|
key_gen::ProcessorMessage::GeneratedKeyPair { id, .. } => (2, id),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![PROCESSSOR_UID, TYPE_KEY_GEN_UID, sub];
|
||||||
|
res.extend(&bincode::serialize(id).unwrap());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
ProcessorMessage::Sign(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since SignId
|
||||||
|
sign::ProcessorMessage::Preprocess { id, .. } => (0, bincode::serialize(id).unwrap()),
|
||||||
|
sign::ProcessorMessage::Share { id, .. } => (1, bincode::serialize(id).unwrap()),
|
||||||
|
// Unique since a processor will only sign a TX once
|
||||||
|
sign::ProcessorMessage::Completed { id, .. } => (2, id.to_vec()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![PROCESSSOR_UID, TYPE_SIGN_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
ProcessorMessage::Coordinator(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
coordinator::ProcessorMessage::SubstrateBlockAck { network, block, .. } => {
|
||||||
|
(0, bincode::serialize(&(network, block)).unwrap())
|
||||||
|
}
|
||||||
|
// Unique since SignId
|
||||||
|
coordinator::ProcessorMessage::BatchPreprocess { id, .. } => {
|
||||||
|
(1, bincode::serialize(id).unwrap())
|
||||||
|
}
|
||||||
|
coordinator::ProcessorMessage::BatchShare { id, .. } => {
|
||||||
|
(2, bincode::serialize(id).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![PROCESSSOR_UID, TYPE_COORDINATOR_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
ProcessorMessage::Substrate(msg) => {
|
||||||
|
let (sub, id) = match msg {
|
||||||
|
// Unique since network and ID binding
|
||||||
|
substrate::ProcessorMessage::Update { batch, .. } => {
|
||||||
|
(0, bincode::serialize(&(batch.batch.network, batch.batch.id)).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut res = vec![PROCESSSOR_UID, TYPE_SUBSTRATE_UID, sub];
|
||||||
|
res.extend(&id);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -383,6 +383,7 @@ async fn handle_coordinator_msg<D: Db, C: Coin, Co: Coordinator>(
|
||||||
|
|
||||||
messages::substrate::CoordinatorMessage::SubstrateBlock {
|
messages::substrate::CoordinatorMessage::SubstrateBlock {
|
||||||
context,
|
context,
|
||||||
|
network,
|
||||||
block,
|
block,
|
||||||
key: key_vec,
|
key: key_vec,
|
||||||
burns,
|
burns,
|
||||||
|
@ -408,7 +409,11 @@ async fn handle_coordinator_msg<D: Db, C: Coin, Co: Coordinator>(
|
||||||
instruction: OutInstruction { address, data },
|
instruction: OutInstruction { address, data },
|
||||||
balance,
|
balance,
|
||||||
} = out;
|
} = out;
|
||||||
|
// TODO: Check network is this coin's network
|
||||||
|
assert_eq!(balance.coin.network(), network);
|
||||||
|
|
||||||
if let Ok(address) = C::Address::try_from(address.consume()) {
|
if let Ok(address) = C::Address::try_from(address.consume()) {
|
||||||
|
// TODO: Add coin to payment
|
||||||
payments.push(Payment {
|
payments.push(Payment {
|
||||||
address,
|
address,
|
||||||
data: data.map(|data| data.consume()),
|
data: data.map(|data| data.consume()),
|
||||||
|
@ -426,6 +431,7 @@ async fn handle_coordinator_msg<D: Db, C: Coin, Co: Coordinator>(
|
||||||
coordinator
|
coordinator
|
||||||
.send(ProcessorMessage::Coordinator(
|
.send(ProcessorMessage::Coordinator(
|
||||||
messages::coordinator::ProcessorMessage::SubstrateBlockAck {
|
messages::coordinator::ProcessorMessage::SubstrateBlockAck {
|
||||||
|
network,
|
||||||
block,
|
block,
|
||||||
plans: plans.iter().map(|plan| plan.id()).collect(),
|
plans: plans.iter().map(|plan| plan.id()).collect(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl<C: Coin> Plan<C> {
|
||||||
pub fn transcript(&self) -> RecommendedTranscript {
|
pub fn transcript(&self) -> RecommendedTranscript {
|
||||||
let mut transcript = RecommendedTranscript::new(b"Serai Processor Plan ID");
|
let mut transcript = RecommendedTranscript::new(b"Serai Processor Plan ID");
|
||||||
transcript.domain_separate(b"meta");
|
transcript.domain_separate(b"meta");
|
||||||
|
transcript.append_message(b"network", C::ID);
|
||||||
transcript.append_message(b"key", self.key.to_bytes());
|
transcript.append_message(b"key", self.key.to_bytes());
|
||||||
|
|
||||||
transcript.domain_separate(b"inputs");
|
transcript.domain_separate(b"inputs");
|
||||||
|
|
Loading…
Reference in a new issue