Skip to content

Commit e0762d8

Browse files
Use a more robust way to run fn instantiate for EVM contracts. (#3939)
## Motivation The `fn instantiate` function of EVM contract is called if the `instantiate_argument` is non-empty. But it is conceivable to have an empty instantiation argument. ## Proposal We can identify if a selector is present in the bytecode by looking for `[PUSH4; selector]` pattern. ## Test Plan The CI. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None.
1 parent b45d74b commit e0762d8

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

linera-execution/src/evm/revm.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ mod tests {
9191
}
9292
}
9393

94+
fn has_instantiation_function(module: &[u8]) -> bool {
95+
let push4 = 0x63; // An EVM instruction
96+
let mut vec = vec![push4];
97+
vec.extend(INSTANTIATE_SELECTOR);
98+
module.windows(5).any(|window| window == vec)
99+
}
100+
94101
#[cfg(with_metrics)]
95102
mod metrics {
96103
use std::sync::LazyLock;
@@ -833,8 +840,8 @@ where
833840
{
834841
fn instantiate(&mut self, argument: Vec<u8>) -> Result<(), ExecutionError> {
835842
self.initialize_contract()?;
836-
let instantiation_argument = serde_json::from_slice::<Vec<u8>>(&argument)?;
837-
if !instantiation_argument.is_empty() {
843+
if has_instantiation_function(&self.module) {
844+
let instantiation_argument = serde_json::from_slice::<Vec<u8>>(&argument)?;
838845
let argument = get_revm_instantiation_bytes(instantiation_argument);
839846
let result = self.transact_commit(Choice::Call, &argument)?;
840847
self.write_logs(result.logs, "instantiate")?;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
pragma solidity ^0.8.0;
4+
5+
contract ExampleExecuteMessage {
6+
uint64 value;
7+
8+
constructor() {
9+
value = 37;
10+
}
11+
12+
function instantiate(bytes memory input) external {
13+
require(input.length == 0);
14+
value = 42;
15+
}
16+
17+
function get_value() external view returns (uint64) {
18+
return value;
19+
}
20+
}

linera-service/tests/linera_net_tests.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,88 @@ async fn test_evm_execute_message_end_to_end_counter(config: impl LineraNetConfi
950950
Ok(())
951951
}
952952

953+
#[cfg(with_revm)]
954+
#[cfg_attr(feature = "storage-service", test_case(LocalNetConfig::new_test(Database::Service, Network::Grpc) ; "storage_test_service_grpc"))]
955+
#[cfg_attr(feature = "scylladb", test_case(LocalNetConfig::new_test(Database::ScyllaDb, Network::Grpc) ; "scylladb_grpc"))]
956+
#[cfg_attr(feature = "dynamodb", test_case(LocalNetConfig::new_test(Database::DynamoDb, Network::Grpc) ; "aws_grpc"))]
957+
#[cfg_attr(feature = "kubernetes", test_case(SharedLocalKubernetesNetTestingConfig::new(Network::Grpc, BuildArg::Build) ; "kubernetes_grpc"))]
958+
#[cfg_attr(feature = "remote-net", test_case(RemoteNetTestingConfig::new(None) ; "remote_net_grpc"))]
959+
#[test_log::test(tokio::test)]
960+
async fn test_evm_empty_instantiate(config: impl LineraNetConfig) -> Result<()> {
961+
use alloy_sol_types::{sol, SolCall};
962+
use linera_base::vm::EvmQuery;
963+
use linera_execution::test_utils::solidity::{get_evm_contract_path, read_evm_u64_entry};
964+
use linera_sdk::abis::evm::EvmAbi;
965+
let _guard = INTEGRATION_TEST_GUARD.lock().await;
966+
tracing::info!("Starting test {}", test_name!());
967+
968+
let (mut net, client1) = config.instantiate().await?;
969+
970+
let client2 = net.make_client().await;
971+
client2.wallet_init(None).await?;
972+
973+
let chain1 = client1.load_wallet()?.default_chain().unwrap();
974+
let chain2 = client1.open_and_assign(&client2, Amount::ONE).await?;
975+
976+
// Creating the API of the contracts
977+
978+
sol! {
979+
function get_value();
980+
}
981+
let query = get_valueCall {};
982+
let query = query.abi_encode();
983+
let query = EvmQuery::Query(query);
984+
985+
let constructor_argument = Vec::new();
986+
let instantiation_argument = Vec::new();
987+
988+
let (evm_contract, _dir) =
989+
get_evm_contract_path("tests/fixtures/evm_example_empty_instantiate.sol")?;
990+
991+
let application_id = client1
992+
.publish_and_create::<EvmAbi, Vec<u8>, Vec<u8>>(
993+
evm_contract.clone(),
994+
evm_contract,
995+
VmRuntime::Evm,
996+
&constructor_argument,
997+
&instantiation_argument,
998+
&[],
999+
None,
1000+
)
1001+
.await?;
1002+
1003+
let port1 = get_node_port().await;
1004+
let port2 = get_node_port().await;
1005+
let mut node_service1 = client1.run_node_service(port1, ProcessInbox::Skip).await?;
1006+
let mut node_service2 = client2.run_node_service(port2, ProcessInbox::Skip).await?;
1007+
1008+
// Creating the applications.
1009+
1010+
let application1 = node_service1
1011+
.make_application(&chain1, &application_id)
1012+
.await?;
1013+
1014+
let application2 = node_service2
1015+
.make_application(&chain2, &application_id)
1016+
.await?;
1017+
1018+
// Checking the initial value of the contracts.
1019+
let result = application1.run_json_query(query.clone()).await?;
1020+
let counter_value = read_evm_u64_entry(result);
1021+
assert_eq!(counter_value, 42);
1022+
let result = application2.run_json_query(query).await?;
1023+
let counter_value = read_evm_u64_entry(result);
1024+
assert_eq!(counter_value, 37);
1025+
1026+
node_service1.ensure_is_running()?;
1027+
node_service2.ensure_is_running()?;
1028+
1029+
net.ensure_is_running().await?;
1030+
net.terminate().await?;
1031+
1032+
Ok(())
1033+
}
1034+
9531035
#[cfg(with_revm)]
9541036
#[cfg_attr(feature = "storage-service", test_case(LocalNetConfig::new_test(Database::Service, Network::Grpc) ; "storage_test_service_grpc"))]
9551037
#[cfg_attr(feature = "scylladb", test_case(LocalNetConfig::new_test(Database::ScyllaDb, Network::Grpc) ; "scylladb_grpc"))]

0 commit comments

Comments
 (0)