Skip to content

Commit c63a41f

Browse files
committed
Add verification to replacement messages
1 parent 52781d6 commit c63a41f

File tree

1 file changed

+52
-4
lines changed

1 file changed

+52
-4
lines changed

crates/batcher/src/lib.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,22 +1115,69 @@ impl Batcher {
11151115
};
11161116

11171117
let original_max_fee = entry.nonced_verification_data.max_fee;
1118-
if original_max_fee > replacement_max_fee {
1118+
// Require 10% fee increase to prevent DoS attacks. While this could theoretically overflow,
1119+
// it would require an attacker to have an impractical amount of Ethereum to reach U256::MAX
1120+
let min_required_fee = original_max_fee + (original_max_fee / U256::from(10)); // 10% increase (1.1x)
1121+
if replacement_max_fee < min_required_fee {
11191122
drop(batch_state_guard);
11201123
drop(user_state_guard);
1121-
warn!("Invalid replacement message for address {addr}, had max fee: {original_max_fee:?}, received fee: {replacement_max_fee:?}");
1124+
info!("Replacement message fee increase too small for address {addr}. Original: {original_max_fee:?}, received: {replacement_max_fee:?}, minimum required: {min_required_fee:?}");
11221125
send_message(
11231126
ws_conn_sink.clone(),
11241127
SubmitProofResponseMessage::InvalidReplacementMessage,
11251128
)
11261129
.await;
11271130
self.metrics
1128-
.user_error(&["invalid_replacement_message", ""]);
1131+
.user_error(&["insufficient_fee_increase", ""]);
11291132
return;
11301133
}
11311134

11321135
info!("Replacing message for address {addr} with nonce {nonce} and max fee {replacement_max_fee}");
11331136

1137+
// When pre-verification is enabled, verify the replacement proof
1138+
if self.pre_verification_is_enabled {
1139+
let verification_data = &nonced_verification_data.verification_data;
1140+
if self
1141+
.is_verifier_disabled(verification_data.proving_system)
1142+
.await
1143+
{
1144+
drop(batch_state_guard);
1145+
drop(user_state_guard);
1146+
warn!(
1147+
"Verifier for proving system {} is disabled for replacement message",
1148+
verification_data.proving_system
1149+
);
1150+
send_message(
1151+
ws_conn_sink.clone(),
1152+
SubmitProofResponseMessage::InvalidProof(ProofInvalidReason::DisabledVerifier(
1153+
verification_data.proving_system,
1154+
)),
1155+
)
1156+
.await;
1157+
self.metrics.user_error(&[
1158+
"disabled_verifier",
1159+
&format!("{}", verification_data.proving_system),
1160+
]);
1161+
return;
1162+
}
1163+
1164+
if !zk_utils::verify(verification_data).await {
1165+
drop(batch_state_guard);
1166+
drop(user_state_guard);
1167+
error!("Invalid replacement proof detected. Verification failed");
1168+
send_message(
1169+
ws_conn_sink.clone(),
1170+
SubmitProofResponseMessage::InvalidProof(ProofInvalidReason::RejectedProof),
1171+
)
1172+
.await;
1173+
self.metrics.user_error(&[
1174+
"rejected_proof",
1175+
&format!("{}", verification_data.proving_system),
1176+
]);
1177+
return;
1178+
}
1179+
}
1180+
11341181
// The replacement entry is built from the old entry and validated for then to be replaced
11351182
let mut replacement_entry = entry.clone();
11361183
replacement_entry.signature = signature;
@@ -1157,7 +1204,8 @@ impl Batcher {
11571204

11581205
replacement_entry.messaging_sink = Some(ws_conn_sink.clone());
11591206
if !batch_state_guard.replacement_entry_is_valid(&replacement_entry) {
1160-
std::mem::drop(batch_state_guard);
1207+
drop(batch_state_guard);
1208+
drop(user_state_guard);
11611209
warn!("Invalid replacement message");
11621210
send_message(
11631211
ws_conn_sink.clone(),

0 commit comments

Comments
 (0)