-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Fix generated address returned by Substrate RPC runtime call #8504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
75cffc3
41fa5d3
e2171b5
a1cb3e0
5397556
b4047ff
b686c94
b1e357a
8c2d486
a69a1f2
4cc01dd
4d21dc3
89b0c05
5dc7092
b215946
683c84c
be30983
f8c3187
6c30760
998f58e
2517693
846b885
e350976
f929174
e5e683b
0fd29ad
7f124d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| title: Fix generated address returned by Substrate RPC runtime call | ||
| doc: | ||
| - audience: Runtime Dev | ||
| description: |- | ||
| ## Description | ||
|
|
||
| When dry-running a contract deployment through the runtime API, the returned address does not match the actual address that will be used when the transaction is submitted. This inconsistency occurs because the address derivation logic doesn't properly account for the difference between transaction execution and dry-run execution contexts. | ||
|
|
||
| The issue stems from the `create1` address derivation logic in `exec.rs`: | ||
|
|
||
| ```rust | ||
| address::create1( | ||
| &deployer, | ||
| // the Nonce from the origin has been incremented pre-dispatch, so we | ||
| // need to subtract 1 to get the nonce at the time of the call. | ||
| if origin_is_caller { | ||
| account_nonce.saturating_sub(1u32.into()).saturated_into() | ||
| } else { | ||
| account_nonce.saturated_into() | ||
| }, | ||
| ) | ||
| ``` | ||
|
|
||
| The code correctly subtracts 1 from the account nonce during a transaction execution (because the nonce is incremented pre-dispatch), but doesn't account for execution context - whether it's a real transaction or a dry run through the RPC. | ||
|
|
||
| ## Review Notes | ||
|
|
||
| This PR adds a new condition to check for the `ExecContext` when calculating the nonce for address derivation: | ||
|
|
||
| ```rust | ||
| address::create1( | ||
| &deployer, | ||
| // the Nonce from the origin has been incremented pre-dispatch, so we | ||
| // need to subtract 1 to get the nonce at the time of the call. | ||
| if origin_is_caller && matches!(exec_context, ExecContext::Transaction) { | ||
| account_nonce.saturating_sub(1u32.into()).saturated_into() | ||
| } else { | ||
| account_nonce.saturated_into() | ||
| }, | ||
| ) | ||
| ``` | ||
|
|
||
| A new test `nonce_not_incremented_in_dry_run()` has been added to verify the behavior. | ||
|
|
||
| ## Before Fix | ||
|
|
||
| - Dry-run contract deployment returns address derived with nonce N | ||
| - Actual transaction deployment creates contract at address derived with nonce N-1 | ||
| - Result: Inconsistent addresses between simulation and actual execution | ||
|
|
||
| ## After Fix | ||
|
|
||
| - Dry-run and actual transaction deployments both create contracts at the same address | ||
| - Result: Consistent contract addresses regardless of execution context | ||
| - Added test case to verify nonce handling in different execution contexts | ||
|
|
||
| This fix ensures that users can rely on the address returned by a dry run to match the actual address that will be used when the transaction is submitted. | ||
|
|
||
| Fixes https://github.com/paritytech/contract-issues/issues/37 | ||
|
|
||
| # Checklist | ||
|
|
||
| * [x] My PR includes a detailed description as outlined in the "Description" and its two subsections above. | ||
| * [x] My PR follows the [labeling requirements]( | ||
| https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/CONTRIBUTING.md#Process | ||
| ) of this project (at minimum one label for `T` required) | ||
| * External contributors: ask maintainers to put the right label on your PR. | ||
| * [x] I have made corresponding changes to the documentation (if applicable) | ||
| * [x] I have added tests that prove my fix is effective or that my feature works (if applicable) | ||
| crates: | ||
| - name: asset-hub-westend-runtime | ||
| bump: patch | ||
| - name: pallet-revive | ||
| bump: major |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -513,6 +513,18 @@ pub mod pallet { | |
| AddressMapping, | ||
| } | ||
|
|
||
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
| pub enum NonceAlreadyIncremented { | ||
| /// Indicates that the nonce has not been incremented yet. | ||
| /// | ||
| /// This happens when the instantiation is triggered by a dry-run or another contract. | ||
| No, | ||
| /// Indicates that the nonce has already been incremented. | ||
| /// | ||
| /// This happens when the instantiation is triggered by a transaction. | ||
| Yes, | ||
| } | ||
|
Comment on lines
+515
to
+526
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it makes sense to merge bump nonce logic with I suggest just sticking to the original
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you are right my bad, you can just pass the BumpNonce as an extra argument to |
||
|
|
||
castillax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// A mapping from a contract's code hash to its code. | ||
| #[pallet::storage] | ||
| pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, CodeVec>; | ||
|
|
@@ -797,6 +809,7 @@ pub mod pallet { | |
| Code::Existing(code_hash), | ||
| data, | ||
| salt, | ||
| NonceAlreadyIncremented::Yes, | ||
| ); | ||
| if let Ok(retval) = &output.result { | ||
| if retval.result.did_revert() { | ||
|
|
@@ -861,6 +874,7 @@ pub mod pallet { | |
| Code::Upload(code), | ||
| data, | ||
| salt, | ||
| NonceAlreadyIncremented::Yes, | ||
| ); | ||
| if let Ok(retval) = &output.result { | ||
| if retval.result.did_revert() { | ||
|
|
@@ -1074,6 +1088,7 @@ where | |
| code: Code, | ||
| data: Vec<u8>, | ||
| salt: Option<[u8; 32]>, | ||
| nonce_already_incremented: NonceAlreadyIncremented, | ||
| ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> { | ||
| let mut gas_meter = GasMeter::new(gas_limit); | ||
| let mut storage_deposit = Default::default(); | ||
|
|
@@ -1116,6 +1131,7 @@ where | |
| data, | ||
| salt.as_ref(), | ||
| unchecked_deposit_limit, | ||
| nonce_already_incremented, | ||
| ); | ||
| storage_deposit = storage_meter | ||
| .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)? | ||
|
|
@@ -1285,6 +1301,7 @@ where | |
| Code::Upload(code.to_vec()), | ||
| data.to_vec(), | ||
| None, | ||
| NonceAlreadyIncremented::No, | ||
| ); | ||
|
|
||
| let returned_data = match result.result { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.