implement the code size opcodes (#107)

This commit is contained in:
Cyrill Leutwiler
2024-10-31 11:46:47 +01:00
committed by GitHub
parent 68564f9866
commit 43d2ef3ce9
6 changed files with 484 additions and 418 deletions
Generated
+357 -357
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 # polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" } codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.1", default-features = false } scale-info = { version = "2.11.1", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "db40a66db71e8e7fe943dda5cd0e28078efa2a19" } polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "2b6b69641ccff4d7aa9c32051bbb2f1e775ef8cc" }
# llvm # llvm
[workspace.dependencies.inkwell] [workspace.dependencies.inkwell]
+2 -4
View File
@@ -105,9 +105,7 @@ contract ERC20Tester {
ERC20 token = new ERC20(); ERC20 token = new ERC20();
assert(token.decimals() == 18); assert(token.decimals() == 18);
// use call directly when code_size is implemented on pallet-revive token.mint(300);
address(token).call(abi.encodeWithSignature("mint(uint256)", 300));
assert(token.balanceOf(address(this)) == 300); assert(token.balanceOf(address(this)) == 300);
token.transfer(BOB, 100); token.transfer(BOB, 100);
assert(token.balanceOf(address(this)) == 200); assert(token.balanceOf(address(this)) == 200);
@@ -119,7 +117,7 @@ contract ERC20Tester {
assert(token.balanceOf(BOB) == 200); assert(token.balanceOf(BOB) == 200);
assert(token.balanceOf(address(this)) == 100); assert(token.balanceOf(address(this)) == 100);
address(token).call(abi.encodeWithSignature("burn(uint256)", 100)); token.burn(100);
assert(token.balanceOf(address(this)) == 0); assert(token.balanceOf(address(this)) == 0);
} }
} }
+108 -24
View File
@@ -321,6 +321,114 @@ fn ext_code_hash() {
.run(); .run();
} }
#[test]
fn ext_code_size() {
let alice = Address::from(ALICE.0);
let own_address = alice.create(0);
let baseline_address = alice.create2([0u8; 32], keccak256(Contract::baseline().pvm_runtime));
let own_code_size = U256::from(
Contract::ext_code_size(Default::default())
.pvm_runtime
.len(),
);
let baseline_code_size = U256::from(Contract::baseline().pvm_runtime.len());
Specs {
actions: vec![
// Instantiate the test contract
instantiate("contracts/ExtCode.sol", "ExtCode").remove(0),
// Instantiate the baseline contract
Instantiate {
origin: TestAddress::Alice,
value: 0,
gas_limit: Some(GAS_LIMIT),
storage_deposit_limit: None,
code: Code::Solidity {
path: Some("contracts/Baseline.sol".into()),
contract: "Baseline".to_string(),
solc_optimizer: None,
pipeline: None,
},
data: vec![],
salt: OptionalHex::from([0; 32]),
},
// Alice is not a contract and returns a code size of 0
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(alice).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}),
// Unknown address returns a code size of 0
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(Address::from([0xff; 20])).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}),
// Own address via extcodesize returns own code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(own_address).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(own_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
// Own address via codesize returns own code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::code_size().calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(own_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
// Baseline address returns the baseline code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(baseline_address).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(baseline_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
],
..Default::default()
}
.run();
}
/* /*
// These test were implement for the mock-runtime and need to be ported yet. // These test were implement for the mock-runtime and need to be ported yet.
@@ -349,28 +457,4 @@ fn create2_failure() {
assert_eq!(output.flags, ReturnFlags::Revert); assert_eq!(output.flags, ReturnFlags::Revert);
} }
#[test]
fn ext_code_size() {
let contract = Contract::ext_code_size(Transaction::default_address());
let (_, output) = assert_success(&contract, false);
let received = U256::from_be_slice(&output.data);
let expected = U256::from(contract.pvm_runtime.len());
assert_eq!(received, expected);
let contract = Contract::ext_code_size(Default::default());
let (_, output) = assert_success(&contract, false);
let received = U256::from_be_slice(&output.data);
let expected = U256::ZERO;
assert_eq!(received, expected);
}
#[test]
fn code_size() {
let contract = Contract::code_size();
let (_, output) = assert_success(&contract, false);
let expected = U256::from(contract.pvm_runtime.len());
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received);
}
*/ */
+14 -30
View File
@@ -1,7 +1,5 @@
//! Translates the external code operations. //! Translates the external code operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context; use crate::polkavm::context::Context;
use crate::polkavm::Dependency; use crate::polkavm::Dependency;
@@ -14,37 +12,23 @@ pub fn size<'ctx, D>(
where where
D: Dependency + Clone, D: Dependency + Clone,
{ {
let address_pointer = match address { let address = match address {
Some(address) => { Some(address) => address,
let address_pointer = context.build_alloca(context.word_type(), "value"); None => super::context::address(context)?.into_int_value(),
context.build_store(address_pointer, address)?;
address_pointer
}
None => context.sentinel_pointer(),
}; };
let address_pointer_casted = context.builder().build_ptr_to_int( let address_pointer = context.build_address_argument_store(address)?;
address_pointer.value, let output_pointer = context.build_alloca_at_entry(context.word_type(), "output_pointer");
context.xlen_type(),
"address_pointer",
)?;
let value = context
.build_runtime_call(
revive_runtime_api::polkavm_imports::CODE_SIZE,
&[address_pointer_casted.into()],
)
.unwrap_or_else(|| {
panic!(
"{} should return a value",
revive_runtime_api::polkavm_imports::CODE_SIZE
)
})
.into_int_value();
Ok(context context.build_runtime_call(
.builder() revive_runtime_api::polkavm_imports::CODE_SIZE,
.build_int_z_extend(value, context.word_type(), "extcodesize")? &[
.as_basic_value_enum()) address_pointer.to_int(context).into(),
output_pointer.to_int(context).into(),
],
);
context.build_load(output_pointer, "code_size")
} }
/// Translates the `extcodehash` instruction. /// Translates the `extcodehash` instruction.
+1 -1
View File
@@ -78,7 +78,7 @@ POLKAVM_IMPORT(void, caller, uint32_t)
POLKAVM_IMPORT(void, chain_id, uint32_t) POLKAVM_IMPORT(void, chain_id, uint32_t)
POLKAVM_IMPORT(uint32_t, code_size, uint32_t) POLKAVM_IMPORT(uint32_t, code_size, uint32_t, uint32_t)
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t) POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)