Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: Multi block migrations #14045

Merged
merged 83 commits into from
May 31, 2023
Merged

contracts: Multi block migrations #14045

merged 83 commits into from
May 31, 2023

Conversation

pgherveou
Copy link
Contributor

@pgherveou pgherveou commented Apr 28, 2023

Multi-block migration framework for pallet-contracts.

Fix #13638

Test migration with try-runtime

Steps to test against contracts-rococo:

#1 build and run cumulus and move the binary to /usr/local/bin/polkadot-parachain
cargo build --release --bin polkadot-parachain
mv target/release/polkadot-parachain /usr/local/bin/polkadot-parachain

#2 sync it, and keep it running in one terminal tab
polkadot-parachain \
 --chain contracts-rococo \
 --base-path ~/Documents/db/contracts-rococo

#3 compile cumulus against local checkout, and build it with try-runtime feature
diener patch --crates-to-patch /path/to/substrate --substrate
cargo build -p contracts-rococo-runtime --features try-runtime --release

#4 run the migration with 
RUST_LOG="runtime::contracts=debug,try-runtime::cli=debug" ./target/release/polkadot-parachain try-runtime \
 --runtime target/release/wbuild/contracts-rococo-runtime/contracts_rococo_runtime.wasm \
 --chain contracts-rococo \
 on-runtime-upgrade live \
 --uri ws://localhost:9944

Instead of testing against a live chain, a snapshot of contracts-rococo running on version 8, can be used for testing.
One could test the migration, with the following snapshot.
contracts-rococo-9420@ed7ddd5b2bc635ff096f3457e18412cf8b7a7d0ca9375ad42aad65dae42c3077.snap.zip

It was generated with this command:

RUST_LOG="try-runtime::cli=debug" ./target/release/polkadot-parachain try-runtime \
 --runtime existing \
 --chain contracts-rococo \
create-snapshot \
--at  0xed7ddd5b2bc635ff096f3457e18412cf8b7a7d0ca9375ad42aad65dae42c3077  \
--uri ws://localhost:9944

It can be tested with this command

RUST_LOG="runtime::contracts=debug,try-runtime::cli=debug" ./target/release/polkadot-parachain try-runtime \
 --runtime target/release/wbuild/contracts-rococo-runtime/contracts_rococo_runtime.wasm \
 --chain contracts-rococo \
 on-runtime-upgrade snap --snapshot-path ./contracts-rococo-9420@60070dc60358594d0132b82aca52724f0913120002d46d07a72a51850ef282e8.snap

Test migration with zombienet

For testing this migration, I checked out polkadot-v0.9.32 that runs v8 of pallet-contracts so we can test all migrations steps included in this PR.

# go to polkadot dir
cargo build --bin polkadot --release --features fast-runtime --bin polkadot
mv target/release/polkadot /usr/local/bin/polkadot

# go to cumulus dir
git checkout polkadot-v0.9.32
cargo build --bin polkadot-parachain --release
mv target/release/polkadot-parachain /usr/local/bin/polkadot-parachain

You can now configure zombienet with

[relaychain]
default_command = "polkadot"
default_args = [ "-lparachain=debug" ]
chain = "rococo-local"

  [[relaychain.nodes]]
  name = "alice"
  validator = true

  [[relaychain.nodes]]
  name = "bob"
  validator = true

[[parachains]]
id = 1000
chain = "contracts-rococo-local"
cumulus_based = true

  [[parachains.collators]]
  name = "collator-1"
  command = "polkadot-parachain"
  args = ["-lparachain=debug", "--force-authoring" ]

start zombienet with

# assuming the config is saved under ~/Desktop/zombienet.toml
zombienet spawn --provider native ~/Desktop/zombienet.toml

Before running the migration, I use Polkadot-JS app and upload a dummy smart contract, so we can have some state to migrate. You could try to compile something with Ink, but that might be a bit tricky to get something that can work on such an old version. An easier way is to use wat2wasm to compile the dummy contract from pallet-contracts fixtures folder:

wat2wasm substrate/frame/contracts/fixtures/dummy.wat -o dummy.wasm

Then using Polkadot-JS: Developer -> extrinsics -> instantiateWithCode.

In Developer -> Chain state. You should be able to see this data for your storage:

contracts.contractInfoOf: Option<PalletContractsStorageContractInfo>
[
  [
    [
      5HjrdiRuv59XjnX8FxqKKvdkdxhJPa4DeR6zvyYZG2DNeVdo
    ]
    {
      trieId: 0xcc42a3f978ac2433dcc004700c6c7b3abac7e0b5f58bb21994b7e79b8ade5c28
      codeHash: 0x13585c8379d4578083cdb00ce4c6938ca8469e51e9a7ddc5129d3abb2c3d8786
      storageBytes: 0
      storageItems: 0
      storageByteDeposit: 0
      storageItemDeposit: 0
      storageBaseDeposit: 670,699,953
    }
  ]
]

system.account: FrameSystemAccountInfo
[
    [
      5HjrdiRuv59XjnX8FxqKKvdkdxhJPa4DeR6zvyYZG2DNeVdo
    ]
    {
      nonce: 0
      consumers: 0
      providers: 1
      sufficients: 0
      data: {
        free: 0
        reserved: 670,699,953
        miscFrozen: 0
        feeFrozen: 0
      }
    }
  ]

Now that we have setup the stage, we can go ahead and compile the new version of contracts-rococo
Before compiling make sure you bump the spec version:

parachains/runtimes/contracts/contracts-rococo/src/lib.rs
@@ -134,7 +134,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
  spec_name: create_runtime_str!("contracts-rococo"),
  impl_name: create_runtime_str!("contracts-rococo"),
  authoring_version: 1,
- spec_version: 9420,
+ spec_version: 9421,

Then compile with:

cd parachains/runtimes/contracts/contracts-rococo
cargo build --release

target/release/wbuild/contracts-rococo-runtime/contracts_rococo_runtime.compact.compressed.wasm is the wasm file you want to upload from polkadot.js to test the migration

You do that, we can use Polkadot-JS app again:

  • first do: Developer -> Extrinsics -> sudo > parachainSystem > authorizeUpgrade(codeHash)
  • Then: Developer -> Extrinsics -> parachainSystem > enactAuthorizedUpgrade(code)

Once the migration is passed you should be able to read this new state:

contracts.palletVersion: u16
11

contracts.contractInfoOf: Option<PalletContractsStorageContractInfo>
[
  [
    [
      5HjrdiRuv59XjnX8FxqKKvdkdxhJPa4DeR6zvyYZG2DNeVdo
    ]
    {
      trieId: 0xcc42a3f978ac2433dcc004700c6c7b3abac7e0b5f58bb21994b7e79b8ade5c28
      depositAccount: 5FvfZVJNAH938c21AQxqPv6fRpDvfTdUwAAc27XSyffwzqRR
      codeHash: 0x13585c8379d4578083cdb00ce4c6938ca8469e51e9a7ddc5129d3abb2c3d8786
      storageBytes: 0
      storageItems: 0
      storageByteDeposit: 0
      storageItemDeposit: 0
      storageBaseDeposit: 637,366,620
    }
  ]
]

system.account: FrameSystemAccountInfo
{
  nonce: 0
  consumers: 0
  providers: 2
  sufficients: 0
  data: {
    free: 33,333,333
    reserved: 0
    frozen: 0
    flags: 170,141,183,460,469,231,731,687,303,715,884,105,728
  }
}

system.account: FrameSystemAccountInfo
{
  nonce: 0
  consumers: 0
  providers: 1
  sufficients: 0
  data: {
    free: 637,366,620
    reserved: 0
    frozen: 0
    flags: 170,141,183,460,469,231,731,687,303,715,884,105,728
  }
}

Find blocks involved in a migration.

I built a custom CLI for that see repo here

> contracts-query --url "wss://rococo-contracts-rpc.polkadot.io:443" print-migrating-blocks --target-version 8
Fetching migration blocks:
2023-06-05 18:29:00.138 +02:00 -> BlockInfo { block_hash: 0xc5a879739b995b8b69655607f3ae59f8707c6e92026e953f9d474131808cf9e1, block_number: 2738932, version: 10, migration_in_progress: true }
2023-06-05 18:28:48.079 +02:00 -> BlockInfo { block_hash: 0x27224b9b37a031bedf507fa37c0aad108480a711a1b5ab572aa3a7680aa79bc8, block_number: 2738931, version: 9, migration_in_progress: true }
2023-06-05 18:28:00.135 +02:00 -> BlockInfo { block_hash: 0x60070dc60358594d0132b82aca52724f0913120002d46d07a72a51850ef282e8, block_number: 2738928, version: 8, migration_in_progress: true }
2023-06-05 18:26:24.076 +02:00 -> BlockInfo { block_hash: 0xed7ddd5b2bc635ff096f3457e18412cf8b7a7d0ca9375ad42aad65dae42c3077, block_number: 2738922, version: 8, migration_in_progress: false }
Summary:
Version 10 -> 11 took 01 block(s), from blocks 2738932 to 2738932
Version 09 -> 10 took 03 block(s), from blocks 2738929 to 2738931
Version 08 -> 09 took 06 block(s), from blocks 2738923 to 2738928

Steps to make it a generic migration framework

The migration framework as it is build in this PR lives inside pallet-contracts.
To pull it out of the pallet and create a reusable framework, the following updates should be made:

  • We are currently relying on MigrationInProgress storage.
#[pallet::storage]
pub(crate) type MigrationInProgress<T: Config> =
    StorageValue<_, migration::Cursor, OptionQuery>;

This storage defines a cursor for the current migration step. If it exists, a migration is in progress.
We check the existence of this value before each dispatchable:

#[pallet::call_index(...)]
#[pallet::weight(...)]
pub fn my_dispatchable(
   origin: OriginFor<T>,
   ...
) -> DispatchResult{
   Migration::<T>::ensure_migrated()?;
   ensure!(!MigrationInProgress::<T>::exists(), Error::<T>::MigrationInProgress);
   // ...

If we move this code to a library, we need to find a way to create this storage from something that is not a pallet
Or have Pallet implements a new trait, let's say MultiblockMigration

trait MultiBlockMigration {
    fn is_migration_in_progress() -> bool;
    fn set_migration_in_progress();
}

Ideally we implement some macros as well so that every Pallet that implements this trait get the ensure code automatically generated.

Once we have that, people can configure their Migration type that they pass to Executive with something like this

Migration<my_pallet::Pallet, (my_pallet::migration::v8, my_pallet::migration::v9, ...)>

This works similarly to to `translate` but only translate a single entry.
This function will be useful in the context of multi-block migration.
@pgherveou pgherveou changed the base branch from pg/frame-add-translate_next to at/migration May 2, 2023 10:41
@pgherveou pgherveou changed the base branch from at/migration to master May 2, 2023 10:42
@pgherveou pgherveou changed the base branch from master to pg/frame-add-translate_next May 2, 2023 10:42
@pgherveou pgherveou changed the base branch from pg/frame-add-translate_next to at/migration May 2, 2023 11:31
@pgherveou pgherveou changed the base branch from at/migration to master May 2, 2023 11:31
@pgherveou pgherveou changed the base branch from master to at/migration May 2, 2023 11:32
@pgherveou pgherveou changed the base branch from at/migration to master May 2, 2023 11:32
@pgherveou pgherveou changed the base branch from master to at/migration May 2, 2023 11:37
frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
@pgherveou pgherveou marked this pull request as ready for review May 2, 2023 12:49
@pgherveou pgherveou requested a review from athei as a code owner May 2, 2023 12:49
@pgherveou pgherveou added A0-please_review Pull request needs code review. C1-low PR touches the given topic and has a low impact on builders. D5-nicetohaveaudit ⚠️ PR contains trivial changes to logic that should be properly reviewed. B1-note_worthy Changes should be noted in the release notes labels May 2, 2023
@command-bot
Copy link

command-bot bot commented May 16, 2023

@pgherveou Command "$PIPELINE_SCRIPTS_DIR/commands/bench/bench.sh" pallet dev pallet_contracts has finished. Result: https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2843031 has finished. If any artifacts were generated, you can download them from https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2843031/artifacts/download.

frame/contracts/src/benchmarking/mod.rs Outdated Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
frame/contracts/src/migration.rs Show resolved Hide resolved
frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
pgherveou and others added 2 commits May 16, 2023 22:17
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
/// Calls that contribute to advancing the migration have their fees waived, as it's helpful
/// for the chain. Note that while the migration is in progress, the pallet will also
/// leverage the `on_idle` hooks to run migration steps.
#[pallet::call_index(9)]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note from talk with Sasha: should we give a different DispatchClass to this call?

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just asking based on title: could this have been made into something general and reusable?

@pgherveou
Copy link
Contributor Author

pgherveou commented May 17, 2023

just asking based on title: could this have been made into something general and reusable?

@kianenigma, it was easier to build it inside the pallet first, but its pretty generic already and I am looking forward to making it a broader solution that can be used by other pallets, once it has been battle tested.

@agryaznov
Copy link
Contributor

pls merge master into your branch

@pgherveou pgherveou requested a review from a team May 26, 2023 12:05
@pgherveou
Copy link
Contributor Author

bot merge

@paritytech-processbot paritytech-processbot bot merged commit 7f1861e into master May 31, 2023
@paritytech-processbot paritytech-processbot bot deleted the pg/migration branch May 31, 2023 14:19
frame/contracts/src/migration.rs Show resolved Hide resolved
frame/contracts/src/lib.rs Show resolved Hide resolved
frame/contracts/src/benchmarking/mod.rs Show resolved Hide resolved
frame/contracts/src/migration/v9.rs Show resolved Hide resolved
frame/contracts/src/migration/v10.rs Show resolved Hide resolved
frame/contracts/src/migration/v10.rs Show resolved Hide resolved
frame/contracts/src/migration/v10.rs Show resolved Hide resolved

// Attempt to transfer the old deposit to the deposit account.
let amount = old_deposit
.saturating_sub(min_balance)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why subtract min_balance here? if old_deposit < reducible_balance, then tranferring it away from the account should leave it with more than min_balance, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm we could test it again, but I think you need to remove the min_balance that was added in the original deposit to create the account.

frame/contracts/src/migration/v10.rs Show resolved Hide resolved
frame/contracts/src/benchmarking/mod.rs Show resolved Hide resolved
Dinonard pushed a commit to AstarNetwork/substrate that referenced this pull request Jul 10, 2023
* Frame Add translate_next

This works similarly to to `translate` but only translate a single entry.
This function will be useful in the context of multi-block migration.

* Move to lazy migration

* Updates

* simplify MockMigration

* wip

* wip

* add bench

* add bench

* fmt

* fix bench

* add .

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Scalfold v10 / v11 fix tests

* PR comment

* tweak pub use

* wip

* wip

* wip

* misc merge master

* misc merge master

* wip

* rm tmp stuff

* wip

* wip

* wip

* wip

* wip

* fixes

* add state

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* fixed compilation

* clean up logs

* wip

* Revert "Frame Add translate_next"

This reverts commit b878662.

* Fix v10 logic

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* wip

* fixes

* exercise del_queue

* bump sample size

* fmt

* wip

* blank line

* fix lint

* fix rustdoc job lint

* PR comment do not use dangerous into()

* Ad macros for updating mod visibility

* Add doc

* Add max_weight to integrity_test

* fix compilation

* Add no migration tests

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* fix clippy

* PR review

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Fix master merge

* fix merge 2

* fix tryruntime

* fix lint

---------

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: command-bot <>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
nathanwhit pushed a commit to nathanwhit/substrate that referenced this pull request Jul 19, 2023
* Frame Add translate_next

This works similarly to to `translate` but only translate a single entry.
This function will be useful in the context of multi-block migration.

* Move to lazy migration

* Updates

* simplify MockMigration

* wip

* wip

* add bench

* add bench

* fmt

* fix bench

* add .

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* Scalfold v10 / v11 fix tests

* PR comment

* tweak pub use

* wip

* wip

* wip

* misc merge master

* misc merge master

* wip

* rm tmp stuff

* wip

* wip

* wip

* wip

* wip

* fixes

* add state

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* fixed compilation

* clean up logs

* wip

* Revert "Frame Add translate_next"

This reverts commit b878662.

* Fix v10 logic

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* wip

* fixes

* exercise del_queue

* bump sample size

* fmt

* wip

* blank line

* fix lint

* fix rustdoc job lint

* PR comment do not use dangerous into()

* Ad macros for updating mod visibility

* Add doc

* Add max_weight to integrity_test

* fix compilation

* Add no migration tests

* ".git/.scripts/commands/bench/bench.sh" pallet dev pallet_contracts

* fix clippy

* PR review

* Update frame/contracts/src/lib.rs

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Fix master merge

* fix merge 2

* fix tryruntime

* fix lint

---------

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
Co-authored-by: command-bot <>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B1-note_worthy Changes should be noted in the release notes C1-low PR touches the given topic and has a low impact on builders. D5-nicetohaveaudit ⚠️ PR contains trivial changes to logic that should be properly reviewed. T1-runtime This PR/Issue is related to the topic “runtime”.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

contracts: Multi block migrations
5 participants