owned this note
owned this note
Published
Linked with GitHub
> Edit: Take a look at this too for a more automated version of the content on this page: https://github.com/maxsam4/fork-off-substrate
First, build two genesis specs:
```sh
./target/release/polkadot build-spec --chain=kusama --raw > /tmp/genesis-kusama.json;
./target/release/polkadot build-spec --dev --raw > /tmp/genesis-dev.json
```
Next, dump current Kusama state:
```sh
curl http://localhost:9933 -H "Content-type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"state_getPairs","params":["0x"]}' > /tmp/storage.json
```
Next, dump the current Kusama code. The Kusama code, as hex, can be found off of the `target` path. `hexdump` can be used to turn it into hex:
```sh
cat target/release/wbuild/kusama-runtime/kusama_runtime.compact.wasm | hexdump -ve '/1 "%02x"' > /tmp/kusama.hex
```
Identify all modules (or storage items) whose old values should be included in migration.
To determine a storage item's prefix using oo7-substrate, open a page and in the console, enter:
```js
function twox128(b) { return toLEHex(XXH.h64(b.buffer, 0), 8) + toLEHex(XXH.h64(b.buffer, 1), 8) }
```
then use e.g.:
```js
> twox128(stringToBytes("System")) + twox128(stringToBytes("AccountNonce"))
'26aa394eea5630e07c48ae0c9558cef79c2f82b23e5fd031fb54c292794b4cc4'
```
Polkadot-js can also be used; any storage value's hex can be logged via `console.log(api.query.<module>.<call>.key([...opt params]))`, e.g. `console.log(api.query.timestamp.now.key())`.
If you want a map/doublemap key prefix, you can do it via `.keyPrefix()`, e.g. `console.log(api.query.system.account.keyPrefix())`.
For module hashing, do it via the `hashing` object, e.g. `console.log(hashing.xxhashAsHex('System', 128))`.
Common modules include:
```js
[
'26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9', /* System.Account */
'bd2a529379475088d3e29a918cd47872', /* RandomnessCollectiveFlip */
'f0c365c3cf59d671eb72da0e7a4113c4', /* Timestamp */
'1a736d37504c2e3fb73dad160c55b291', /* Indices */
'c2261276cc9d1f8598ea4b6a74b15c2f', /* Balances */
'3f1467a096bcd71a5b6a0c8155e20810', /* TransactionPayment */
'd57bce545fb382c34570e5dfbf338f5e', /* Authorship */
'5f3e4907f716ac89b6347d15ececedca', /* Staking */
'd5c41b52a371aa36c9254ce34324f2a5', /* Offences */
'cec5070d609dd3497f72bde07fc96ba0', /* Session */
'2b06af9719ac64d755623cda8ddd9b94', /* ImOnline */
'2099d7f109d6e535fb000bba623fd440', /* AuthorityDiscovery */
'f2794c22e353e9a839f12faab03a911b', /* Democracy */
'11f3ba2e1cdd6d62f2ff9b5589e7ff81', /* Instance1Collective */
'8985776095addd4789fccbce8ca77b23', /* Instance2Collective */
'e2e62dd81c48a88f73b6f6463555fd8e', /* PhragmenElection */
'492a52699edf49c972c21db794cfcf57', /* Instance1Membership */
'89d139e01a5eb2256f222e5fc5dbe6b3', /* Treasury */
'9c5d795d0297be56027a4b2464e33397', /* Claims */
'0b76934f4cc08dee01012d059e1b83ee', /* Parachains */
'ae394d879ddf7f99595bc0dd36e355b5', /* Attestations */
'6ac983d82528bf1595ab26438ae5b2cf', /* Slots */
'3fba98689ebed1138735e0e7a5a790ab', /* Registrar */
'd5e1a2fa16732ce6906189438c0a82c6', /* Utility */
'2aeddc77fe58c98d50bd37f1b90840f9', /* Identity */
'426e15054d267946093858132eb537f1', /* Society */
'a2ce73642c549ae79c14f0a671cf45f9', /* Recovery */
'5f27b51b5ec208ee9cb25b55d8728243', /* Vesting */
'3db7a24cfdc9de785974746c14a99df9', /* Scheduler */
'1809d78346727a0ef58c0fa03bafa323', /* Proxy */
'7474449cca95dc5d0c00e71735a6d17d', /* Multisig */
]
```
Fire up a node shell and load the storage dump.
```js
var storage = JSON.parse(require('fs').readFileSync('/tmp/storage.json', 'utf8')).result;
```
Express the selected prefixes in node, e.g. we could do everything except system, session, babe, grandpa and finalitytracker, along with the system's account nonces:
```js
let prefixes = [
'26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9', /* System.Account */
'bd2a529379475088d3e29a918cd47872', /* RandomnessCollectiveFlip */
'f0c365c3cf59d671eb72da0e7a4113c4', /* Timestamp */
'1a736d37504c2e3fb73dad160c55b291', /* Indices */
'c2261276cc9d1f8598ea4b6a74b15c2f', /* Balances */
'3f1467a096bcd71a5b6a0c8155e20810', /* TransactionPayment */
'd57bce545fb382c34570e5dfbf338f5e', /* Authorship */
'5f3e4907f716ac89b6347d15ececedca', /* Staking */
'd5c41b52a371aa36c9254ce34324f2a5', /* Offences */
'cec5070d609dd3497f72bde07fc96ba0', /* Session */
'2b06af9719ac64d755623cda8ddd9b94', /* ImOnline */
'2099d7f109d6e535fb000bba623fd440', /* AuthorityDiscovery */
'f2794c22e353e9a839f12faab03a911b', /* Democracy */
'11f3ba2e1cdd6d62f2ff9b5589e7ff81', /* Instance1Collective */
'8985776095addd4789fccbce8ca77b23', /* Instance2Collective */
'e2e62dd81c48a88f73b6f6463555fd8e', /* PhragmenElection */
'492a52699edf49c972c21db794cfcf57', /* Instance1Membership */
'89d139e01a5eb2256f222e5fc5dbe6b3', /* Treasury */
'9c5d795d0297be56027a4b2464e33397', /* Claims */
'0b76934f4cc08dee01012d059e1b83ee', /* Parachains */
'ae394d879ddf7f99595bc0dd36e355b5', /* Attestations */
'6ac983d82528bf1595ab26438ae5b2cf', /* Slots */
'3fba98689ebed1138735e0e7a5a790ab', /* Registrar */
'd5e1a2fa16732ce6906189438c0a82c6', /* Utility */
'2aeddc77fe58c98d50bd37f1b90840f9', /* Identity */
'426e15054d267946093858132eb537f1', /* Society */
'a2ce73642c549ae79c14f0a671cf45f9', /* Recovery */
'5f27b51b5ec208ee9cb25b55d8728243', /* Vesting */
'3db7a24cfdc9de785974746c14a99df9', /* Scheduler */
'1809d78346727a0ef58c0fa03bafa323', /* Proxy */
'7474449cca95dc5d0c00e71735a6d17d', /* Multisig */
]
```
Load the two specs into node, ensuring the dev spec (which we will be mutating into the final spec) is mutable:
```js
let kusama = JSON.parse(require('fs').readFileSync('/tmp/genesis-kusama.json', 'utf8'));
var spec = JSON.parse(require('fs').readFileSync('/tmp/genesis-dev.json', 'utf8'))
```
By default, we have a "development" genesis spec, using the Polkadot runtime. Assuming we're testing Kusama, we'll want to change it to resemble the Kusama genesis spec.
Alter the spec to make it clear that it's Kusama for the UI:
```js
spec.name = kusama.name
spec.id = kusama.id
spec.protocolId = kusama.protocolId
```
Set the code to the current Kusama code:
```js
spec.genesis.raw.top['0x3a636f6465'] = '0x' + require('fs').readFileSync('/tmp/kusama.hex', 'utf8').trim(); null
```
To prevent the validator set from changing mid-test, set `Staking.ForceEra` to `ForceNone` (`'0x02'`):
(In oo7-substrate:)
```
> twox128(stringToBytes("Staking")) + twox128(stringToBytes("ForceEra"))
"5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3"
```
```js
spec.genesis.raw.top['0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3'] = '0x02'
```
We also delete `System.LastRuntimeUpgrade` to ensure that the `on_runtime_upgrade` event is triggered:
(In oo7-substrate:)
```
> twox128(stringToBytes("System")) + twox128(stringToBytes("LastRuntimeUpgrade"))
"26aa394eea5630e07c48ae0c9558cef746a1725a3a183380053e6f67ab1e00a6"
```
```js
delete spec.genesis.raw.top['0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8']
```
We may also need to remove any migration flags or set appropriate storage versions. e.g. `Balances.StorageVersion`:
(In oo7-substrate:)
```
> twox128(stringToBytes("Balances")) + twox128(stringToBytes("StorageVersion"))
"c2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1"
```
```js
spec.genesis.raw.top['0xc2261276cc9d1f8598ea4b6a74b15c2f308ce9615de0775a82f8a94dc3d285a1'] = '0x01'
```
Now, we move the desired storage keys over from the snapshot of the chain. We grab the items to be moved, then iterate through and insert into storage:
```js
storage.filter(i => prefixes.some(prefix => i[0].startsWith('0x'+prefix))).forEach(([key, value]) => spec.genesis.raw.top[key] = value)
```
Then dump the new spec out:
```js
require('fs').writeFile('/tmp/genesis.json', JSON.stringify(spec, null, 4), () => {})
```
Exit and try out the new spec:
```sh
./target/release/polkadot purge-chain --chain /tmp/genesis.json
./target/release/polkadot --chain /tmp/genesis.json --validator --alice
```
# The script
This is just a staging ground for a script for testing.
```sh
./target/release/polkadot build-spec --chain=kusama --raw > /tmp/genesis-kusama.json;
./target/release/polkadot build-spec --dev --raw > /tmp/genesis-dev.json
curl http://localhost:9933 -H "Content-type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"state_getPairs","params":["0x"]}' > /tmp/storage.json;
cat target/release/wbuild/kusama-runtime/kusama_runtime.compact.wasm | hexdump -ve '/1 "%02x"' > /tmp/kusama.hex
```
```js
let storage = JSON.parse(require('fs').readFileSync('/tmp/storage.json', 'utf8')).result;
let prefixes = [
'26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9', /* System.Account */
'bd2a529379475088d3e29a918cd47872', /* RandomnessCollectiveFlip */
'f0c365c3cf59d671eb72da0e7a4113c4', /* Timestamp */
'1a736d37504c2e3fb73dad160c55b291', /* Indices */
'c2261276cc9d1f8598ea4b6a74b15c2f', /* Balances */
'3f1467a096bcd71a5b6a0c8155e20810', /* TransactionPayment */
'd57bce545fb382c34570e5dfbf338f5e', /* Authorship */
'5f3e4907f716ac89b6347d15ececedca', /* Staking */
'd5c41b52a371aa36c9254ce34324f2a5', /* Offences */
'cec5070d609dd3497f72bde07fc96ba0', /* Session */
'2b06af9719ac64d755623cda8ddd9b94', /* ImOnline */
'2099d7f109d6e535fb000bba623fd440', /* AuthorityDiscovery */
'f2794c22e353e9a839f12faab03a911b', /* Democracy */
'11f3ba2e1cdd6d62f2ff9b5589e7ff81', /* Instance1Collective */
'8985776095addd4789fccbce8ca77b23', /* Instance2Collective */
'e2e62dd81c48a88f73b6f6463555fd8e', /* PhragmenElection */
'492a52699edf49c972c21db794cfcf57', /* Instance1Membership */
'89d139e01a5eb2256f222e5fc5dbe6b3', /* Treasury */
'9c5d795d0297be56027a4b2464e33397', /* Claims */
'0b76934f4cc08dee01012d059e1b83ee', /* Parachains */
'ae394d879ddf7f99595bc0dd36e355b5', /* Attestations */
'6ac983d82528bf1595ab26438ae5b2cf', /* Slots */
'3fba98689ebed1138735e0e7a5a790ab', /* Registrar */
'd5e1a2fa16732ce6906189438c0a82c6', /* Utility */
'2aeddc77fe58c98d50bd37f1b90840f9', /* Identity */
'426e15054d267946093858132eb537f1', /* Society */
'a2ce73642c549ae79c14f0a671cf45f9', /* Recovery */
'5f27b51b5ec208ee9cb25b55d8728243', /* Vesting */
'3db7a24cfdc9de785974746c14a99df9', /* Scheduler */
'1809d78346727a0ef58c0fa03bafa323', /* Proxy */
'7474449cca95dc5d0c00e71735a6d17d', /* Multisig */
];
let kusama = JSON.parse(require('fs').readFileSync('/tmp/genesis-kusama.json', 'utf8'));
var spec = JSON.parse(require('fs').readFileSync('/tmp/genesis-dev.json', 'utf8'));
spec.name = kusama.name
spec.id = kusama.id
spec.protocolId = kusama.protocolId
spec.genesis.raw.top['0x3a636f6465'] = '0x' + require('fs').readFileSync('/tmp/kusama.hex', 'utf8').trim(); null;
spec.genesis.raw.top['0x5f3e4907f716ac89b6347d15ececedcaf7dad0317324aecae8744b87fc95f2f3'] = '0x02';
spec.genesis.raw.top['0x26aa394eea5630e07c48ae0c9558cef746a1725a3a183380053e6f67ab1e00a6'] = '0x01';
storage.filter(i => prefixes.some(prefix => i[0].startsWith('0x'+prefix))).forEach(([key, value]) => spec.genesis.raw.top[key] = value);
delete spec.genesis.raw.top['0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8']
require('fs').writeFile('/tmp/genesis.json', JSON.stringify(spec, null, 4), () => {});
```