Skip to content

Commit

Permalink
fix(finalizer): introduce an optional threshold to eth withdrawals (#348
Browse files Browse the repository at this point in the history
)

# What ❔

<!-- What are the changes this PR brings about? -->
<!-- Example: This PR adds a PR template to the repo. -->
<!-- (For bigger PRs adding more context is appreciated) -->

## Why ❔

<!-- Why are these changes done? What goal do they contribute to? What
are the principles behind them? -->
<!-- Example: PR templates ensure PR reviewers, observers, and future
iterators are in context about the evolution of repos. -->

## Checklist

<!-- Check your PR fulfills the following items. -->
<!-- For draft PRs check the boxes as you complete them. -->

- [ ] PR title corresponds to the body of PR (we generate changelog
entries from PRs).
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] Code has been formatted via `cargo fmt`.
  • Loading branch information
montekki authored Jan 16, 2024
1 parent 08e6971 commit a4e91dd
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Deployment is done by deploying a dockerized image of the service.
| `CUSTOM_TOKEN_DEPLOYER_ADDRESSES` | (Optional) Normally ERC20 tokens are deployed by the bridge contract. However, in custom cases it may be necessary to override that behavior with a custom set of addresses that have deployed tokens |
| `CUSTOM_TOKEN_ADDRESSES` | (Optional) Adds a predefined list of tokens to finalize. May be useful in case of custom bridge setups when the regular technique of finding token deployments does not work. |
| `ENABLE_WITHDRAWAL_METERING` | (Optional, default: `"true"`) By default Finalizer collects metrics about withdrawn token volumens. Users may optionally switch off this metering. |
| `ETH_FINALIZATION_THRESHOLD`| (Optional, default: "0") Finalizer will only finalize ETH withdrawals that are greater or equal to this value |

The configuration structure describing the service config can be found in [`config.rs`](https://github.com/matter-labs/zksync-withdrawal-finalizer/blob/main/bin/withdrawal-finalizer/src/config.rs)

Expand Down
3 changes: 3 additions & 0 deletions bin/withdrawal-finalizer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ pub struct Config {

#[envconfig(from = "CUSTOM_TOKEN_ADDRESS_MAPPINGS")]
pub custom_token_address_mappings: Option<CustomTokenAddressMappings>,

#[envconfig(from = "ETH_FINALIZATION_THRESHOLD")]
pub eth_finalization_threshold: Option<String>,
}

#[derive(Deserialize, Serialize, Debug, Eq, PartialEq)]
Expand Down
7 changes: 7 additions & 0 deletions bin/withdrawal-finalizer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,12 @@ async fn main() -> Result<()> {
config.batch_finalization_gas_limit,
);

let eth_finalization_threshold = match config.eth_finalization_threshold {
Some(eth_finalization_threshold) => {
Some(ethers::utils::parse_ether(eth_finalization_threshold)?)
}
None => None,
};
let finalizer = finalizer::Finalizer::new(
pgpool,
one_withdrawal_gas_limit,
Expand All @@ -294,6 +300,7 @@ async fn main() -> Result<()> {
finalizer_account_address,
config.tokens_to_finalize.unwrap_or_default(),
meter_withdrawals,
eth_finalization_threshold,
);
let finalizer_handle = tokio::spawn(finalizer.run(client_l2));

Expand Down
13 changes: 11 additions & 2 deletions finalizer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub struct Finalizer<M1, M2> {
account_address: Address,
withdrawals_meterer: Option<WithdrawalsMeter>,
token_list: TokenList,
eth_threshold: Option<U256>,
}

const NO_NEW_WITHDRAWALS_BACKOFF: Duration = Duration::from_secs(5);
Expand Down Expand Up @@ -132,6 +133,7 @@ where
account_address: Address,
token_list: TokenList,
meter_withdrawals: bool,
eth_threshold: Option<U256>,
) -> Self {
let withdrawals_meterer = meter_withdrawals.then_some(WithdrawalsMeter::new(
pgpool.clone(),
Expand All @@ -157,6 +159,7 @@ where
account_address,
withdrawals_meterer,
token_list,
eth_threshold,
}
}

Expand Down Expand Up @@ -351,14 +354,19 @@ where

let try_finalize_these = match &self.token_list {
TokenList::All => {
storage::withdrawals_to_finalize(&self.pgpool, self.query_db_pagination_limit)
.await?
storage::withdrawals_to_finalize(
&self.pgpool,
self.query_db_pagination_limit,
self.eth_threshold,
)
.await?
}
TokenList::WhiteList(w) => {
storage::withdrawals_to_finalize_with_whitelist(
&self.pgpool,
self.query_db_pagination_limit,
w,
self.eth_threshold,
)
.await?
}
Expand All @@ -367,6 +375,7 @@ where
&self.pgpool,
self.query_db_pagination_limit,
b,
self.eth_threshold,
)
.await?
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 28 additions & 1 deletion storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

//! Finalizer watcher.storage.operations.
use ethers::types::{Address, H160, H256};
use ethers::types::{Address, H160, H256, U256};
use sqlx::{PgConnection, PgPool};

use chain_events::L2TokenInitEvent;
Expand Down Expand Up @@ -746,8 +746,11 @@ pub async fn withdrawals_to_finalize_with_blacklist(
pool: &PgPool,
limit_by: u64,
token_blacklist: &[Address],
eth_threshold: Option<U256>,
) -> Result<Vec<WithdrawalParams>> {
let blacklist: Vec<_> = token_blacklist.iter().map(|a| a.0.to_vec()).collect();
// if no threshold, query _all_ ethereum withdrawals since all of them are >= 0.
let eth_threshold = eth_threshold.unwrap_or(U256::zero());

let data = sqlx::query!(
"
Expand Down Expand Up @@ -782,13 +785,19 @@ pub async fn withdrawals_to_finalize_with_blacklist(
AND w.token NOT IN (SELECT * FROM UNNEST (
$2 :: BYTEA []
))
AND (
CASE WHEN token = decode('000000000000000000000000000000000000800A', 'hex') THEN amount >= $3
ELSE TRUE
END
)
ORDER BY
finalization_data.l2_block_number
LIMIT
$1
",
limit_by as i64,
&blacklist,
u256_to_big_decimal(eth_threshold),
)
.fetch_all(pool)
.await?
Expand Down Expand Up @@ -816,8 +825,11 @@ pub async fn withdrawals_to_finalize_with_whitelist(
pool: &PgPool,
limit_by: u64,
token_whitelist: &[Address],
eth_threshold: Option<U256>,
) -> Result<Vec<WithdrawalParams>> {
let whitelist: Vec<_> = token_whitelist.iter().map(|a| a.0.to_vec()).collect();
// if no threshold, query _all_ ethereum withdrawals since all of them are >= 0.
let eth_threshold = eth_threshold.unwrap_or(U256::zero());

let data = sqlx::query!(
"
Expand Down Expand Up @@ -852,13 +864,19 @@ pub async fn withdrawals_to_finalize_with_whitelist(
AND w.token IN (SELECT * FROM UNNEST (
$2 :: BYTEA []
))
AND (
CASE WHEN token = decode('000000000000000000000000000000000000800A', 'hex') THEN amount >= $3
ELSE TRUE
END
)
ORDER BY
finalization_data.l2_block_number
LIMIT
$1
",
limit_by as i64,
&whitelist,
u256_to_big_decimal(eth_threshold),
)
.fetch_all(pool)
.await?
Expand All @@ -885,8 +903,11 @@ pub async fn withdrawals_to_finalize_with_whitelist(
pub async fn withdrawals_to_finalize(
pool: &PgPool,
limit_by: u64,
eth_threshold: Option<U256>,
) -> Result<Vec<WithdrawalParams>> {
let latency = STORAGE_METRICS.call[&"withdrawals_to_finalize"].start();
// if no threshold, query _all_ ethereum withdrawals since all of them are >= 0.
let eth_threshold = eth_threshold.unwrap_or(U256::zero());

let data = sqlx::query!(
"
Expand Down Expand Up @@ -923,12 +944,18 @@ pub async fn withdrawals_to_finalize(
OR
last_finalization_attempt < NOW() - INTERVAL '1 minutes'
)
AND (
CASE WHEN token = decode('000000000000000000000000000000000000800A', 'hex') THEN amount >= $2
ELSE TRUE
END
)
ORDER BY
finalization_data.l2_block_number
LIMIT
$1
",
limit_by as i64,
u256_to_big_decimal(eth_threshold),
)
.fetch_all(pool)
.await?
Expand Down

0 comments on commit a4e91dd

Please sign in to comment.