diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index db2c6eac9cf6..235802e1073b 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -128,6 +128,7 @@ pub(crate) struct RemoteENConfig { pub l2_weth_bridge_addr: Option
, pub l2_testnet_paymaster_addr: Option
, pub l2_timestamp_asserter_addr: Option
, + pub l1_wrapped_base_token_store: Option
, pub base_token_addr: Address, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, pub dummy_verifier: bool, @@ -195,6 +196,9 @@ impl RemoteENConfig { l1_bytecodes_supplier_addr: ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + l1_wrapped_base_token_store: ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), l1_diamond_proxy_addr, l2_testnet_paymaster_addr, l1_erc20_bridge_proxy_addr: bridges.l1_erc20_default_bridge, @@ -235,6 +239,7 @@ impl RemoteENConfig { l2_shared_bridge_addr: Some(Address::repeat_byte(6)), l2_legacy_shared_bridge_addr: Some(Address::repeat_byte(7)), l1_batch_commit_data_generator_mode: L1BatchCommitmentMode::Rollup, + l1_wrapped_base_token_store: None, dummy_verifier: true, l2_timestamp_asserter_addr: None, } @@ -1477,6 +1482,7 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { l2_weth_bridge: config.remote.l2_weth_bridge_addr, }, l1_bytecodes_supplier_addr: config.remote.l1_bytecodes_supplier_addr, + l1_wrapped_base_token_store: config.remote.l1_wrapped_base_token_store, l1_bridgehub_proxy_addr: config.remote.l1_bridgehub_proxy_addr, l1_state_transition_proxy_addr: config.remote.l1_state_transition_proxy_addr, l1_transparent_proxy_admin_addr: config.remote.l1_transparent_proxy_admin_addr, diff --git a/core/lib/basic_types/src/protocol_version.rs b/core/lib/basic_types/src/protocol_version.rs index 89251b3a2a40..eb8117b01e03 100644 --- a/core/lib/basic_types/src/protocol_version.rs +++ b/core/lib/basic_types/src/protocol_version.rs @@ -123,7 +123,7 @@ impl ProtocolVersionId { ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory, ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, - ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, + ProtocolVersionId::Version26 => VmVersion::VmGateway, ProtocolVersionId::Version27 => VmVersion::VmGateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not yet supported"), } @@ -192,7 +192,7 @@ impl ProtocolVersionId { } pub const fn gateway_upgrade() -> Self { - ProtocolVersionId::Version27 + ProtocolVersionId::Version26 } } @@ -298,7 +298,7 @@ impl From for VmVersion { ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory, ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, - ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, + ProtocolVersionId::Version26 => VmVersion::VmGateway, ProtocolVersionId::Version27 => VmVersion::VmGateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not yet supported"), } diff --git a/core/lib/basic_types/src/vm.rs b/core/lib/basic_types/src/vm.rs index f11f98596f18..4469785c7411 100644 --- a/core/lib/basic_types/src/vm.rs +++ b/core/lib/basic_types/src/vm.rs @@ -22,7 +22,7 @@ pub enum VmVersion { impl VmVersion { /// Returns the latest supported VM version. pub const fn latest() -> VmVersion { - Self::Vm1_5_0IncreasedBootloaderMemory + Self::VmGateway } } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index f6bd02f2dfae..561e51fa5dd5 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -9,6 +9,10 @@ pub struct EcosystemContracts { pub state_transition_proxy_addr: Address, pub transparent_proxy_admin_addr: Address, pub l1_bytecodes_supplier_addr: Option
, + // Note that on the contract side of things this contract is called `L2WrappedBaseTokenStore`, + // while on the server side for consistency with the conventions, where the prefix denotes + // the location of the contracts we call it `l1_wrapped_base_token_store` + pub l1_wrapped_base_token_store: Option
, } impl EcosystemContracts { @@ -18,6 +22,7 @@ impl EcosystemContracts { state_transition_proxy_addr: Address::repeat_byte(0x15), transparent_proxy_admin_addr: Address::repeat_byte(0x15), l1_bytecodes_supplier_addr: Some(Address::repeat_byte(0x16)), + l1_wrapped_base_token_store: Some(Address::repeat_byte(0x17)), } } } @@ -50,8 +55,6 @@ pub struct ContractsConfig { pub base_token_addr: Option
, pub l1_base_token_asset_id: Option, - pub l2_predeployed_wrapped_base_token_address: Option
, - pub chain_admin_addr: Option
, pub l2_da_validator_addr: Option
, } @@ -76,7 +79,6 @@ impl ContractsConfig { governance_addr: Address::repeat_byte(0x13), base_token_addr: Some(Address::repeat_byte(0x14)), l1_base_token_asset_id: Some(H256::repeat_byte(0x15)), - l2_predeployed_wrapped_base_token_address: Some(Address::repeat_byte(0x1b)), ecosystem_contracts: Some(EcosystemContracts::for_tests()), chain_admin_addr: Some(Address::repeat_byte(0x18)), l2_da_validator_addr: Some(Address::repeat_byte(0x1a)), diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 3472cf4e7d0a..431fa406d109 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -268,7 +268,6 @@ impl Distribution for EncodeDist { ecosystem_contracts: self.sample(rng), base_token_addr: self.sample_opt(|| rng.gen()), l1_base_token_asset_id: self.sample_opt(|| rng.gen()), - l2_predeployed_wrapped_base_token_address: self.sample_opt(|| rng.gen()), chain_admin_addr: self.sample_opt(|| rng.gen()), l2_da_validator_addr: self.sample_opt(|| rng.gen()), } @@ -763,6 +762,7 @@ impl Distribution for EncodeDist { state_transition_proxy_addr: rng.gen(), transparent_proxy_admin_addr: rng.gen(), l1_bytecodes_supplier_addr: rng.gen(), + l1_wrapped_base_token_store: rng.gen(), } } } diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 9ca679fef899..5e9de985f22f 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -1423,7 +1423,25 @@ pub static POST_SHARED_BRIDGE_EXECUTE_FUNCTION: Lazy = Lazy::new(|| { serde_json::from_str(abi).unwrap() }); -// Temporary thing, should be removed when new contracts are merged. +// Temporary items, should be removed when new contracts are merged. + +pub static L1_ASSET_ROUTER_CONTRACT: Lazy = Lazy::new(|| { + let abi = r#" + [ + {"type":"function","name":"l2BridgeAddress","inputs":[{"name":"_chainId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"}, + {"type":"function","name":"L1_NULLIFIER","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IL1Nullifier"}],"stateMutability":"view"} + ]"#; + serde_json::from_str(abi).unwrap() +}); + +pub static WRAPPED_BASE_TOKEN_STORE_CONTRACT: Lazy = Lazy::new(|| { + let abi = r#" + [ + {"type":"function","name":"l2WBaseTokenAddress","inputs":[{"name":"chainId","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"l2WBaseTokenAddress","type":"address","internalType":"address"}],"stateMutability":"view"} + ]"#; + serde_json::from_str(abi).unwrap() +}); + pub static MESSAGE_ROOT_CONTRACT: Lazy = Lazy::new(|| { let abi = r#" [{ diff --git a/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json b/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json new file mode 100644 index 000000000000..703a57ae0597 --- /dev/null +++ b/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n COUNT(*)\n FROM\n eth_txs\n WHERE\n confirmed_eth_tx_history_id IS NULL\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572" +} diff --git a/core/lib/dal/src/eth_sender_dal.rs b/core/lib/dal/src/eth_sender_dal.rs index 191ea3231d1c..eecd102f395e 100644 --- a/core/lib/dal/src/eth_sender_dal.rs +++ b/core/lib/dal/src/eth_sender_dal.rs @@ -86,6 +86,25 @@ impl EthSenderDal<'_, '_> { Ok(count.try_into().unwrap()) } + pub async fn get_unconfirmed_txs_count(&mut self) -> DalResult { + let count = sqlx::query!( + r#" + SELECT + COUNT(*) + FROM + eth_txs + WHERE + confirmed_eth_tx_history_id IS NULL + "# + ) + .instrument("get_unconfirmed_txs_count") + .fetch_one(self.storage) + .await? + .count + .unwrap(); + Ok(count.try_into().unwrap()) + } + pub async fn get_eth_l1_batches(&mut self) -> sqlx::Result { struct EthTxRow { number: i64, diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 457a946d9831..4cd0b021ff20 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -10,8 +10,14 @@ impl FromEnv for EcosystemContracts { .parse()?, transparent_proxy_admin_addr: std::env::var("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR")? .parse()?, - // Not supported yet - l1_bytecodes_supplier_addr: None, + l1_bytecodes_supplier_addr: std::env::var("CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR")? + .parse() + .ok(), + l1_wrapped_base_token_store: std::env::var( + "CONTRACTS_L1_WRAPPED_BASE_TOKEN_STORE_ADDR", + )? + .parse() + .ok(), }) } } @@ -44,6 +50,9 @@ impl FromEnv for ContractsConfig { #[cfg(test)] mod tests { + use std::str::FromStr; + + use zksync_basic_types::H256; use zksync_config::configs::EcosystemContracts; use zksync_system_constants::SHARED_BRIDGE_ETHER_TOKEN_ADDRESS; @@ -72,11 +81,20 @@ mod tests { bridgehub_proxy_addr: addr("0x35ea7f92f4c5f433efe15284e99c040110cf6297"), state_transition_proxy_addr: addr("0xd90f1c081c6117241624e97cb6147257c3cb2097"), transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"), - l1_bytecodes_supplier_addr: None, + l1_bytecodes_supplier_addr: Some(addr( + "0x36ea7f92f4c5f433efe15284e99c040110cf6297", + )), + l1_wrapped_base_token_store: Some(addr( + "0x36ea7f92f4c5f433efe15284e99c040110cf6298", + )), }), base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), - l1_base_token_asset_id: None, - l2_predeployed_wrapped_base_token_address: None, + l1_base_token_asset_id: Some( + H256::from_str( + "0x0000000000000000000000000000000000000001000000000000000000000000", + ) + .unwrap(), + ), chain_admin_addr: Some(addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_da_validator_addr: Some(addr("0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_timestamp_asserter_addr: Some(addr("0x0000000000000000000000000000000000000002")), @@ -101,11 +119,16 @@ CONTRACTS_L2_CONSENSUS_REGISTRY_ADDR="D64e136566a9E04eb05B30184fF577F52682D182" CONTRACTS_L1_MULTICALL3_ADDR="0xcA11bde05977b3631167028862bE2a173976CA11" CONTRACTS_L1_SHARED_BRIDGE_PROXY_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" CONTRACTS_L2_SHARED_BRIDGE_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" +CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR="0x36ea7f92f4c5f433efe15284e99c040110cf6297" CONTRACTS_L2_LEGACY_SHARED_BRIDGE_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" CONTRACTS_BRIDGEHUB_PROXY_ADDR="0x35ea7f92f4c5f433efe15284e99c040110cf6297" CONTRACTS_STATE_TRANSITION_PROXY_ADDR="0xd90f1c081c6117241624e97cb6147257c3cb2097" CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5" CONTRACTS_BASE_TOKEN_ADDR="0x0000000000000000000000000000000000000001" +CONTRACTS_L1_BASE_TOKEN_ASSET_ID="0x0000000000000000000000000000000000000001000000000000000000000000" +CONTRACTS_L1_WRAPPED_BASE_TOKEN_STORE_ADDR="0x36ea7f92f4c5f433efe15284e99c040110cf6298" +CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR="0xfc073319977e314f251eae6ae6be76b0b3baeecf" +CONTRACTS_PREDEPLOYED_L2_WRAPPED_BASE_TOKEN_ADDRESS="0x35ea7f92f4c5f433efe15284e99c040110cf6299" CONTRACTS_CHAIN_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff" CONTRACTS_L2_DA_VALIDATOR_ADDR="0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff" CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR="0x0000000000000000000000000000000000000002" diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 12cbf996697b..1c9711ef62d2 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -34,6 +34,10 @@ impl ProtoRepr for proto::Contracts { .l1_bytecodes_supplier_addr .as_ref() .map(|x| parse_h160(x).expect("Invalid address")), + l1_wrapped_base_token_store: ecosystem_contracts + .l1_wrapped_base_token_store + .as_ref() + .map(|x| parse_h160(x).expect("Invalid address")), }) } else { None @@ -123,12 +127,6 @@ impl ProtoRepr for proto::Contracts { .map(|x| parse_h256(x)) .transpose() .context("base_token_asset_id")?, - l2_predeployed_wrapped_base_token_address: l2 - .predeployed_wrapped_base_token_address - .as_ref() - .map(|x| parse_h160(x)) - .transpose() - .context("l2 predeployed_wrapped_base_token_address")?, chain_admin_addr: l1 .chain_admin_addr .as_ref() @@ -164,6 +162,9 @@ impl ProtoRepr for proto::Contracts { l1_bytecodes_supplier_addr: ecosystem_contracts .l1_bytecodes_supplier_addr .map(|x| format!("{:?}", x)), + l1_wrapped_base_token_store: ecosystem_contracts + .l1_wrapped_base_token_store + .map(|x| format!("{:?}", x)), }); Self { ecosystem_contracts, @@ -184,9 +185,6 @@ impl ProtoRepr for proto::Contracts { legacy_shared_bridge_addr: this .l2_legacy_shared_bridge_addr .map(|a| format!("{:?}", a)), - predeployed_wrapped_base_token_address: this - .l2_predeployed_wrapped_base_token_address - .map(|x| format!("{:?}", x)), timestamp_asserter_addr: this .l2_timestamp_asserter_addr .map(|a| format!("{:?}", a)), diff --git a/core/lib/protobuf_config/src/proto/config/contracts.proto b/core/lib/protobuf_config/src/proto/config/contracts.proto index febbc981478b..31c6a122f800 100644 --- a/core/lib/protobuf_config/src/proto/config/contracts.proto +++ b/core/lib/protobuf_config/src/proto/config/contracts.proto @@ -7,6 +7,7 @@ message EcosystemContracts { optional string state_transition_proxy_addr = 2; // optional; h160 optional string transparent_proxy_admin_addr = 3; // optional; h160 optional string l1_bytecodes_supplier_addr = 4; // optional; h160 + optional string l1_wrapped_base_token_store = 5; // optional; h160 } message L1 { @@ -26,7 +27,7 @@ message L2 { optional string da_validator_addr = 2; // optional; H160 optional string legacy_shared_bridge_addr = 3; // optional; H160 optional string timestamp_asserter_addr = 4; // optional; H160 - optional string predeployed_wrapped_base_token_address = 5; // optional; H160 + reserved 5; reserved "predeployed_wrapped_base_token_address"; } message Bridge { diff --git a/core/lib/types/src/abi.rs b/core/lib/types/src/abi.rs index b40aaaf882e2..da51c6297d4d 100644 --- a/core/lib/types/src/abi.rs +++ b/core/lib/types/src/abi.rs @@ -531,7 +531,7 @@ impl GatewayUpgradeEncodedInput { pub struct ZkChainSpecificUpgradeData { pub base_token_asset_id: H256, pub l2_legacy_shared_bridge: Address, - pub predeployed_l2_weth_address: Address, + pub l2_predeployed_wrapped_base_token: Address, pub base_token_l1_address: Address, pub base_token_name: String, pub base_token_symbol: String, @@ -551,7 +551,7 @@ impl ZkChainSpecificUpgradeData { l2_legacy_shared_bridge: l2_legacy_shared_bridge?, // Note, that some chains may not contain previous deployment of L2 wrapped base // token. For those, zero address is used. - predeployed_l2_weth_address: predeployed_l2_weth_address.unwrap_or_default(), + l2_predeployed_wrapped_base_token: predeployed_l2_weth_address.unwrap_or_default(), base_token_l1_address: base_token_l1_address?, base_token_name: base_token_name?, base_token_symbol: base_token_symbol?, @@ -572,7 +572,7 @@ impl ZkChainSpecificUpgradeData { Token::Tuple(vec![ Token::FixedBytes(self.base_token_asset_id.0.to_vec()), Token::Address(self.l2_legacy_shared_bridge), - Token::Address(self.predeployed_l2_weth_address), + Token::Address(self.l2_predeployed_wrapped_base_token), Token::Address(self.base_token_l1_address), Token::String(self.base_token_name.clone()), Token::String(self.base_token_symbol.clone()), diff --git a/core/lib/vm_executor/src/oneshot/contracts.rs b/core/lib/vm_executor/src/oneshot/contracts.rs index 257ede5a7c7c..d67d1dfbc662 100644 --- a/core/lib/vm_executor/src/oneshot/contracts.rs +++ b/core/lib/vm_executor/src/oneshot/contracts.rs @@ -106,10 +106,8 @@ impl MultiVmBaseSystemContracts { ProtocolVersionId::Version21 | ProtocolVersionId::Version22 => &self.post_1_4_2, ProtocolVersionId::Version23 => &self.vm_1_5_0_small_memory, ProtocolVersionId::Version24 => &self.vm_1_5_0_increased_memory, - ProtocolVersionId::Version25 | ProtocolVersionId::Version26 => { - &self.vm_protocol_defense - } - ProtocolVersionId::Version27 => &self.gateway, + ProtocolVersionId::Version25 => &self.vm_protocol_defense, + ProtocolVersionId::Version26 | ProtocolVersionId::Version27 => &self.gateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not supported yet"), }; let base = base.clone(); diff --git a/core/lib/web3_decl/src/namespaces/unstable.rs b/core/lib/web3_decl/src/namespaces/unstable.rs index f666f02f2811..da18806d126c 100644 --- a/core/lib/web3_decl/src/namespaces/unstable.rs +++ b/core/lib/web3_decl/src/namespaces/unstable.rs @@ -38,4 +38,7 @@ pub trait UnstableNamespace { l1_batch_number: L1BatchNumber, chain_id: L2ChainId, ) -> RpcResult>; + + #[method(name = "unconfirmedTxsCount")] + async fn get_unconfirmed_txs_count(&self) -> RpcResult; } diff --git a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs index cfa8c84b05b0..214e34241cf9 100644 --- a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs +++ b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs @@ -40,4 +40,10 @@ impl UnstableNamespaceServer for UnstableNamespace { .await .map_err(|err| self.current_method().map_err(err)) } + + async fn get_unconfirmed_txs_count(&self) -> RpcResult { + self.get_unconfirmed_txs_count_impl() + .await + .map_err(|err| self.current_method().map_err(err)) + } } diff --git a/core/node/api_server/src/web3/namespaces/en.rs b/core/node/api_server/src/web3/namespaces/en.rs index b2baa8497c98..9ccecf9001b5 100644 --- a/core/node/api_server/src/web3/namespaces/en.rs +++ b/core/node/api_server/src/web3/namespaces/en.rs @@ -165,6 +165,7 @@ impl EnNamespace { .l1_transparent_proxy_admin_addr .unwrap(), l1_bytecodes_supplier_addr: self.state.api_config.l1_bytecodes_supplier_addr, + l1_wrapped_base_token_store: self.state.api_config.l1_wrapped_base_token_store, }) .context("Shared bridge doesn't supported")?) } diff --git a/core/node/api_server/src/web3/namespaces/unstable/mod.rs b/core/node/api_server/src/web3/namespaces/unstable/mod.rs index 47e43f10282b..c70cb6f6e0fe 100644 --- a/core/node/api_server/src/web3/namespaces/unstable/mod.rs +++ b/core/node/api_server/src/web3/namespaces/unstable/mod.rs @@ -139,4 +139,16 @@ impl UnstableNamespace { chain_id_leaf_proof_mask: chain_id_leaf_proof_mask as u64, })) } + + pub async fn get_unconfirmed_txs_count_impl(&self) -> Result { + let mut connection = self.state.acquire_connection().await?; + + let result = connection + .eth_sender_dal() + .get_unconfirmed_txs_count() + .await + .map_err(DalError::generalize)?; + + Ok(result) + } } diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index a50b9d062321..97ffd933c801 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -108,6 +108,7 @@ pub struct InternalApiConfig { pub estimate_gas_optimize_search: bool, pub bridge_addresses: api::BridgeAddresses, pub l1_bytecodes_supplier_addr: Option
, + pub l1_wrapped_base_token_store: Option
, pub l1_bridgehub_proxy_addr: Option
, pub l1_state_transition_proxy_addr: Option
, pub l1_transparent_proxy_admin_addr: Option
, @@ -169,6 +170,10 @@ impl InternalApiConfig { .ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + l1_wrapped_base_token_store: contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), l1_diamond_proxy_addr: contracts_config.diamond_proxy_addr, l2_testnet_paymaster_addr: contracts_config.l2_testnet_paymaster_addr, req_entities_limit: web3_config.req_entities_limit(), @@ -236,6 +241,10 @@ impl BridgeAddressesHandle { self.0.write().await.l1_shared_default_bridge = Some(l1_shared_bridge); } + pub async fn update_l2_shared_bridge(&self, l2_shared_bridge: Address) { + self.0.write().await.l2_shared_default_bridge = Some(l2_shared_bridge); + } + pub async fn read(&self) -> api::BridgeAddresses { self.0.read().await.clone() } diff --git a/core/node/eth_watch/src/client.rs b/core/node/eth_watch/src/client.rs index a86f760dc398..648c634ab8f4 100644 --- a/core/node/eth_watch/src/client.rs +++ b/core/node/eth_watch/src/client.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt, sync::Arc}; use anyhow::Context; use zksync_contracts::{ getters_facet_contract, state_transition_manager_contract, verifier_contract, - MESSAGE_ROOT_CONTRACT, + L1_ASSET_ROUTER_CONTRACT, MESSAGE_ROOT_CONTRACT, WRAPPED_BASE_TOKEN_STORE_CONTRACT, }; use zksync_eth_client::{ clients::{DynClient, L1}, @@ -12,12 +12,12 @@ use zksync_eth_client::{ }; use zksync_system_constants::L2_MESSAGE_ROOT_ADDRESS; use zksync_types::{ + abi::ZkChainSpecificUpgradeData, api::{ChainAggProof, Log}, - ethabi::{self, decode, Contract, ParamType}, - tokens::TokenMetadata, - web3::{BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, - Address, L1BatchNumber, L2ChainId, SLChainId, H256, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, - U64, + ethabi::{self, decode, encode, Contract, ParamType}, + web3::{keccak256, BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, + Address, L1BatchNumber, L2ChainId, SLChainId, H256, L2_NATIVE_TOKEN_VAULT_ADDRESS, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, U64, }; use zksync_web3_decl::{ client::{Network, L2}, @@ -64,7 +64,9 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { hashes: Vec, ) -> EnrichedClientResult>>>; - async fn get_base_token_metadata(&self) -> Result; + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError>; /// Returns ID of the chain. async fn chain_id(&self) -> EnrichedClientResult; @@ -97,27 +99,36 @@ pub struct EthHttpQueryClient { new_upgrade_cut_data_signature: H256, bytecode_published_signature: H256, bytecode_supplier_addr: Option
, + wrapped_base_token_store: Option
, + l1_shared_bridge_addr: Option
, // Only present for post-shared bridge chains. state_transition_manager_address: Option
, chain_admin_address: Option
, verifier_contract_abi: Contract, getters_facet_contract_abi: Contract, message_root_abi: Contract, + l1_asset_router_abi: Contract, + wrapped_base_token_store_abi: Contract, confirmations_for_eth_event: Option, + l2_chain_id: L2ChainId, } impl EthHttpQueryClient where Box>: GetLogsClient, { + #[allow(clippy::too_many_arguments)] pub fn new( client: Box>, diamond_proxy_addr: Address, bytecode_supplier_addr: Option
, + wrapped_base_token_store: Option
, + l1_shared_bridge_addr: Option
, state_transition_manager_address: Option
, chain_admin_address: Option
, governance_address: Address, confirmations_for_eth_event: Option, + l2_chain_id: L2ChainId, ) -> Self { tracing::debug!( "New eth client, ZKsync addr: {:x}, governance addr: {:?}", @@ -143,7 +154,12 @@ where verifier_contract_abi: verifier_contract(), getters_facet_contract_abi: getters_facet_contract(), message_root_abi: MESSAGE_ROOT_CONTRACT.clone(), + l1_asset_router_abi: L1_ASSET_ROUTER_CONTRACT.clone(), + wrapped_base_token_store_abi: WRAPPED_BASE_TOKEN_STORE_CONTRACT.clone(), confirmations_for_eth_event, + wrapped_base_token_store, + l1_shared_bridge_addr, + l2_chain_id, } } @@ -439,51 +455,100 @@ where .await } - async fn get_base_token_metadata(&self) -> Result { - let base_token_addr: Address = CallFunctionArgs::new("getBaseToken", ()) + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + let Some(l1_shared_bridge_addr) = self.l1_shared_bridge_addr else { + tracing::warn!("l1 shared bridge is not provided!"); + return Ok(None); + }; + + let Some(l1_wrapped_base_token_store) = self.wrapped_base_token_store else { + tracing::warn!("l1 wrapped base token store is not provided!"); + return Ok(None); + }; + + let l2_chain_id = U256::from(self.l2_chain_id.as_u64()); + + // It does not matter whether the l1 shared bridge is an L1AssetRouter or L1Nullifier, + // either way it supports the "l2BridgeAddress" method. + let l2_legacy_shared_bridge: Address = + CallFunctionArgs::new("l2BridgeAddress", l2_chain_id) + .for_contract(l1_shared_bridge_addr, &self.l1_asset_router_abi) + .call(&self.client) + .await?; + + if l2_legacy_shared_bridge == Address::zero() { + // This state is not completely impossible, but somewhat undesirable. + // Contracts will still allow the upgrade to go through without + // the shared bridge, so we will allow it here as well. + tracing::error!("L2 shared bridge from L1 is empty"); + } + + let l2_predeployed_wrapped_base_token: Address = + CallFunctionArgs::new("l2WBaseTokenAddress", l2_chain_id) + .for_contract( + l1_wrapped_base_token_store, + &self.wrapped_base_token_store_abi, + ) + .call(&self.client) + .await?; + + if l2_predeployed_wrapped_base_token == Address::zero() { + // This state is not completely impossible, but somewhat undesirable. + // Contracts will still allow the upgrade to go through without + // the l2 predeployed wrapped base token, so we will allow it here as well. + tracing::error!("L2 predeployed wrapped base token is empty"); + } + + let base_token_l1_address: Address = CallFunctionArgs::new("getBaseToken", ()) .for_contract(self.diamond_proxy_addr, &self.getters_facet_contract_abi) .call(&self.client) .await?; - if base_token_addr == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { - return Ok(TokenMetadata { - name: String::from("Ether"), - symbol: String::from("ETH"), - decimals: 18, - }); - } + let (base_token_name, base_token_symbol) = + if base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + (String::from("Ether"), String::from("ETH")) + } else { + // TODO(EVM-934): support non-standard tokens. + let selectors: [[u8; 4]; 2] = [ + ethabi::short_signature("name", &[]), + ethabi::short_signature("symbol", &[]), + ]; + let types: [ParamType; 2] = [ParamType::String, ParamType::String]; + + let mut decoded_result = vec![]; + for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { + let request = CallRequest { + to: Some(base_token_l1_address), + data: Some(selector.into()), + ..Default::default() + }; + let result = self.client.call_contract_function(request, None).await?; + // Base tokens are expected to support erc20 metadata + let mut token = ethabi::decode(&[param_type], &result.0) + .expect("base token does not support erc20 metadata"); + decoded_result.push(token.pop().unwrap()); + } - // TODO(EVM-934): support non-standard tokens. - let selectors: [[u8; 4]; 3] = [ - zksync_types::ethabi::short_signature("name", &[]), - zksync_types::ethabi::short_signature("symbol", &[]), - zksync_types::ethabi::short_signature("decimals", &[]), - ]; - let types: [ParamType; 3] = [ParamType::String, ParamType::String, ParamType::Uint(32)]; - - let mut decoded_result = vec![]; - for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { - let request = CallRequest { - to: Some(base_token_addr), - data: Some(selector.into()), - ..Default::default() + (decoded_result[0].to_string(), decoded_result[1].to_string()) }; - let result = self.client.call_contract_function(request, None).await?; - // Base tokens are expected to support erc20 metadata - let mut token = zksync_types::ethabi::decode(&[param_type], &result.0) - .expect("base token does not support erc20 metadata"); - decoded_result.push(token.pop().unwrap()); - } - Ok(TokenMetadata { - name: decoded_result[0].to_string(), - symbol: decoded_result[1].to_string(), - decimals: decoded_result[2] - .clone() - .into_uint() - .expect("decimals not supported") - .as_u32() as u8, - }) + let base_token_asset_id = encode_ntv_asset_id( + // Note, that this is correct only for tokens that are being upgraded to the gateway protocol version. + // The chains that were deployed after it may have tokens with non-L1 base tokens. + U256::from(self.chain_id().await?.0), + base_token_l1_address, + ); + + Ok(Some(ZkChainSpecificUpgradeData { + base_token_asset_id, + l2_legacy_shared_bridge, + l2_predeployed_wrapped_base_token, + base_token_l1_address, + base_token_name, + base_token_symbol, + })) } } @@ -632,8 +697,10 @@ impl EthClient for L2EthClientW { self.0.get_chain_root(block_number, l2_chain_id).await } - async fn get_base_token_metadata(&self) -> Result { - self.0.get_base_token_metadata().await + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + self.0.get_chain_gateway_upgrade_info().await } async fn get_published_preimages( @@ -643,3 +710,13 @@ impl EthClient for L2EthClientW { self.0.get_published_preimages(hashes).await } } + +pub(crate) fn encode_ntv_asset_id(l1_chain_id: U256, addr: Address) -> H256 { + let encoded_data = encode(&[ + ethabi::Token::Uint(l1_chain_id), + ethabi::Token::Address(L2_NATIVE_TOKEN_VAULT_ADDRESS), + ethabi::Token::Address(addr), + ]); + + H256(keccak256(&encoded_data)) +} diff --git a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs index c2f499b8ce64..2892d6ca718f 100644 --- a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs +++ b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs @@ -3,9 +3,8 @@ use std::sync::Arc; use anyhow::Context as _; use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError}; use zksync_types::{ - abi::ZkChainSpecificUpgradeData, api::Log, ethabi::Contract, - protocol_upgrade::ProtocolUpgradePreimageOracle, protocol_version::ProtocolSemanticVersion, - ProtocolUpgrade, H256, U256, + api::Log, ethabi::Contract, protocol_upgrade::ProtocolUpgradePreimageOracle, + protocol_version::ProtocolSemanticVersion, ProtocolUpgrade, H256, U256, }; use crate::{ @@ -20,7 +19,6 @@ pub struct DecentralizedUpgradesEventProcessor { /// Last protocol version seen. Used to skip events for already known upgrade proposals. last_seen_protocol_version: ProtocolSemanticVersion, update_upgrade_timestamp_signature: H256, - chain_specific_data: Option, sl_client: Arc, l1_client: Arc, } @@ -29,7 +27,6 @@ impl DecentralizedUpgradesEventProcessor { pub fn new( last_seen_protocol_version: ProtocolSemanticVersion, chain_admin_contract: &Contract, - chain_specific_data: Option, sl_client: Arc, l1_client: Arc, ) -> Self { @@ -40,7 +37,6 @@ impl DecentralizedUpgradesEventProcessor { .context("UpdateUpgradeTimestamp event is missing in ABI") .unwrap() .signature(), - chain_specific_data, sl_client, l1_client, } @@ -58,7 +54,10 @@ impl ProtocolUpgradePreimageOracle for &dyn EthClient { let mut result = vec![]; for (i, preimage) in preimages.into_iter().enumerate() { let preimage = preimage.with_context(|| { - format!("Protocol upgrade preimage for {:#?} is missing", hashes[i]) + format!( + "Protocol upgrade preimage under id {i} for {:#?} is missing", + hashes[i] + ) })?; result.push(preimage); } @@ -93,7 +92,7 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { ..ProtocolUpgrade::try_from_diamond_cut( &diamond_cut, self.l1_client.as_ref(), - self.chain_specific_data.clone(), + self.l1_client.get_chain_gateway_upgrade_info().await?, ) .await? }; diff --git a/core/node/eth_watch/src/lib.rs b/core/node/eth_watch/src/lib.rs index 59f441457139..f866c8e627c5 100644 --- a/core/node/eth_watch/src/lib.rs +++ b/core/node/eth_watch/src/lib.rs @@ -6,14 +6,12 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context as _; use tokio::sync::watch; -use zksync_config::ContractsConfig; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal, DalError}; use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_system_constants::PRIORITY_EXPIRATION; use zksync_types::{ - abi::ZkChainSpecificUpgradeData, ethabi::Contract, protocol_version::ProtocolSemanticVersion, - tokens::TokenMetadata, web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, - PriorityOpId, + ethabi::Contract, protocol_version::ProtocolSemanticVersion, + web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, PriorityOpId, }; pub use self::client::{EthClient, EthHttpQueryClient, L2EthClient}; @@ -58,7 +56,6 @@ impl EthWatch { sl_l2_client: Option>, pool: ConnectionPool, poll_interval: Duration, - contracts_config: &ContractsConfig, chain_id: L2ChainId, ) -> anyhow::Result { let mut storage = pool.connection_tagged("eth_watch").await?; @@ -79,7 +76,6 @@ impl EthWatch { let decentralized_upgrades_processor = DecentralizedUpgradesEventProcessor::new( state.last_seen_protocol_version, chain_admin_contract, - get_chain_specific_upgrade_params(&l1_client, contracts_config).await?, sl_client.clone(), l1_client.clone(), ); @@ -246,19 +242,3 @@ impl EthWatch { Ok(()) } } - -async fn get_chain_specific_upgrade_params( - l1_client: &Arc, - contracts_config: &ContractsConfig, -) -> anyhow::Result> { - let TokenMetadata { name, symbol, .. } = l1_client.get_base_token_metadata().await?; - - Ok(ZkChainSpecificUpgradeData::from_partial_components( - contracts_config.l1_base_token_asset_id, - contracts_config.l2_legacy_shared_bridge_addr, - contracts_config.l2_predeployed_wrapped_base_token_address, - contracts_config.base_token_addr, - Some(name), - Some(symbol), - )) -} diff --git a/core/node/eth_watch/src/tests/client.rs b/core/node/eth_watch/src/tests/client.rs index f242488949b1..cec297435225 100644 --- a/core/node/eth_watch/src/tests/client.rs +++ b/core/node/eth_watch/src/tests/client.rs @@ -6,19 +6,19 @@ use zksync_contracts::{ }; use zksync_eth_client::{ContractCallError, EnrichedClientResult}; use zksync_types::{ - abi::{self, ProposedUpgrade}, + abi::{self, ProposedUpgrade, ZkChainSpecificUpgradeData}, api::{ChainAggProof, Log}, bytecode::BytecodeHash, ethabi::{self, Token}, l1::L1Tx, protocol_upgrade::ProtocolUpgradeTx, - tokens::TokenMetadata, u256_to_h256, web3::{contract::Tokenizable, BlockNumber}, - Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, U256, U64, + Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, U64, }; -use crate::client::{EthClient, L2EthClient, RETRY_LIMIT}; +use crate::client::{encode_ntv_asset_id, EthClient, L2EthClient, RETRY_LIMIT}; #[derive(Debug)] pub struct FakeEthClientData { @@ -306,12 +306,20 @@ impl EthClient for MockEthClient { Ok(result) } - async fn get_base_token_metadata(&self) -> Result { - Ok(TokenMetadata { - name: "ETH".to_string(), - symbol: "Ether".to_string(), - decimals: 18, - }) + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + Ok(Some(ZkChainSpecificUpgradeData { + base_token_asset_id: encode_ntv_asset_id( + self.chain_id().await?.0.into(), + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + ), + l2_legacy_shared_bridge: Address::repeat_byte(0x01), + l2_predeployed_wrapped_base_token: Address::repeat_byte(0x02), + base_token_l1_address: SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + base_token_name: String::from("Ether"), + base_token_symbol: String::from("ETH"), + })) } async fn fflonk_scheduler_vk_hash( diff --git a/core/node/eth_watch/src/tests/mod.rs b/core/node/eth_watch/src/tests/mod.rs index e6c7945b7d93..496f47545f09 100644 --- a/core/node/eth_watch/src/tests/mod.rs +++ b/core/node/eth_watch/src/tests/mod.rs @@ -1,6 +1,5 @@ use std::convert::TryInto; -use zksync_config::ContractsConfig; use zksync_contracts::chain_admin_contract; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -72,6 +71,7 @@ fn build_upgrade_tx(id: ProtocolVersionId) -> ProtocolUpgradeTx { common_data: ProtocolUpgradeTxCommonData { upgrade_id: id, sender: [1u8; 20].into(), + // Note, that the field is deprecated eth_block: 0, gas_limit: Default::default(), max_fee_per_gas: Default::default(), @@ -109,7 +109,6 @@ async fn create_test_watcher( sl_l2_client, connection_pool, std::time::Duration::from_nanos(1), - &ContractsConfig::for_tests(), L2ChainId::default(), ) .await @@ -216,7 +215,6 @@ async fn test_normal_operation_upgrade_timestamp() { None, connection_pool.clone(), std::time::Duration::from_nanos(1), - &ContractsConfig::for_tests(), L2ChainId::default(), ) .await diff --git a/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs b/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs index e9ce4cc19e1a..b8951c2a91ca 100644 --- a/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs +++ b/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs @@ -6,7 +6,10 @@ use zksync_eth_sender::EthTxManager; use crate::{ implementations::resources::{ circuit_breakers::CircuitBreakersResource, - eth_interface::{BoundEthInterfaceForBlobsResource, BoundEthInterfaceResource}, + eth_interface::{ + BoundEthInterfaceForBlobsResource, BoundEthInterfaceForL2Resource, + BoundEthInterfaceResource, + }, gas_adjuster::GasAdjusterResource, healthcheck::AppHealthCheckResource, pools::{MasterPool, PoolResource, ReplicaPool}, @@ -46,6 +49,7 @@ pub struct Input { pub replica_pool: PoolResource, pub eth_client: BoundEthInterfaceResource, pub eth_client_blobs: Option, + pub eth_client_gateway: Option, pub gas_adjuster: GasAdjusterResource, #[context(default)] pub circuit_breakers: CircuitBreakersResource, @@ -80,10 +84,9 @@ impl WiringLayer for EthTxManagerLayer { let master_pool = input.master_pool.get().await.unwrap(); let replica_pool = input.replica_pool.get().await.unwrap(); - let settlement_mode = self.eth_sender_config.gas_adjuster.unwrap().settlement_mode; let eth_client = input.eth_client.0.clone(); let eth_client_blobs = input.eth_client_blobs.map(|c| c.0); - let l2_client = input.eth_client.0; + let l2_client = input.eth_client_gateway.map(|c| c.0); let config = self.eth_sender_config.sender.context("sender")?; @@ -93,21 +96,9 @@ impl WiringLayer for EthTxManagerLayer { master_pool, config, gas_adjuster, - if !settlement_mode.is_gateway() { - Some(eth_client) - } else { - None - }, - if !settlement_mode.is_gateway() { - eth_client_blobs - } else { - None - }, - if settlement_mode.is_gateway() { - Some(l2_client) - } else { - None - }, + Some(eth_client), + eth_client_blobs, + l2_client, ); // Insert circuit breaker. diff --git a/core/node/node_framework/src/implementations/layers/eth_watch.rs b/core/node/node_framework/src/implementations/layers/eth_watch.rs index da0e26355cf2..92356e770c40 100644 --- a/core/node/node_framework/src/implementations/layers/eth_watch.rs +++ b/core/node/node_framework/src/implementations/layers/eth_watch.rs @@ -98,6 +98,11 @@ impl WiringLayer for EthWatchLayer { .ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + self.contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), + self.contracts_config.l1_shared_bridge_proxy_addr, self.contracts_config .ecosystem_contracts .as_ref() @@ -105,6 +110,7 @@ impl WiringLayer for EthWatchLayer { self.contracts_config.chain_admin_addr, self.contracts_config.governance_addr, self.eth_watch_config.confirmations_for_eth_event, + self.chain_id, ); let sl_l2_client: Option> = @@ -113,12 +119,17 @@ impl WiringLayer for EthWatchLayer { Some(Box::new(EthHttpQueryClient::new( gateway_client.0, contracts_config.diamond_proxy_addr, - // Bytecode supplier is only present on L1 + // Only present on L1. + None, + // Only present on L1. + None, + // Only present on L1. None, Some(contracts_config.state_transition_proxy_addr), contracts_config.chain_admin_addr, contracts_config.governance_addr, self.eth_watch_config.confirmations_for_eth_event, + self.chain_id, ))) } else { None @@ -130,7 +141,6 @@ impl WiringLayer for EthWatchLayer { sl_l2_client, main_pool, self.eth_watch_config.poll_interval(), - &self.contracts_config, self.chain_id, ) .await?; diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs index 785c19846a60..5bfd0984e5de 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs @@ -1,8 +1,8 @@ use std::time::Duration; -use zksync_eth_client::CallFunctionArgs; +use zksync_eth_client::{CallFunctionArgs, ContractCallError}; use zksync_node_api_server::web3::state::BridgeAddressesHandle; -use zksync_types::{ethabi::Contract, Address}; +use zksync_types::{ethabi::Contract, Address, L2_ASSET_ROUTER_ADDRESS}; use zksync_web3_decl::{ client::{DynClient, L1, L2}, namespaces::ZksNamespaceClient, @@ -37,20 +37,53 @@ pub struct L1UpdaterInner { pub bridgehub_addr: Address, pub update_interval: Option, pub bridgehub_abi: Contract, + pub l1_asset_router_abi: Contract, +} + +#[derive(Debug)] +struct L1SharedBridgeInfo { + l1_shared_bridge_addr: Address, + should_use_l2_asset_router: bool, } impl L1UpdaterInner { - async fn loop_iteration(&self) { - let call_result = CallFunctionArgs::new("sharedBridge", ()) + async fn get_shared_bridge_info(&self) -> Result { + let l1_shared_bridge_addr: Address = CallFunctionArgs::new("sharedBridge", ()) .for_contract(self.bridgehub_addr, &self.bridgehub_abi) .call(&self.l1_eth_client) - .await; + .await?; + + let l1_nullifier_addr: Result = + CallFunctionArgs::new("L1_NULLIFIER", ()) + .for_contract(l1_shared_bridge_addr, &self.l1_asset_router_abi) + .call(&self.l1_eth_client) + .await; - match call_result { - Ok(shared_bridge_address) => { + // In case we can successfully retrieve the l1 nullifier, this is definitely the new l1 asset router. + // The contrary is not necessarily true: the query can fail either due to network issues or + // due to the contract being outdated. To be conservative, we just always treat such cases as `false`. + let should_use_l2_asset_router = l1_nullifier_addr.is_ok(); + + Ok(L1SharedBridgeInfo { + l1_shared_bridge_addr, + should_use_l2_asset_router, + }) + } + + async fn loop_iteration(&self) { + match self.get_shared_bridge_info().await { + Ok(info) => { self.bridge_address_updater - .update_l1_shared_bridge(shared_bridge_address) + .update_l1_shared_bridge(info.l1_shared_bridge_addr) .await; + // We only update one way: + // - Once the L2 asset router should be used, there is never a need to go back + // - To not undo the previous change in case of a network error + if info.should_use_l2_asset_router { + self.bridge_address_updater + .update_l2_shared_bridge(L2_ASSET_ROUTER_ADDRESS) + .await; + } } Err(err) => { tracing::error!("Failed to query shared bridge address, error: {err:?}"); diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs index c4c18b6ecb3f..01b1d5ac5359 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs @@ -5,7 +5,7 @@ use bridge_addresses::{L1UpdaterInner, MainNodeUpdaterInner}; use tokio::{sync::oneshot, task::JoinHandle}; use zksync_circuit_breaker::replication_lag::ReplicationLagChecker; use zksync_config::configs::api::MaxResponseSize; -use zksync_contracts::bridgehub_contract; +use zksync_contracts::{bridgehub_contract, L1_ASSET_ROUTER_CONTRACT}; use zksync_node_api_server::web3::{ state::{BridgeAddressesHandle, InternalApiConfig, SealedL2BlockNumber}, ApiBuilder, ApiServer, Namespace, @@ -209,25 +209,26 @@ impl WiringLayer for Web3ServerLayer { // In case it is an EN, the bridge addresses should be updated by fetching values from the main node. // It is the main node, the bridge addresses need to be updated by querying the L1. - - let bridge_addresses_updater_task = if let Some(main_node_client) = input.main_node_client { - BridgeAddressesUpdaterTask::MainNodeUpdater(MainNodeUpdaterInner { - bridge_address_updater: bridge_addresses_handle.clone(), - main_node_client: main_node_client.0, - update_interval: self.optional_config.bridge_addresses_refresh_interval, - }) - } else { - BridgeAddressesUpdaterTask::L1Updater(L1UpdaterInner { - bridge_address_updater: bridge_addresses_handle.clone(), - l1_eth_client: input.l1_eth_client.0, - bridgehub_addr: self - .internal_api_config - .l1_bridgehub_proxy_addr - .context("Lacking l1 bridgehub proxy address")?, - update_interval: self.optional_config.bridge_addresses_refresh_interval, - bridgehub_abi: bridgehub_contract(), - }) - }; + let bridge_addresses_updater_task = + if let Some(main_node_client) = input.main_node_client.clone() { + BridgeAddressesUpdaterTask::MainNodeUpdater(MainNodeUpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + main_node_client: main_node_client.0, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + }) + } else { + BridgeAddressesUpdaterTask::L1Updater(L1UpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + l1_eth_client: input.l1_eth_client.0, + bridgehub_addr: self + .internal_api_config + .l1_bridgehub_proxy_addr + .context("Lacking l1 bridgehub proxy address")?, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + bridgehub_abi: bridgehub_contract(), + l1_asset_router_abi: L1_ASSET_ROUTER_CONTRACT.clone(), + }) + }; // Build server. let mut api_builder = @@ -251,6 +252,9 @@ impl WiringLayer for Web3ServerLayer { if let Some(sync_state) = sync_state { api_builder = api_builder.with_sync_state(sync_state); } + if let Some(main_node_client) = input.main_node_client { + api_builder = api_builder.with_l2_l1_log_proof_handler(main_node_client.0) + } let replication_lag_limit = self.optional_config.replication_lag_limit; api_builder = self.optional_config.apply(api_builder);