Skip to content

Commit

Permalink
feat(api): Add tokens_whitelisted_for_paymaster (#1545)
Browse files Browse the repository at this point in the history
## What ❔

- Adds `tokens_whitelisted_for_paymaster` config var to main node.
- Adds `en_tokensWhitelistedForPaymaster` endpoint.

## Why ❔

Required to whitelist some tokens to be used by paymaster in addition to
natively bridged tokens.

## 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 `zk fmt` and `zk lint`.
- [ ] Spellcheck has been run via `zk spellcheck`.
- [ ] Linkcheck has been run via `zk linkcheck`.
  • Loading branch information
perekopskiy committed Apr 4, 2024
1 parent a923e11 commit 6da89cd
Show file tree
Hide file tree
Showing 15 changed files with 146 additions and 23 deletions.
1 change: 1 addition & 0 deletions checks-config/era.dic
Expand Up @@ -190,6 +190,7 @@ sorted_timestamps
known_bytecodes
returndata
namespaces
natively
StateDiffRecord
BYTES_PER_ENUMERATION_INDEX
derived_key
Expand Down
2 changes: 2 additions & 0 deletions core/bin/external_node/src/config/mod.rs
Expand Up @@ -737,6 +737,8 @@ impl From<ExternalNodeConfig> for TxSenderConfig {
.optional
.l1_to_l2_transactions_compatibility_mode,
max_pubdata_per_batch: config.remote.max_pubdata_per_batch,
// Does not matter for EN.
whitelisted_tokens_for_aa: Default::default(),
}
}
}
Expand Down
40 changes: 36 additions & 4 deletions core/bin/external_node/src/main.rs
Expand Up @@ -5,7 +5,7 @@ use clap::Parser;
use metrics::EN_METRICS;
use prometheus_exporter::PrometheusExporterConfig;
use tokio::{
sync::watch,
sync::{watch, RwLock},
task::{self, JoinHandle},
};
use zksync_concurrency::{ctx, scope};
Expand Down Expand Up @@ -51,7 +51,7 @@ use zksync_state::PostgresStorageCaches;
use zksync_storage::RocksDB;
use zksync_types::L2ChainId;
use zksync_utils::wait_for_tasks::ManagedTasks;
use zksync_web3_decl::client::L2Client;
use zksync_web3_decl::{client::L2Client, namespaces::EnNamespaceClient};

use crate::{
config::{observability::observability_config_from_env, ExternalNodeConfig},
Expand Down Expand Up @@ -387,8 +387,14 @@ async fn run_api(
}
};

let (tx_sender, vm_barrier, cache_update_handle, proxy_cache_updater_handle) = {
let tx_proxy = TxProxy::new(main_node_client);
let (
tx_sender,
vm_barrier,
cache_update_handle,
proxy_cache_updater_handle,
whitelisted_tokens_update_handle,
) = {
let tx_proxy = TxProxy::new(main_node_client.clone());
let proxy_cache_updater_pool = singleton_pool_builder
.build()
.await
Expand Down Expand Up @@ -426,7 +432,31 @@ async fn run_api(
)
});

let whitelisted_tokens_for_aa_cache = Arc::new(RwLock::new(Vec::new()));
let whitelisted_tokens_for_aa_cache_clone = whitelisted_tokens_for_aa_cache.clone();
let mut stop_receiver_for_task = stop_receiver.clone();
let whitelisted_tokens_update_task = task::spawn(async move {
loop {
match main_node_client.whitelisted_tokens_for_aa().await {
Ok(tokens) => {
*whitelisted_tokens_for_aa_cache_clone.write().await = tokens;
}
Err(err) => {
tracing::error!(
"Failed to query `whitelisted_tokens_for_aa`, error: {err:?}"
);
}
}

// Error here corresponds to a timeout w/o `stop_receiver` changed; we're OK with this.
tokio::time::timeout(Duration::from_secs(60), stop_receiver_for_task.changed())
.await
.ok();
}
});

let tx_sender = tx_sender_builder
.with_whitelisted_tokens_for_aa(whitelisted_tokens_for_aa_cache)
.build(
fee_params_fetcher,
Arc::new(vm_concurrency_limiter),
Expand All @@ -439,6 +469,7 @@ async fn run_api(
vm_barrier,
cache_update_handle,
proxy_cache_updater_handle,
whitelisted_tokens_update_task,
)
};

Expand Down Expand Up @@ -490,6 +521,7 @@ async fn run_api(

task_futures.extend(cache_update_handle);
task_futures.push(proxy_cache_updater_handle);
task_futures.push(whitelisted_tokens_update_handle);

Ok(())
}
Expand Down
7 changes: 6 additions & 1 deletion core/lib/config/src/configs/api.rs
@@ -1,7 +1,7 @@
use std::{net::SocketAddr, num::NonZeroU32, time::Duration};

use serde::Deserialize;
use zksync_basic_types::H256;
use zksync_basic_types::{Address, H256};

pub use crate::configs::PrometheusConfig;

Expand Down Expand Up @@ -100,6 +100,10 @@ pub struct Web3JsonRpcConfig {
pub mempool_cache_update_interval: Option<u64>,
/// Maximum number of transactions to be stored in the mempool cache. Default is 10000.
pub mempool_cache_size: Option<usize>,
/// List of L2 token addresses that are white-listed to use by paymasters
/// (additionally to natively bridged tokens).
#[serde(default)]
pub whitelisted_tokens_for_aa: Vec<Address>,
}

impl Web3JsonRpcConfig {
Expand Down Expand Up @@ -137,6 +141,7 @@ impl Web3JsonRpcConfig {
mempool_cache_update_interval: Default::default(),
mempool_cache_size: Default::default(),
tree_api_url: None,
whitelisted_tokens_for_aa: Default::default(),
}
}

Expand Down
1 change: 1 addition & 0 deletions core/lib/config/src/testonly.rs
Expand Up @@ -79,6 +79,7 @@ impl Distribution<configs::api::Web3JsonRpcConfig> for EncodeDist {
tree_api_url: self.sample(rng),
mempool_cache_update_interval: self.sample(rng),
mempool_cache_size: self.sample(rng),
whitelisted_tokens_for_aa: self.sample_range(rng).map(|_| rng.gen()).collect(),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion core/lib/env_config/src/api.rs
Expand Up @@ -49,7 +49,7 @@ mod tests {
use std::num::NonZeroU32;

use super::*;
use crate::test_utils::{hash, EnvMutex};
use crate::test_utils::{addr, hash, EnvMutex};

static MUTEX: EnvMutex = EnvMutex::new();

Expand Down Expand Up @@ -88,6 +88,10 @@ mod tests {
tree_api_url: None,
mempool_cache_update_interval: Some(50),
mempool_cache_size: Some(10000),
whitelisted_tokens_for_aa: vec![
addr("0x0000000000000000000000000000000000000001"),
addr("0x0000000000000000000000000000000000000002"),
],
},
prometheus: PrometheusConfig {
listener_port: 3312,
Expand Down Expand Up @@ -120,6 +124,7 @@ mod tests {
API_WEB3_JSON_RPC_GAS_PRICE_SCALE_FACTOR=1.2
API_WEB3_JSON_RPC_REQUEST_TIMEOUT=10
API_WEB3_JSON_RPC_ACCOUNT_PKS="0x0000000000000000000000000000000000000000000000000000000000000001,0x0000000000000000000000000000000000000000000000000000000000000002"
API_WEB3_JSON_RPC_WHITELISTED_TOKENS_FOR_AA="0x0000000000000000000000000000000000000001,0x0000000000000000000000000000000000000002"
API_WEB3_JSON_RPC_ESTIMATE_GAS_SCALE_FACTOR=1.0
API_WEB3_JSON_RPC_ESTIMATE_GAS_ACCEPTABLE_OVERESTIMATION=1000
API_WEB3_JSON_RPC_L1_TO_L2_TRANSACTIONS_COMPATIBILITY_MODE=true
Expand Down
14 changes: 13 additions & 1 deletion core/lib/protobuf_config/src/api.rs
Expand Up @@ -5,7 +5,7 @@ use zksync_protobuf::{
required,
};

use crate::{parse_h256, proto::api as proto};
use crate::{parse_h160, parse_h256, proto::api as proto};

impl ProtoRepr for proto::Api {
type Type = ApiConfig;
Expand Down Expand Up @@ -124,6 +124,13 @@ impl ProtoRepr for proto::Web3JsonRpc {
.map(|x| x.try_into())
.transpose()
.context("mempool_cache_size")?,
whitelisted_tokens_for_aa: self
.whitelisted_tokens_for_aa
.iter()
.enumerate()
.map(|(i, k)| parse_h160(k).context(i))
.collect::<Result<Vec<_>, _>>()
.context("account_pks")?,
})
}
fn build(this: &Self::Type) -> Self {
Expand Down Expand Up @@ -177,6 +184,11 @@ impl ProtoRepr for proto::Web3JsonRpc {
.websocket_requests_per_minute_limit
.map(|x| x.into()),
tree_api_url: this.tree_api_url.clone(),
whitelisted_tokens_for_aa: this
.whitelisted_tokens_for_aa
.iter()
.map(|k| format!("{:?}", k))
.collect(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core/lib/protobuf_config/src/proto/api.proto
Expand Up @@ -34,6 +34,7 @@ message Web3JsonRpc {
optional bool filters_disabled = 27; // optional
optional uint64 mempool_cache_update_interval = 28; // optional
optional uint64 mempool_cache_size = 29; // optional
repeated string whitelisted_tokens_for_aa = 30; // optional
}


Expand Down
6 changes: 5 additions & 1 deletion core/lib/web3_decl/src/namespaces/en.rs
@@ -1,6 +1,6 @@
use jsonrpsee::{core::RpcResult, proc_macros::rpc};
use zksync_config::GenesisConfig;
use zksync_types::{api::en, tokens::TokenInfo, MiniblockNumber};
use zksync_types::{api::en, tokens::TokenInfo, Address, MiniblockNumber};

#[cfg_attr(
all(feature = "client", feature = "server"),
Expand Down Expand Up @@ -35,4 +35,8 @@ pub trait EnNamespace {
/// Get genesis configuration
#[method(name = "genesisConfig")]
async fn genesis_config(&self) -> RpcResult<GenesisConfig>;

/// Get tokens that are white-listed and it can be used by paymasters.
#[method(name = "whitelistedTokensForAA")]
async fn whitelisted_tokens_for_aa(&self) -> RpcResult<Vec<Address>>;
}
5 changes: 4 additions & 1 deletion core/lib/zksync_core/src/api_server/execution_sandbox/mod.rs
Expand Up @@ -6,7 +6,8 @@ use zksync_dal::{Connection, ConnectionPool, Core, CoreDal};
use zksync_state::{PostgresStorage, PostgresStorageCaches, ReadStorage, StorageView};
use zksync_system_constants::PUBLISH_BYTECODE_OVERHEAD;
use zksync_types::{
api, fee_model::BatchFeeInput, AccountTreeId, L1BatchNumber, L2ChainId, MiniblockNumber,
api, fee_model::BatchFeeInput, AccountTreeId, Address, L1BatchNumber, L2ChainId,
MiniblockNumber,
};
use zksync_utils::bytecode::{compress_bytecode, hash_bytecode};

Expand Down Expand Up @@ -217,6 +218,7 @@ pub(crate) struct TxSharedArgs {
pub caches: PostgresStorageCaches,
pub validation_computational_gas_limit: u32,
pub chain_id: L2ChainId,
pub whitelisted_tokens_for_aa: Vec<Address>,
}

impl TxSharedArgs {
Expand All @@ -229,6 +231,7 @@ impl TxSharedArgs {
caches: PostgresStorageCaches::new(1, 1),
validation_computational_gas_limit: u32::MAX,
chain_id: L2ChainId::default(),
whitelisted_tokens_for_aa: Vec::new(),
}
}
}
Expand Down
29 changes: 21 additions & 8 deletions core/lib/zksync_core/src/api_server/execution_sandbox/validate.rs
Expand Up @@ -11,7 +11,7 @@ use multivm::{
MultiVMTracer,
};
use zksync_dal::{Connection, ConnectionPool, Core, CoreDal};
use zksync_types::{l2::L2Tx, Transaction, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS};
use zksync_types::{l2::L2Tx, Address, Transaction, TRUSTED_ADDRESS_SLOTS, TRUSTED_TOKEN_SLOTS};

use super::{
apply,
Expand Down Expand Up @@ -50,10 +50,14 @@ impl TransactionExecutor {
.connection_tagged("api")
.await
.context("failed acquiring DB connection")?;
let validation_params =
get_validation_params(&mut connection, &tx, computational_gas_limit)
.await
.context("failed getting validation params")?;
let validation_params = get_validation_params(
&mut connection,
&tx,
computational_gas_limit,
&shared_args.whitelisted_tokens_for_aa,
)
.await
.context("failed getting validation params")?;
drop(connection);

let execution_args = TxExecutionArgs::for_validation(&tx);
Expand Down Expand Up @@ -120,20 +124,25 @@ async fn get_validation_params(
connection: &mut Connection<'_, Core>,
tx: &L2Tx,
computational_gas_limit: u32,
whitelisted_tokens_for_aa: &[Address],
) -> anyhow::Result<ValidationTracerParams> {
let method_latency = EXECUTION_METRICS.get_validation_params.start();
let user_address = tx.common_data.initiator_address;
let paymaster_address = tx.common_data.paymaster_params.paymaster;

// This method assumes that the number of tokens is relatively low. When it grows
// we may need to introduce some kind of caching.
let all_tokens = connection.tokens_dal().get_all_l2_token_addresses().await?;
let all_bridged_tokens = connection.tokens_dal().get_all_l2_token_addresses().await?;
let all_tokens: Vec<_> = all_bridged_tokens
.iter()
.chain(whitelisted_tokens_for_aa)
.collect();
EXECUTION_METRICS.tokens_amount.set(all_tokens.len());

let span = tracing::debug_span!("compute_trusted_slots_for_validation").entered();
let trusted_slots: HashSet<_> = all_tokens
.iter()
.flat_map(|&token| TRUSTED_TOKEN_SLOTS.iter().map(move |&slot| (token, slot)))
.flat_map(|&token| TRUSTED_TOKEN_SLOTS.iter().map(move |&slot| (*token, slot)))
.collect();

// We currently don't support any specific trusted addresses.
Expand All @@ -143,7 +152,11 @@ async fn get_validation_params(
// Required for working with transparent proxies.
let trusted_address_slots: HashSet<_> = all_tokens
.into_iter()
.flat_map(|token| TRUSTED_ADDRESS_SLOTS.iter().map(move |&slot| (token, slot)))
.flat_map(|token| {
TRUSTED_ADDRESS_SLOTS
.iter()
.map(move |&slot| (*token, slot))
})
.collect();
EXECUTION_METRICS
.trusted_address_slots_amount
Expand Down

0 comments on commit 6da89cd

Please sign in to comment.