initial SELFDESTRUCT support (#400)

Note:
- The unstable interface in `v2509.0.0` of `polkadot-sdk` is required.
- The differential test fails against EVM in `v2509.0.0` of
`polkadot-sdk`.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus
2025-11-03 15:04:04 +01:00
committed by GitHub
parent c0cdde5a5a
commit 70037e1136
8 changed files with 97 additions and 31 deletions
+3
View File
@@ -6,6 +6,9 @@ This is a development pre-release.
Supported `polkadot-sdk` rev: `2509.0.0`
### Added
- Support for `selfdestruct`.
### Changed
- Emulated EVM heap memory accesses of zero length are never out of bounds.
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
// TODO: This currently fails the differential test.
// The pallet doesn't send the correct balance back.
/* runner.json
{
"differential": false,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "SelfdestructTester"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Selfdestruct"
}
},
"value": 123456789
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract Selfdestruct {
address tester;
uint value;
constructor() payable {
require(msg.value > 0, "the test should have value");
value = msg.value;
SelfdestructTester s = new SelfdestructTester{value: msg.value}();
tester = address(s);
}
fallback() external {
(bool success, ) = tester.call(hex"");
require(success, "the call to the self destructing contract should succeed");
}
}
contract SelfdestructTester {
constructor() payable {}
fallback() external {
selfdestruct(payable(msg.sender));
}
}
+1
View File
@@ -63,6 +63,7 @@ test_spec!(layout_at, "LayoutAt", "LayoutAt.sol");
test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate {
@@ -60,3 +60,16 @@ pub fn invalid(context: &mut Context) -> anyhow::Result<()> {
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
Ok(())
}
/// Translates the `selfdestruct` instruction.
pub fn selfdestruct<'ctx>(
context: &mut Context<'ctx>,
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> {
let address_pointer = context.build_address_argument_store(address)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::TERMINATE,
&[address_pointer.to_int(context).into()],
);
Ok(())
}
@@ -98,26 +98,3 @@ contract ExternalCodeCopy {
build_solidity(sources(&[("test.sol", code)])).unwrap();
}
#[test]
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
fn selfdestruct_yul() {
let solidity = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MinimalDestructible {
address payable public owner;
constructor() {
owner = payable(msg.sender);
}
function destroy() public {
require(msg.sender == owner, "Only the owner can call this function.");
selfdestruct(owner);
}
}"#;
build_solidity(sources(&[("test.sol", solidity)])).unwrap();
}
+2
View File
@@ -101,4 +101,6 @@ POLKAVM_IMPORT(void, set_immutable_data, uint32_t, uint32_t);
POLKAVM_IMPORT(uint32_t, set_storage_or_clear, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, terminate, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t)
+4 -1
View File
@@ -64,11 +64,13 @@ pub static SET_IMMUTABLE_DATA: &str = "set_immutable_data";
pub static SET_STORAGE: &str = "set_storage_or_clear";
pub static TERMINATE: &str = "terminate";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 32] = [
pub static IMPORTS: [&str; 33] = [
ADDRESS,
BALANCE,
BALANCE_OF,
@@ -100,6 +102,7 @@ pub static IMPORTS: [&str; 32] = [
RETURNDATASIZE,
SET_IMMUTABLE_DATA,
SET_STORAGE,
TERMINATE,
VALUE_TRANSFERRED,
];
@@ -661,6 +661,14 @@ impl FunctionCall {
Name::Invalid => {
revive_llvm_context::polkavm_evm_return::invalid(context).map(|_| None)
}
Name::SelfDestruct => {
let arguments = self.pop_arguments_llvm::<1>(context)?;
revive_llvm_context::polkavm_evm_return::selfdestruct(
context,
arguments[0].into_int_value(),
)
.map(|_| None)
}
Name::Log0 => {
let arguments = self.pop_arguments_llvm::<2>(context)?;
@@ -962,13 +970,6 @@ impl FunctionCall {
location
)
}
Name::SelfDestruct => {
let _arguments = self.pop_arguments_llvm::<1>(context)?;
anyhow::bail!(
"{} The `SELFDESTRUCT` instruction is not supported",
location
)
}
}
}