Skip to content

Commit 274a781

Browse files
[pallet-revive] do not trap the caller on instantiations with duplicate contracts (#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>
1 parent 4f4f6f8 commit 274a781

File tree

4 files changed

+37
-0
lines changed

4 files changed

+37
-0
lines changed

prdoc/pr_7414.prdoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
title: '[pallet-revive] do not trap the caller on instantiations with duplicate contracts'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
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.
6+
7+
Solidity allows `catch`ing this, which doesn't work if we are trapping the caller. For example, the change makes the following snippet work:
8+
9+
```Solidity
10+
try new Foo{salt: hex"00"}() returns (Foo) {
11+
// Instantiation was successful (contract address was free and constructor did not revert)
12+
} catch {
13+
// This branch is expected to be taken if the instantiation failed because of a duplicate salt
14+
}
15+
```
16+
crates:
17+
- name: pallet-revive
18+
bump: major
19+
- name: pallet-revive-uapi
20+
bump: major

substrate/frame/revive/src/tests.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,18 @@ fn instantiate_return_code() {
16141614
.data(callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect())
16151615
.build_and_unwrap_result();
16161616
assert_return_code!(result, RuntimeReturnCode::CalleeTrapped);
1617+
1618+
// Contract instantiation succeeds
1619+
let result = builder::bare_call(contract.addr)
1620+
.data(callee_hash.iter().chain(&0u32.to_le_bytes()).cloned().collect())
1621+
.build_and_unwrap_result();
1622+
assert_return_code!(result, 0);
1623+
1624+
// Contract instantiation fails because the same salt is being used again.
1625+
let result = builder::bare_call(contract.addr)
1626+
.data(callee_hash.iter().chain(&0u32.to_le_bytes()).cloned().collect())
1627+
.build_and_unwrap_result();
1628+
assert_return_code!(result, RuntimeReturnCode::DuplicateContractAddress);
16171629
});
16181630
}
16191631

substrate/frame/revive/src/wasm/runtime.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,10 +792,12 @@ impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
792792
let transfer_failed = Error::<E::T>::TransferFailed.into();
793793
let out_of_gas = Error::<E::T>::OutOfGas.into();
794794
let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
795+
let duplicate_contract = Error::<E::T>::DuplicateContract.into();
795796

796797
// errors in the callee do not trap the caller
797798
match (from.error, from.origin) {
798799
(err, _) if err == transfer_failed => Ok(TransferFailed),
800+
(err, _) if err == duplicate_contract => Ok(DuplicateContractAddress),
799801
(err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources),
800802
(_, Callee) => Ok(CalleeTrapped),
801803
(err, _) => Err(err),

substrate/frame/revive/uapi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ define_error_codes! {
9898
XcmExecutionFailed = 9,
9999
/// The `xcm_send` call failed.
100100
XcmSendFailed = 10,
101+
/// Contract instantiation failed because the address already exists.
102+
/// Occurs when instantiating the same contract with the same salt more than once.
103+
DuplicateContractAddress = 11,
101104
}
102105

103106
/// The raw return code returned by the host side.

0 commit comments

Comments
 (0)