Skip to content

Commit 881a5b8

Browse files
committed
refactor: batcher errors
1 parent f8b33ac commit 881a5b8

File tree

10 files changed

+295
-459
lines changed

10 files changed

+295
-459
lines changed

crates/batcher/src/lib.rs

Lines changed: 113 additions & 228 deletions
Large diffs are not rendered by default.

crates/batcher/src/types/batch_state.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl BatchState {
203203
pub(crate) fn replacement_entry_is_valid(
204204
&mut self,
205205
replacement_entry: &BatchQueueEntry,
206-
) -> bool {
206+
) -> Result<(), (U256, U256)> {
207207
let replacement_max_fee = replacement_entry.nonced_verification_data.max_fee;
208208
let nonce = replacement_entry.nonced_verification_data.nonce;
209209
let sender = replacement_entry.sender;
@@ -214,11 +214,18 @@ impl BatchState {
214214
);
215215

216216
// it is a valid entry only if there is no entry with the same sender, lower nonce and a lower fee
217-
!self.batch_queue.iter().any(|(entry, _)| {
217+
if let Some((entry, _)) = self.batch_queue.iter().find(|(entry, _)| {
218218
entry.sender == sender
219219
&& entry.nonced_verification_data.nonce < nonce
220220
&& entry.nonced_verification_data.max_fee < replacement_max_fee
221-
})
221+
}) {
222+
Err((
223+
entry.nonced_verification_data.nonce,
224+
entry.nonced_verification_data.max_fee,
225+
))
226+
} else {
227+
Ok(())
228+
}
222229
}
223230

224231
/// Updates or removes a user's state when their latest proof entry is removed from the batch queue.

crates/cli/src/main.rs

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ async fn main() -> Result<(), AlignedError> {
504504
PathBuf::from(&submit_args.batch_inclusion_data_directory_path);
505505

506506
std::fs::create_dir_all(&batch_inclusion_data_directory_path).map_err(|e| {
507-
SubmitError::IoError(batch_inclusion_data_directory_path.clone(), e)
507+
SubmitError::IoError(batch_inclusion_data_directory_path.clone(), e.to_string())
508508
})?;
509509

510510
let eth_rpc_url = submit_args.eth_rpc_url.clone();
@@ -542,22 +542,22 @@ async fn main() -> Result<(), AlignedError> {
542542
wallet = wallet.with_chain_id(chain_id);
543543

544544
let nonce = match &submit_args.nonce {
545-
Some(nonce) => U256::from_dec_str(nonce).map_err(|_| SubmitError::InvalidNonce)?,
545+
Some(nonce) => U256::from_dec_str(nonce).expect("A valid nonce number"),
546546
None => {
547547
get_nonce_from_batcher(submit_args.network.clone().into(), wallet.address())
548548
.await
549549
.map_err(|e| match e {
550550
aligned_sdk::common::errors::GetNonceError::EthRpcError(e) => {
551-
SubmitError::GetNonceError(e)
551+
SubmitError::EthereumProviderError(e)
552552
}
553553
aligned_sdk::common::errors::GetNonceError::ConnectionFailed(e) => {
554-
SubmitError::GenericError(e)
554+
SubmitError::WebSocketConnectionError(e)
555555
}
556556
aligned_sdk::common::errors::GetNonceError::InvalidRequest(e) => {
557557
SubmitError::GenericError(e)
558558
}
559559
aligned_sdk::common::errors::GetNonceError::SerializationError(e) => {
560-
SubmitError::GenericError(e)
560+
SubmitError::SerializationError(e)
561561
}
562562
aligned_sdk::common::errors::GetNonceError::ProtocolMismatch {
563563
current,
@@ -612,8 +612,7 @@ async fn main() -> Result<(), AlignedError> {
612612
.insert(aligned_verification_data.batch_merkle_root);
613613
}
614614
Err(e) => {
615-
warn!("Error while submitting proof: {:?}", e);
616-
handle_submit_err(e).await;
615+
warn!("Error while submitting proof: {}", e);
617616
return Ok(());
618617
}
619618
};
@@ -646,13 +645,16 @@ async fn main() -> Result<(), AlignedError> {
646645
VerifyProofOnchain(verify_inclusion_args) => {
647646
let batch_inclusion_file =
648647
File::open(verify_inclusion_args.batch_inclusion_data.clone()).map_err(|e| {
649-
SubmitError::IoError(verify_inclusion_args.batch_inclusion_data.clone(), e)
648+
SubmitError::IoError(
649+
verify_inclusion_args.batch_inclusion_data.clone(),
650+
e.to_string(),
651+
)
650652
})?;
651653

652654
let reader = BufReader::new(batch_inclusion_file);
653655

654-
let aligned_verification_data: AlignedVerificationData =
655-
cbor_deserialize(reader).map_err(SubmitError::SerializationError)?;
656+
let aligned_verification_data: AlignedVerificationData = cbor_deserialize(reader)
657+
.map_err(|e| SubmitError::SerializationError(e.to_string()))?;
656658

657659
info!("Verifying response data matches sent proof data...");
658660
let response = verification_layer::is_proof_verified(
@@ -677,10 +679,10 @@ async fn main() -> Result<(), AlignedError> {
677679
info!("Commitment: {}", hex::encode(vk_commitment));
678680
if let Some(output_file) = args.output_file {
679681
let mut file = File::create(output_file.clone())
680-
.map_err(|e| SubmitError::IoError(output_file.clone(), e))?;
682+
.map_err(|e| SubmitError::IoError(output_file.clone(), e.to_string()))?;
681683

682684
file.write_all(hex::encode(vk_commitment).as_bytes())
683-
.map_err(|e| SubmitError::IoError(output_file.clone(), e))?;
685+
.map_err(|e| SubmitError::IoError(output_file.clone(), e.to_string()))?;
684686
}
685687
}
686688
DepositToBatcher(deposit_to_batcher_args) => {
@@ -940,27 +942,8 @@ fn verification_data_from_args(args: &SubmitArgs) -> Result<VerificationData, Su
940942
})
941943
}
942944

943-
async fn handle_submit_err(err: SubmitError) {
944-
match err {
945-
SubmitError::InvalidNonce => {
946-
error!("Invalid nonce. try again");
947-
}
948-
SubmitError::ProofQueueFlushed => {
949-
error!("Batch was reset. try resubmitting the proof");
950-
}
951-
SubmitError::InvalidProof(reason) => error!("Submitted proof is invalid: {}", reason),
952-
SubmitError::InsufficientBalance(sender_address) => {
953-
error!(
954-
"Insufficient balance to pay for the transaction, address: {}",
955-
sender_address
956-
)
957-
}
958-
_ => {}
959-
}
960-
}
961-
962945
fn read_file(file_name: PathBuf) -> Result<Vec<u8>, SubmitError> {
963-
std::fs::read(&file_name).map_err(|e| SubmitError::IoError(file_name, e))
946+
std::fs::read(&file_name).map_err(|e| SubmitError::IoError(file_name, e.to_string()))
964947
}
965948

966949
fn read_file_option(param_name: &str, file_name: Option<PathBuf>) -> Result<Vec<u8>, SubmitError> {

crates/sdk/src/common/errors.rs

Lines changed: 110 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ use core::fmt;
22
use ethers::providers::ProviderError;
33
use ethers::signers::WalletError;
44
use ethers::types::transaction::eip712::Eip712Error;
5-
use ethers::types::{SignatureError, H160};
5+
use ethers::types::{SignatureError, H160, U256};
6+
use ethers::utils::format_ether;
67
use serde::{Deserialize, Serialize};
8+
use std::fmt::Display;
79
use std::io;
810
use std::path::PathBuf;
9-
use tokio_tungstenite::tungstenite::protocol::CloseFrame;
10-
11-
use crate::communication::serialization::SerializationError;
1211

1312
use super::types::ProofInvalidReason;
1413

@@ -63,12 +62,14 @@ impl fmt::Display for AlignedError {
6362
}
6463
}
6564

66-
#[derive(Debug)]
65+
#[derive(Debug, Clone, Serialize, Deserialize)]
6766
pub enum SubmitError {
68-
WebSocketConnectionError(tokio_tungstenite::tungstenite::Error),
69-
WebSocketClosedUnexpectedlyError(CloseFrame<'static>),
70-
IoError(PathBuf, io::Error),
71-
SerializationError(SerializationError),
67+
// TODO: remove the GenericError and create an error for each case
68+
GenericError(String),
69+
WebSocketConnectionError(String),
70+
WebSocketClosedUnexpectedlyError(String),
71+
IoError(PathBuf, String),
72+
SerializationError(String),
7273
EthereumProviderError(String),
7374
HexDecodingError(String),
7475
WalletSignerError(String),
@@ -82,33 +83,52 @@ pub enum SubmitError {
8283
UnexpectedBatcherResponse(String),
8384
EmptyVerificationDataCommitments,
8485
EmptyVerificationDataList,
85-
InvalidNonce,
86-
InvalidMaxFee,
86+
InvalidNonce { sent: U256, expected: U256 },
87+
InvalidMaxFee { sent: U256, required: U256 },
8788
ProofQueueFlushed,
8889
InvalidSignature,
8990
InvalidChainId,
9091
InvalidProof(ProofInvalidReason),
91-
ProofTooLarge,
92-
InvalidReplacementMessage,
93-
InsufficientBalance(H160),
94-
InvalidPaymentServiceAddress(H160, H160),
92+
InvalidReplacementMessage(ReplacementInvalidReason),
93+
InsufficientBalance { available: U256, required: U256 },
94+
BalanceUnlocked,
95+
InvalidPaymentServiceAddress { expected: H160, received: H160 },
9596
BatchSubmissionFailed(String),
96-
AddToBatchError,
9797
InvalidProofInclusionData,
98-
GetNonceError(String),
99-
BatchQueueLimitExceededError,
100-
GenericError(String),
98+
BatchQueueLimitExceeded,
99+
BatcherUnexpectedError,
101100
}
102101

103-
impl From<tokio_tungstenite::tungstenite::Error> for SubmitError {
104-
fn from(e: tokio_tungstenite::tungstenite::Error) -> Self {
105-
SubmitError::WebSocketConnectionError(e)
102+
#[derive(Debug, Clone, Serialize, Deserialize)]
103+
pub enum ReplacementInvalidReason {
104+
EntryNotFound,
105+
UnderpricedMaxFee { sent: U256, min_bump_required: U256 },
106+
ReplacementConflictWithPendingEntry { nonce: U256, max_fee: U256 },
107+
}
108+
109+
impl Display for ReplacementInvalidReason {
110+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111+
match &self {
112+
Self::EntryNotFound => write!(f, "Entry not found in state"),
113+
Self::UnderpricedMaxFee {
114+
sent,
115+
min_bump_required,
116+
} => write!(
117+
f,
118+
"Max fee does not cover replacement, sent: {}ether, min bump required {}ether",
119+
format_ether(*sent),
120+
format_ether(*min_bump_required),
121+
),
122+
Self::ReplacementConflictWithPendingEntry { nonce, max_fee } => {
123+
write!(f, "Replacement rejected: a pending entry from the same sender exists with a lower nonce and higher max fee (nonce: {}, fee: {}).", nonce, format_ether(*max_fee))
124+
}
125+
}
106126
}
107127
}
108128

109-
impl From<SerializationError> for SubmitError {
110-
fn from(e: SerializationError) -> Self {
111-
SubmitError::SerializationError(e)
129+
impl From<tokio_tungstenite::tungstenite::Error> for SubmitError {
130+
fn from(e: tokio_tungstenite::tungstenite::Error) -> Self {
131+
SubmitError::WebSocketConnectionError(e.to_string())
112132
}
113133
}
114134

@@ -140,31 +160,82 @@ impl From<VerificationError> for SubmitError {
140160
impl fmt::Display for SubmitError {
141161
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142162
match self {
163+
// General System-level Errors
164+
SubmitError::GenericError(e) => write!(f, "Generic error: {}", e),
143165
SubmitError::WebSocketConnectionError(e) => {
144166
write!(f, "WebSocket connection error: {}", e)
145167
}
146168
SubmitError::WebSocketClosedUnexpectedlyError(close_frame) => {
147169
write!(f, "WebSocket closed unexpectedly: {}", close_frame)
148170
}
171+
172+
// Serialization Networking Errors
149173
SubmitError::IoError(path, e) => write!(f, "IO error: {}: {}", path.display(), e),
150174
SubmitError::SerializationError(e) => write!(f, "Serialization error: {}", e),
175+
176+
// Ethereum Cryptographic Errors
151177
SubmitError::EthereumProviderError(e) => write!(f, "Ethereum provider error: {}", e),
152178
SubmitError::HexDecodingError(e) => write!(f, "Hex decoding error: {}", e),
153179
SubmitError::WalletSignerError(e) => write!(f, "Wallet signer error: {}", e),
180+
SubmitError::InvalidEthereumAddress(address) => {
181+
write!(f, "Invalid Ethereum address: {}", address)
182+
}
183+
SubmitError::InvalidSignature => write!(f, "Invalid Signature"),
184+
SubmitError::InvalidChainId => write!(f, "Invalid chain Id"),
185+
186+
// User Input Parameter Validation
154187
SubmitError::MissingRequiredParameter(param) => {
155188
write!(f, "Missing required parameter: {}", param)
156189
}
157190
SubmitError::UnsupportedProvingSystem(proving_system) => {
158191
write!(f, "Unsupported proving system: {}", proving_system)
159192
}
160-
SubmitError::InvalidEthereumAddress(address) => {
161-
write!(f, "Invalid Ethereum address: {}", address)
162-
}
163193
SubmitError::ProtocolVersionMismatch { current, expected } => write!(
164194
f,
165195
"Protocol version mismatch: current={}, expected={}",
166196
current, expected
167197
),
198+
SubmitError::InvalidNonce { sent, expected } => {
199+
write!(f, "Invalid nonce, sent: {}, required: {}", sent, expected)
200+
}
201+
SubmitError::InvalidMaxFee { sent, required } => write!(
202+
f,
203+
"Invalid max fee, sent: {}ether, required: {}ether",
204+
format_ether(*sent),
205+
format_ether(*required)
206+
),
207+
SubmitError::InsufficientBalance {
208+
available,
209+
required,
210+
} => {
211+
write!(
212+
f,
213+
"Insufficient balance, available: {}ether, required {}ether",
214+
format_ether(*available),
215+
format_ether(*required)
216+
)
217+
}
218+
SubmitError::BalanceUnlocked => {
219+
write!(f, "Balance is in batcher payment contract is unlocked")
220+
}
221+
SubmitError::InvalidProof(reason) => {
222+
write!(f, "Invalid proof, reason: {}", reason)
223+
}
224+
SubmitError::InvalidReplacementMessage(reason) => {
225+
write!(f, "Invalid replacement message, reason: {}", reason)
226+
}
227+
SubmitError::InvalidPaymentServiceAddress {
228+
received: received_addr,
229+
expected: expected_addr,
230+
} => {
231+
write!(
232+
f,
233+
"Invalid payment service address, received: {}, expected: {}",
234+
received_addr, expected_addr
235+
)
236+
}
237+
238+
// Batcher-related Errors
168239
SubmitError::BatchVerifiedEventStreamError(e) => {
169240
write!(f, "Batch verified event stream error: {}", e)
170241
}
@@ -182,40 +253,27 @@ impl fmt::Display for SubmitError {
182253
SubmitError::EmptyVerificationDataCommitments => {
183254
write!(f, "Verification data commitments are empty")
184255
}
185-
SubmitError::EmptyVerificationDataList => write!(f, "Verification data list is empty"),
186-
SubmitError::InvalidNonce => write!(f, "Invalid nonce"),
187-
SubmitError::InvalidMaxFee => write!(f, "Invalid max fee"),
256+
SubmitError::EmptyVerificationDataList => {
257+
write!(f, "Verification data list is empty")
258+
}
188259
SubmitError::BatchSubmissionFailed(merkle_root) => write!(
189260
f,
190261
"Could not create task with batch merkle root {}",
191262
merkle_root
192263
),
193-
SubmitError::GenericError(e) => write!(f, "Generic error: {}", e),
194-
SubmitError::InvalidSignature => write!(f, "Invalid Signature"),
195-
SubmitError::InvalidChainId => write!(f, "Invalid chain Id"),
196-
SubmitError::InvalidProof(reason) => write!(f, "Invalid proof {}", reason),
197-
SubmitError::ProofTooLarge => write!(f, "Proof too Large"),
198-
SubmitError::InvalidReplacementMessage => write!(f, "Invalid replacement message"),
199-
SubmitError::InsufficientBalance(addr) => {
200-
write!(f, "Insufficient balance, address: {}", addr)
264+
SubmitError::ProofQueueFlushed => write!(f, "Batch reset"),
265+
SubmitError::InvalidProofInclusionData => {
266+
write!(f, "Batcher responded with invalid batch inclusion data. Can't verify your proof was correctly included in the batch.")
201267
}
202-
SubmitError::InvalidPaymentServiceAddress(received_addr, expected_addr) => {
268+
SubmitError::BatchQueueLimitExceeded => {
203269
write!(
204270
f,
205-
"Invalid payment service address, received: {}, expected: {}",
206-
received_addr, expected_addr
271+
"Error while adding entry to batch, queue limit exceeded."
207272
)
208273
}
209-
SubmitError::ProofQueueFlushed => write!(f, "Batch reset"),
210-
SubmitError::AddToBatchError => write!(f, "Error while adding entry to batch"),
211-
SubmitError::InvalidProofInclusionData => {
212-
write!(f, "Batcher responded with invalid batch inclusion data. Can't verify your proof was correctly included in the batch.")
213-
}
214-
SubmitError::BatchQueueLimitExceededError => {
215-
write!(f, "Error while adding entry to batch, queue limit exeeded.")
274+
SubmitError::BatcherUnexpectedError => {
275+
write!(f, "Batcher responded with an unexpected error")
216276
}
217-
218-
SubmitError::GetNonceError(e) => write!(f, "Error while getting nonce {}", e),
219277
}
220278
}
221279
}
@@ -338,13 +396,7 @@ pub enum BalanceError {
338396
#[derive(Debug)]
339397
pub enum FileError {
340398
IoError(PathBuf, io::Error),
341-
SerializationError(SerializationError),
342-
}
343-
344-
impl From<SerializationError> for FileError {
345-
fn from(e: SerializationError) -> Self {
346-
FileError::SerializationError(e)
347-
}
399+
SerializationError(String),
348400
}
349401

350402
impl From<io::Error> for FileError {

0 commit comments

Comments
 (0)