Implement extcodehash (#77)

This commit is contained in:
Ermal Kaleci
2024-10-21 10:13:50 +02:00
committed by GitHub
parent 82ae22c163
commit ee83d28a51
10 changed files with 582 additions and 477 deletions
Generated
+436 -469
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -67,7 +67,7 @@ log = { version = "0.4" }
# polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.1", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "5f55185eac6b4366484cf1bd7d374b2e652b076a" }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "5e0843e5a0fb601016dc1ccd46451a96049600e9" }
# llvm
[workspace.dependencies.inkwell]
+12
View File
@@ -14,4 +14,16 @@ contract ExtCode {
ret := codesize()
}
}
function ExtCodeHash(address who) public view returns (bytes32 ret) {
assembly {
ret := extcodehash(who)
}
}
function CodeHash() public view returns (bytes32 ret) {
assembly {
ret := extcodehash(address())
}
}
}
+6
View File
@@ -178,10 +178,16 @@ sol!(
function ExtCodeSize(address who) public view returns (uint ret);
function CodeSize() public pure returns (uint ret);
function ExtCodeHash(address who) public view returns (bytes32 ret);
function CodeHash() public view returns (bytes32 ret);
}
);
case!("ExtCode.sol", ExtCode, ExtCodeSizeCall, ext_code_size, address: Address);
case!("ExtCode.sol", ExtCode, CodeSizeCall, code_size,);
case!("ExtCode.sol", ExtCode, ExtCodeHashCall, ext_code_hash, address: Address);
case!("ExtCode.sol", ExtCode, CodeHashCall, code_hash,);
sol!(
contract MCopy {
+86
View File
@@ -234,6 +234,92 @@ fn signed_remainder() {
run_differential(actions);
}
#[test]
fn ext_code_hash() {
let mut actions = instantiate("contracts/ExtCode.sol", "ExtCode");
// First do contract instantiation to figure out address and code hash
let results = Specs {
actions: actions.clone(),
..Default::default()
}
.run();
let (addr, code_hash) = match results.first().cloned() {
Some(CallResult::Instantiate {
result, code_hash, ..
}) => (result.result.unwrap().addr, code_hash),
_ => panic!("instantiate contract failed"),
};
// code hash of itself
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::code_hash().calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(code_hash.as_bytes().to_vec()),
gas_consumed: None,
}));
// code hash for a given contract address
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from(addr.to_fixed_bytes())).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(code_hash.as_bytes().to_vec()),
gas_consumed: None,
}));
// EOA returns fixed hash
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from(CHARLIE.to_fixed_bytes())).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(
hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").to_vec(),
),
gas_consumed: None,
}));
// non-existing account
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from([8u8; 20])).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}));
Specs {
actions,
..Default::default()
}
.run();
}
/*
// These test were implement for the mock-runtime and need to be ported yet.
@@ -49,11 +49,31 @@ where
/// Translates the `extcodehash` instruction.
pub fn hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
context: &mut Context<'ctx, D>,
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated =
context
.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = context.build_byte_swap(address_truncated.into())?;
context.build_store(address_pointer, address_swapped)?;
let extcodehash_pointer =
context.build_alloca_at_entry(context.word_type(), "extcodehash_pointer");
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CODE_HASH,
&[
address_pointer.to_int(context).into(),
extcodehash_pointer.to_int(context).into(),
],
);
context.build_byte_swap(context.build_load(extcodehash_pointer, "extcodehash_value")?)
}
+2 -1
View File
@@ -32,7 +32,7 @@ use polkadot_sdk::{
pallet_revive::{CollectEvents, ContractExecResult, ContractInstantiateResult, DebugInfo},
polkadot_runtime_common::BuildStorage,
polkadot_sdk_frame::testing_prelude::*,
sp_core::H160,
sp_core::{H160, H256},
sp_keystore::{testing::MemoryKeystore, KeystoreExt},
sp_runtime::AccountId32,
};
@@ -185,6 +185,7 @@ pub enum CallResult {
Instantiate {
result: ContractInstantiateResult<Balance, EventRecord>,
wall_time: Duration,
code_hash: H256,
},
}
+10 -2
View File
@@ -4,7 +4,7 @@ use pallet_revive::AddressMapper;
use serde::{Deserialize, Serialize};
use crate::*;
use alloy_primitives::Address;
use alloy_primitives::{keccak256, Address};
#[cfg(feature = "revive-solidity")]
use revive_differential::{Evm, EvmLog};
#[cfg(feature = "revive-solidity")]
@@ -409,6 +409,13 @@ impl Specs {
data,
salt,
} => {
let code: pallet_revive::Code = code.into();
let code_hash = match code.clone() {
pallet_revive::Code::Existing(code_hash) => code_hash,
pallet_revive::Code::Upload(bytes) => {
H256::from_slice(keccak256(&bytes).as_slice())
}
};
let origin = RuntimeOrigin::signed(origin.to_account_id(&results));
let time_start = Instant::now();
let result = Contracts::bare_instantiate(
@@ -416,7 +423,7 @@ impl Specs {
value,
gas_limit.unwrap_or(GAS_LIMIT),
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT),
code.into(),
code,
data,
salt.0,
DebugInfo::Skip,
@@ -425,6 +432,7 @@ impl Specs {
results.push(CallResult::Instantiate {
result,
wall_time: time_start.elapsed(),
code_hash,
})
}
Upload {
+2
View File
@@ -80,6 +80,8 @@ POLKAVM_IMPORT(void, chain_id, uint32_t)
POLKAVM_IMPORT(uint32_t, code_size, uint32_t)
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, get_immutable_data, uint32_t, uint32_t);
+4 -1
View File
@@ -30,6 +30,8 @@ pub static CHAIN_ID: &str = "chain_id";
pub static CODE_SIZE: &str = "code_size";
pub static CODE_HASH: &str = "code_hash";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static GET_IMMUTABLE_DATA: &str = "get_immutable_data";
@@ -58,7 +60,7 @@ pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 23] = [
pub static IMPORTS: [&str; 24] = [
SBRK,
MEMORY_SIZE,
ADDRESS,
@@ -69,6 +71,7 @@ pub static IMPORTS: [&str; 23] = [
CALLER,
CHAIN_ID,
CODE_SIZE,
CODE_HASH,
DEPOSIT_EVENT,
GET_IMMUTABLE_DATA,
GET_STORAGE,