Skip to content

Commit 21f45f8

Browse files
authored
v0.20.0 (#2159)
2 parents ecfef61 + 895cd41 commit 21f45f8

File tree

22 files changed

+464
-18
lines changed

22 files changed

+464
-18
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ endif
1818
CONFIG_FILE?=config-files/config.yaml
1919
AGG_CONFIG_FILE?=config-files/config-aggregator.yaml
2020

21-
OPERATOR_VERSION=v0.19.1
21+
OPERATOR_VERSION=v0.20.0
2222
EIGEN_SDK_GO_VERSION_DEVNET=v0.2.0-beta.1
2323
EIGEN_SDK_GO_VERSION_TESTNET=v0.2.0-beta.1
2424
EIGEN_SDK_GO_VERSION_MAINNET=v0.2.0-beta.1

crates/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/batcher/go_verifiers_lib/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ replace github.com/iden3/go-rapidsnark/types => github.com/yetanotherco/go-rapid
88

99
replace github.com/iden3/go-rapidsnark/verifier => github.com/yetanotherco/go-rapidsnark/verifier v0.0.0-20250829135025-99f9f6b11ecc
1010

11+
replace github.com/consensys/gnark-crypto => github.com/yetanotherco/gnark-crypto v0.0.0-20251017181549-3ba17da18479
12+
1113
require (
1214
github.com/consensys/gnark v0.12.0
1315
github.com/consensys/gnark-crypto v0.17.0

crates/batcher/go_verifiers_lib/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ github.com/consensys/bavard v0.1.29 h1:fobxIYksIQ+ZSrTJUuQgu+HIJwclrAPcdXqd7H2hh
66
github.com/consensys/bavard v0.1.29/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
77
github.com/consensys/gnark v0.12.0 h1:XgQ1kh2R6fHuf5fBYl+i7TxR+QTbGQuZaaqqkk5nLO0=
88
github.com/consensys/gnark v0.12.0/go.mod h1:WDvuIQ8qrRvWT9NhTrib84WeLVBSGhSTrbQBXs1yR5w=
9-
github.com/consensys/gnark-crypto v0.17.0 h1:vKDhZMOrySbpZDCvGMOELrHFv/A9mJ7+9I8HEfRZSkI=
10-
github.com/consensys/gnark-crypto v0.17.0/go.mod h1:A2URlMHUT81ifJ0UlLzSlm7TmnE3t7VxEThApdMukJw=
119
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
1210
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1311
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -46,6 +44,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
4644
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
4745
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
4846
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
47+
github.com/yetanotherco/gnark-crypto v0.0.0-20251017181549-3ba17da18479 h1:ub1gPwChjJVt7Sw8zkxlVp2ES2cxLylrp+pjRnePkYk=
48+
github.com/yetanotherco/gnark-crypto v0.0.0-20251017181549-3ba17da18479/go.mod h1:A2URlMHUT81ifJ0UlLzSlm7TmnE3t7VxEThApdMukJw=
4949
github.com/yetanotherco/go-rapidsnark/types v0.0.0-20250829135025-99f9f6b11ecc h1:9d0e1bmYz6pv0p1V8zrx7jALDdgAIZ8bZWSCDxemgE4=
5050
github.com/yetanotherco/go-rapidsnark/types v0.0.0-20250829135025-99f9f6b11ecc/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4=
5151
github.com/yetanotherco/go-rapidsnark/verifier v0.0.0-20250829135025-99f9f6b11ecc h1:JLSkcXpSuKb4fzYMF1qNM0nO2oysvOkpNeCSQunTtUA=

crates/batcher/src/lib.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ use aligned_sdk::common::constants::{
3333
RESPOND_TO_TASK_FEE_LIMIT_PERCENTAGE_MULTIPLIER,
3434
};
3535
use aligned_sdk::common::types::{
36-
ClientMessage, GetNonceResponseMessage, NoncedVerificationData, ProofInvalidReason,
37-
ProvingSystemId, SubmitProofMessage, SubmitProofResponseMessage, VerificationCommitmentBatch,
38-
VerificationData, VerificationDataCommitment,
36+
ClientMessage, GetLastMaxFeeResponseMessage, GetNonceResponseMessage, NoncedVerificationData,
37+
ProofInvalidReason, ProvingSystemId, SubmitProofMessage, SubmitProofResponseMessage,
38+
VerificationCommitmentBatch, VerificationData, VerificationDataCommitment,
3939
};
4040

4141
use aws_sdk_s3::client::Client as S3Client;
@@ -916,6 +916,11 @@ impl Batcher {
916916
.handle_submit_proof_msg(msg, ws_conn_sink)
917917
.await
918918
}
919+
ClientMessage::GetLastMaxFee(address) => {
920+
self.clone()
921+
.handle_get_last_max_fee(address, ws_conn_sink)
922+
.await
923+
}
919924
}
920925
}
921926

@@ -1004,6 +1009,84 @@ impl Batcher {
10041009
Ok(())
10051010
}
10061011

1012+
async fn handle_get_last_max_fee(
1013+
self: Arc<Self>,
1014+
mut address: Address,
1015+
ws_conn_sink: WsMessageSink,
1016+
) -> Result<(), Error> {
1017+
// If the address is not paying, we will return the last max fee of the aligned_payment_address
1018+
if !self.has_to_pay(&address) {
1019+
info!("Handling nonpaying message");
1020+
let Some(non_paying_config) = self.non_paying_config.as_ref() else {
1021+
warn!(
1022+
"There isn't a non-paying configuration loaded. This message will be ignored"
1023+
);
1024+
send_message(
1025+
ws_conn_sink.clone(),
1026+
GetLastMaxFeeResponseMessage::InvalidRequest(
1027+
"There isn't a non-paying configuration loaded.".to_string(),
1028+
),
1029+
)
1030+
.await;
1031+
return Ok(());
1032+
};
1033+
let replacement_addr = non_paying_config.replacement.address();
1034+
address = replacement_addr;
1035+
}
1036+
1037+
let user_states_guard = match timeout(MESSAGE_HANDLER_LOCK_TIMEOUT, self.user_states.read())
1038+
.await
1039+
{
1040+
Ok(guard) => guard,
1041+
Err(_) => {
1042+
warn!("User states read lock acquisition timed out in handle_get_last_max_fee_for_address_msg");
1043+
self.metrics.inc_message_handler_user_states_lock_timeouts();
1044+
send_message(ws_conn_sink, GetLastMaxFeeResponseMessage::ServerBusy).await;
1045+
return Ok(());
1046+
}
1047+
};
1048+
1049+
let Some(usr_ref) = user_states_guard.get(&address).cloned() else {
1050+
drop(user_states_guard);
1051+
send_message(
1052+
ws_conn_sink.clone(),
1053+
GetLastMaxFeeResponseMessage::LastMaxFee(U256::MAX),
1054+
)
1055+
.await;
1056+
return Ok(());
1057+
};
1058+
1059+
let Some(usr_lock) = self
1060+
.try_user_lock_with_timeout(address, usr_ref.lock())
1061+
.await
1062+
else {
1063+
drop(user_states_guard);
1064+
send_message(
1065+
ws_conn_sink.clone(),
1066+
GetLastMaxFeeResponseMessage::ServerBusy,
1067+
)
1068+
.await;
1069+
return Ok(());
1070+
};
1071+
1072+
let proofs_in_queue = usr_lock.proofs_in_batch;
1073+
let max_fee = if proofs_in_queue > 0 {
1074+
usr_lock.last_max_fee_limit
1075+
} else {
1076+
U256::MAX
1077+
};
1078+
drop(usr_lock);
1079+
drop(user_states_guard);
1080+
1081+
send_message(
1082+
ws_conn_sink.clone(),
1083+
GetLastMaxFeeResponseMessage::LastMaxFee(max_fee),
1084+
)
1085+
.await;
1086+
1087+
Ok(())
1088+
}
1089+
10071090
/// Returns the Aligned-funded address that will be used to pay for proofs when users don't need to pay themselves.
10081091
/// This function assumes that the non-paying configuration is set.
10091092
fn aligned_payment_address(&self) -> Address {

crates/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aligned"
3-
version = "0.19.1"
3+
version = "0.20.0"
44
edition = "2021"
55

66
[dependencies]

crates/cli/src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use aligned_sdk::communication::serialization::cbor_deserialize;
2020
use aligned_sdk::verification_layer;
2121
use aligned_sdk::verification_layer::estimate_fee;
2222
use aligned_sdk::verification_layer::get_chain_id;
23+
use aligned_sdk::verification_layer::get_last_max_fee;
2324
use aligned_sdk::verification_layer::get_nonce_from_batcher;
2425
use aligned_sdk::verification_layer::get_nonce_from_ethereum;
2526
use aligned_sdk::verification_layer::get_unlock_block_time;
@@ -46,6 +47,7 @@ use transaction::eip2718::TypedTransaction;
4647
use crate::AlignedCommands::DepositToBatcher;
4748
use crate::AlignedCommands::GetUserAmountOfQueuedProofs;
4849
use crate::AlignedCommands::GetUserBalance;
50+
use crate::AlignedCommands::GetUserLastMaxFee;
4951
use crate::AlignedCommands::GetUserNonce;
5052
use crate::AlignedCommands::GetUserNonceFromEthereum;
5153
use crate::AlignedCommands::GetVkCommitment;
@@ -94,6 +96,11 @@ pub enum AlignedCommands {
9496
name = "get-user-nonce-from-ethereum"
9597
)]
9698
GetUserNonceFromEthereum(GetUserNonceFromEthereumArgs),
99+
#[clap(
100+
about = "Gets user current last max fee from the batcher. This is the max limit you can send in your next proof.",
101+
name = "get-user-last-max-fee"
102+
)]
103+
GetUserLastMaxFee(GetUserLastMaxFeeArgs),
97104
#[clap(
98105
about = "Gets the number of proofs a user has queued in the Batcher.",
99106
name = "get-user-amount-of-queued-proofs"
@@ -316,6 +323,15 @@ pub struct GetUserNonceArgs {
316323
address: String,
317324
}
318325

326+
#[derive(Parser, Debug)]
327+
#[command(version, about, long_about = None)]
328+
pub struct GetUserLastMaxFeeArgs {
329+
#[clap(flatten)]
330+
network: NetworkArg,
331+
#[arg(name = "The user's Ethereum address", required = true)]
332+
address: String,
333+
}
334+
319335
#[derive(Parser, Debug)]
320336
#[command(version, about, long_about = None)]
321337
pub struct GetUserNonceFromEthereumArgs {
@@ -1041,6 +1057,22 @@ async fn main() -> Result<(), AlignedError> {
10411057
}
10421058
}
10431059
}
1060+
GetUserLastMaxFee(args) => {
1061+
let address = H160::from_str(&args.address).unwrap();
1062+
match get_last_max_fee(args.network.into(), address).await {
1063+
Ok(last_max_fee) => {
1064+
let last_max_fee_ether = ethers::utils::format_ether(last_max_fee);
1065+
info!(
1066+
"Last max fee for address {} is {}eth",
1067+
address, last_max_fee_ether
1068+
);
1069+
}
1070+
Err(e) => {
1071+
error!("Error while getting last max fee: {:?}", e);
1072+
return Ok(());
1073+
}
1074+
}
1075+
}
10441076
GetUserAmountOfQueuedProofs(args) => {
10451077
let address = H160::from_str(&args.address).unwrap();
10461078
let network: Network = args.network.into();

crates/sdk/src/common/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,16 @@ pub enum GetNonceError {
266266
GenericError(String),
267267
}
268268

269+
#[derive(Debug, Serialize, Deserialize, Clone)]
270+
pub enum GetLastMaxFeeError {
271+
ConnectionFailed(String),
272+
SerializationError(String),
273+
UnexpectedResponse(String),
274+
InvalidRequest(String),
275+
ProtocolMismatch { current: u16, expected: u16 },
276+
GenericError(String),
277+
}
278+
269279
#[derive(Debug)]
270280
pub enum ChainIdError {
271281
EthereumProviderError(String),

crates/sdk/src/common/types.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ pub enum ClientMessage {
295295
// Needs to be wrapped in box as the message is 3x bigger than the others
296296
// see https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
297297
SubmitProof(Box<SubmitProofMessage>),
298+
GetLastMaxFee(Address),
298299
}
299300

300301
impl Display for ClientMessage {
@@ -303,6 +304,7 @@ impl Display for ClientMessage {
303304
match self {
304305
ClientMessage::GetNonceForAddress(_) => write!(f, "GetNonceForAddress"),
305306
ClientMessage::SubmitProof(_) => write!(f, "SubmitProof"),
307+
ClientMessage::GetLastMaxFee(_) => write!(f, "GetLastMaxFee"),
306308
}
307309
}
308310
}
@@ -465,6 +467,13 @@ pub enum GetNonceResponseMessage {
465467
ServerBusy,
466468
}
467469

470+
#[derive(Debug, Clone, Serialize, Deserialize)]
471+
pub enum GetLastMaxFeeResponseMessage {
472+
LastMaxFee(U256),
473+
InvalidRequest(String),
474+
ServerBusy,
475+
}
476+
468477
#[derive(Debug, Clone)]
469478
pub enum Network {
470479
Devnet,

crates/sdk/src/verification_layer/mod.rs

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ use crate::{
55
DEFAULT_MAX_FEE_BATCH_SIZE, GAS_PRICE_PERCENTAGE_MULTIPLIER,
66
INSTANT_MAX_FEE_BATCH_SIZE, PERCENTAGE_DIVIDER,
77
},
8-
errors::{self, GetNonceError, PaymentError},
8+
errors::{self, GetLastMaxFeeError, GetNonceError, PaymentError},
99
types::{
10-
AlignedVerificationData, ClientMessage, FeeEstimationType, GetNonceResponseMessage,
11-
Network, ProvingSystemId, VerificationData,
10+
AlignedVerificationData, ClientMessage, FeeEstimationType,
11+
GetLastMaxFeeResponseMessage, GetNonceResponseMessage, Network, ProvingSystemId,
12+
VerificationData,
1213
},
1314
},
1415
communication::{
@@ -650,6 +651,87 @@ pub async fn get_nonce_from_ethereum(
650651
}
651652
}
652653

654+
/// Retrieves the `max_fee` of the proof with the highest nonce in the batcher queue for a given address.
655+
///
656+
/// This value represents the maximum fee limit that can be used when submitting the next proof.
657+
/// To increase the fee limit for a new proof, you must first bump the fee of the previous proofs,
658+
/// and continue doing so recursively until you reach the proof with the highest nonce (this one).
659+
///
660+
/// Read more here: https://docs.alignedlayer.com/architecture/1_proof_verification_layer/1_batcher#max-fee-priority-queue
661+
///
662+
/// # Arguments
663+
/// * `network` - The network from which to retrieve the last `max_fee`.
664+
/// * `address` - The user address whose last `max_fee` will be retrieved.
665+
///
666+
/// # Returns
667+
/// * `Ok(U256)` - The `max_fee` of the proof with the highest nonce for the given user.
668+
/// * `Ok(U256::MAX)` - If the user has no proofs in the queue.
669+
pub async fn get_last_max_fee(
670+
network: Network,
671+
address: Address,
672+
) -> Result<U256, GetLastMaxFeeError> {
673+
let (ws_stream, _) = connect_async(network.get_batcher_url())
674+
.await
675+
.map_err(|_| {
676+
GetLastMaxFeeError::ConnectionFailed("Ws connection to batcher failed".to_string())
677+
})?;
678+
679+
debug!("WebSocket handshake has been successfully completed");
680+
let (mut ws_write, mut ws_read) = ws_stream.split();
681+
check_protocol_version(&mut ws_read)
682+
.map_err(|e| match e {
683+
errors::SubmitError::ProtocolVersionMismatch { current, expected } => {
684+
GetLastMaxFeeError::ProtocolMismatch { current, expected }
685+
}
686+
_ => GetLastMaxFeeError::UnexpectedResponse(
687+
"Unexpected response, expected protocol version".to_string(),
688+
),
689+
})
690+
.await?;
691+
692+
let msg = ClientMessage::GetLastMaxFee(address);
693+
694+
let msg_bin = cbor_serialize(&msg).map_err(|_| {
695+
GetLastMaxFeeError::SerializationError("Failed to serialize msg".to_string())
696+
})?;
697+
ws_write
698+
.send(Message::Binary(msg_bin.clone()))
699+
.await
700+
.map_err(|_| {
701+
GetLastMaxFeeError::ConnectionFailed(
702+
"Ws connection failed to send message to batcher".to_string(),
703+
)
704+
})?;
705+
706+
let mut response_stream: ResponseStream =
707+
ws_read.try_filter(|msg| futures_util::future::ready(msg.is_binary()));
708+
709+
let msg = match response_stream.next().await {
710+
Some(Ok(msg)) => msg,
711+
_ => {
712+
return Err(GetLastMaxFeeError::ConnectionFailed(
713+
"Connection was closed without close message before receiving all messages"
714+
.to_string(),
715+
));
716+
}
717+
};
718+
719+
let _ = ws_write.close().await;
720+
721+
match cbor_deserialize(msg.into_data().as_slice()) {
722+
Ok(GetLastMaxFeeResponseMessage::LastMaxFee(last_max_fee)) => Ok(last_max_fee),
723+
Ok(GetLastMaxFeeResponseMessage::InvalidRequest(e)) => {
724+
Err(GetLastMaxFeeError::InvalidRequest(e))
725+
}
726+
Ok(GetLastMaxFeeResponseMessage::ServerBusy) => Err(GetLastMaxFeeError::GenericError(
727+
"Server is busy processing requests, please retry".to_string(),
728+
)),
729+
Err(_) => Err(GetLastMaxFeeError::SerializationError(
730+
"Failed to deserialize batcher message".to_string(),
731+
)),
732+
}
733+
}
734+
653735
/// Returns the chain ID of the Ethereum network.
654736
///
655737
/// # Arguments

0 commit comments

Comments
 (0)