-
Notifications
You must be signed in to change notification settings - Fork 39
/
batch.rs
175 lines (154 loc) · 5.23 KB
/
batch.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use super::Signed;
use crate::{
attester,
validator::{Genesis, Payload},
};
use anyhow::{ensure, Context as _};
use zksync_consensus_utils::enum_util::Variant;
/// A batch of L2 blocks used for the peers to fetch and keep in sync.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct SyncBatch {
/// The number of the batch.
pub number: BatchNumber,
/// The payloads of the blocks the batch contains.
pub payloads: Vec<Payload>,
/// The proof of the batch.
pub proof: Vec<u8>,
}
impl PartialOrd for SyncBatch {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.number.partial_cmp(&other.number)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
/// A batch number.
pub struct BatchNumber(pub u64);
impl BatchNumber {
/// Increment the batch number.
pub fn next(&self) -> BatchNumber {
BatchNumber(self.0.checked_add(1).unwrap())
}
/// Returns the previous batch number.
pub fn prev(self) -> Option<Self> {
Some(Self(self.0.checked_sub(1)?))
}
}
impl std::fmt::Display for BatchNumber {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, formatter)
}
}
impl std::ops::Add<u64> for BatchNumber {
type Output = BatchNumber;
fn add(self, n: u64) -> Self {
Self(self.0.checked_add(n).unwrap())
}
}
/// A message containing information about a batch of blocks.
/// It is signed by the attesters and then propagated through the gossip network.
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd)]
pub struct Batch {
/// Header of the batch.
pub number: BatchNumber,
// TODO: Hash of the batch.
}
/// A certificate for a batch of L2 blocks to be sent to L1.
/// It contains the signatures of the attesters that signed the batch.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BatchQC {
/// The signatures of the signed L1 batches from all the attesters who signed it.
pub signatures: attester::MultiSig,
/// The message that was signed.
pub message: Batch,
}
/// Error returned by `BatchQC::verify()` if the signature is invalid.
#[derive(thiserror::Error, Debug)]
pub enum BatchQCVerifyError {
/// Bad signature.
#[error("bad signature: {0:#}")]
BadSignature(#[source] anyhow::Error),
/// Not enough signers.
#[error("not enough signers: got {got}, want {want}")]
NotEnoughSigners {
/// Got signers.
got: u64,
/// Want signers.
want: u64,
},
/// Bad signer set.
#[error("signers set doesn't match genesis")]
BadSignersSet,
/// No attester committee in genesis.
#[error("No attester committee in genesis")]
AttestersNotInGenesis,
}
/// Error returned by `BatchQC::add()` if the signature is invalid.
#[derive(thiserror::Error, Debug)]
pub enum BatchQCAddError {
/// Inconsistent messages.
#[error("Trying to add signature for a different message")]
InconsistentMessages,
/// Signer not present in the committee.
#[error("Signer not in committee: {signer:?}")]
SignerNotInCommittee {
/// Signer of the message.
signer: Box<attester::PublicKey>,
},
/// Message already present in BatchQC.
#[error("Message already signed for BatchQC")]
Exists,
}
impl BatchQC {
/// Create a new empty instance for a given `Batch` message.
pub fn new(message: Batch) -> anyhow::Result<Self> {
Ok(Self {
message,
signatures: attester::MultiSig::default(),
})
}
/// Add a attester's signature.
/// Signature is assumed to be already verified.
pub fn add(&mut self, msg: &Signed<Batch>, genesis: &Genesis) -> anyhow::Result<()> {
use BatchQCAddError as Error;
let committee = genesis
.attesters
.as_ref()
.context("no attester committee in genesis")?;
ensure!(self.message == msg.msg, Error::InconsistentMessages);
ensure!(!self.signatures.contains(&msg.key), Error::Exists);
ensure!(
committee.contains(&msg.key),
Error::SignerNotInCommittee {
signer: Box::new(msg.key.clone()),
}
);
self.signatures.add(msg.key.clone(), msg.sig.clone());
Ok(())
}
/// Verifies the signature of the BatchQC.
pub fn verify(&self, genesis: &Genesis) -> Result<(), BatchQCVerifyError> {
use BatchQCVerifyError as Error;
let attesters = genesis
.attesters
.as_ref()
.ok_or(Error::AttestersNotInGenesis)?;
// Verify that all signers are attesters.
for pk in self.signatures.keys() {
if !attesters.contains(pk) {
return Err(Error::BadSignersSet);
}
}
// Verify that the signer's weight is sufficient.
let weight = attesters.weight_of_keys(self.signatures.keys());
let threshold = attesters.threshold();
if weight < threshold {
return Err(Error::NotEnoughSigners {
got: weight,
want: threshold,
});
}
self.signatures
.verify_msg(&self.message.clone().insert())
.map_err(Error::BadSignature)
}
}