Skip to content

Commit

Permalink
[pallet-revive] do not trap the caller on instantiations with duplica…
Browse files Browse the repository at this point in the history
…te contracts (paritytech#7414)

This PR changes the behavior of `instantiate` when the resulting
contract address already exists (because the caller tried to instantiate
the same contract with the same salt multiple times): Instead of
trapping the caller, return an error code.

Solidity allows `catch`ing this, which doesn't work if we are trapping
the caller. For example, the change makes the following snippet work:

```Solidity
try new Foo{salt: hex"00"}() returns (Foo) {
    // Instantiation was successful (contract address was free and constructor did not revert)
} catch {
    // This branch is expected to be taken if the instantiation failed because of a duplicate salt
}
```

`revive` PR: paritytech/revive#188

---------

Signed-off-by: Cyrill Leutwiler <[email protected]>
Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
xermicus and github-actions[bot] authored Feb 3, 2025
1 parent 4f4f6f8 commit 274a781
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 0 deletions.
20 changes: 20 additions & 0 deletions prdoc/pr_7414.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
title: '[pallet-revive] do not trap the caller on instantiations with duplicate contracts'
doc:
- audience: Runtime Dev
description: |-
This PR changes the behavior of `instantiate` when the resulting contract address already exists (because the caller tried to instantiate the same contract with the same salt multiple times): Instead of trapping the caller, return an error code.

Solidity allows `catch`ing this, which doesn't work if we are trapping the caller. For example, the change makes the following snippet work:

```Solidity
try new Foo{salt: hex"00"}() returns (Foo) {
// Instantiation was successful (contract address was free and constructor did not revert)
} catch {
// This branch is expected to be taken if the instantiation failed because of a duplicate salt
}
```
crates:
- name: pallet-revive
bump: major
- name: pallet-revive-uapi
bump: major
12 changes: 12 additions & 0 deletions substrate/frame/revive/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,18 @@ fn instantiate_return_code() {
.data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect())
.build_and_unwrap_result();
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);

// Contract instantiation succeeds
let result = builder::bare_call(contract.addr)
.data(callee_hash.iter().chain(&0u32.to_le_bytes()).cloned().collect())
.build_and_unwrap_result();
assert_return_code!(result, 0);

// Contract instantiation fails because the same salt is being used again.
let result = builder::bare_call(contract.addr)
.data(callee_hash.iter().chain(&0u32.to_le_bytes()).cloned().collect())
.build_and_unwrap_result();
assert_return_code!(result, RuntimeReturnCode::DuplicateContractAddress);
});
}

Expand Down
2 changes: 2 additions & 0 deletions substrate/frame/revive/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,10 +792,12 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
let transfer_failed = Error::<E::T>::TransferFailed.into();
let out_of_gas = Error::<E::T>::OutOfGas.into();
let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
let duplicate_contract = Error::<E::T>::DuplicateContract.into();

// errors in the callee do not trap the caller
match (from.error, from.origin) {
(err, _) if err == transfer_failed => Ok(TransferFailed),
(err, _) if err == duplicate_contract => Ok(DuplicateContractAddress),
(err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources),
(_, Callee) => Ok(CalleeTrapped),
(err, _) => Err(err),
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/revive/uapi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ define_error_codes! {
XcmExecutionFailed = 9,
/// The `xcm_send` call failed.
XcmSendFailed = 10,
/// Contract instantiation failed because the address already exists.
/// Occurs when instantiating the same contract with the same salt more than once.
DuplicateContractAddress = 11,
}

/// The raw return code returned by the host side.
Expand Down

0 comments on commit 274a781

Please sign in to comment.