You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: RUNTIME-CONTRIBUTING.md
+68-37Lines changed: 68 additions & 37 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,9 @@
1
1
## Overview
2
+
2
3
This readme covers some basic introduction to the concept of WASM runtime, with guidelines and recommended approaches for creating, testing, and verifying runtime upgrades.
3
4
4
5
## Target audiance
6
+
5
7
This guide is targeted to developers, council members and any technical community member that wants to participate in the process of runtime upgrade of the chain.
6
8
7
9
It is expected you have some technical and coding background and some knowlege about substrate in general and joystream runtime specifically.
@@ -13,6 +15,7 @@ A key feature of the joystream network, and substrate based chains in general is
13
15
The runtime is stored on chain, at a well known storage key, as an [WebAssembly](https://docs.substrate.io/reference/glossary/#webassembly-wasm) binary, or WASM for short. The WASM is produced by building the joystream [runtime](./runtime) source code, which is made up of substrate and joystream pallets or [rutime-modules](./runtime-modules).
14
16
15
17
## WASM compression
18
+
16
19
The raw compiled WASM is averaging ~5MB. This is too large to fit in transactions and blocks. So the build process compacts the original produce WASM and finally compresses it with the [Zstandard](https://facebook.github.io/zstd/) fast compression algorithm.
17
20
Note a compressed runtime has a magic 8 byte pre-fix so to decompress we must first strip them.
18
21
@@ -28,6 +31,7 @@ You can then decompress with following command:
subwasm get wss://rpc.joystream.org:9944/ -o ./runtime.wasm
58
+
subwasm get wss://rpc.joystream.org -o ./runtime.wasm
55
59
# Get runtime version and hash
56
60
subwasm info ./runtime.wasm
57
61
```
58
62
59
63
### From polkadot-js-app
64
+
60
65
Visit https://polkadot.js.org/apps/#/chainstate/raw and make sure you are pointing at the joystream network.
61
66
62
67
Enter storage key: `0x3a636f6465`
63
68
Copy hex string and use a tool to convert it to a binary file.
64
69
65
-
### From QueryNode
70
+
### From QueryNode
71
+
66
72
It is best to use the approaches mentioned above as the most direct way to fetch the rutnime, but it can also be found in the query-node, if at least one runtime upgrade has been performed through governance.
67
73
68
74
```
@@ -75,6 +81,7 @@ Query the runtimeCode table by hash to fetch the wasm blob
75
81
Reference code from [pioneer](https://github.com/Joystream/pioneer/blob/dev/packages/ui/src/proposals/hooks/useRuntimeBytecode.ts)
76
82
77
83
## Verifying runtime builds
84
+
78
85
By verification we mean that we can follow steps to get a guarantee that a specific runtime wasm blob was compiled/built from a given codebase.
79
86
80
87
For verification to be possible, it is an essential property of the build process to be [deterministic](https://docs.substrate.io/build/build-a-deterministic-runtime/).
@@ -85,6 +92,7 @@ We have chosen the approach of using docker with a fixed version of the rust too
85
92
Polkadot adopts a similar approach and a generalized tool [`srtool`](https://docs.substrate.io/reference/command-line-tools/srtool/) exists for building runtimes. Perhaps we should be consider using that in future.
86
93
87
94
### Example - Verify onchain runtime
95
+
88
96
As a policy the master branch will be kept upto-date with the runtime used on mainnet
89
97
Lets verify that this is the case.
90
98
@@ -115,9 +123,10 @@ Compare results with the runtime you fetched from the chain in previous section.
115
123
If you are verifying a proposed runtime then you will need to build it yourself from what the proposer claims is the git commit/branch for the new proposed runtime, and compare it with the proposed runtime.
116
124
117
125
The proposed WASM can also be downloaded from the proposal details page in pioneer.
118
-
The hash of the propossed runtime should also be visible in pioneer.
126
+
The hash of the propossed runtime should also be visible in pioneer.
119
127
120
128
#### Howto fetch the proposed runtime
129
+
121
130
If you don't want to rely on pioneer,
122
131
another way to check the runtime proposed is with utility scripts provided.
123
132
@@ -147,6 +156,7 @@ Then follow steps from previous section on how extract the wasm file from the im
147
156
and inspect its hash and version information.
148
157
149
158
#### Reviewing code changes
159
+
150
160
Verifying the proposed runtime comes from a specific code base is only the first step ofcourse.
151
161
There is no guarantee the proposed runtime is safe to use, implements changes or new features outlined by the proposer.
### Sharing code changes without using public repo
167
-
A proposer may choose not to make changes public, they can be shared through more secure and private channels with the council members (who ultimately vote on the runtime upgrade proposal).
177
+
178
+
A proposer may choose not to make changes public, they can be shared through more secure and private channels with the council members (who ultimately vote on the runtime upgrade proposal).
168
179
169
180
The primary case where this might be a good idea is when fixing a bug, or security issue.
170
181
171
182
#### Using a patch file
172
-
One approach would be to provide the code changes via a patch file produced from the proposers local changes. Lets say proposer has created a local branch based off master branch called fix-runtime:
173
183
184
+
One approach would be to provide the code changes via a patch file produced from the proposers local changes. Lets say proposer has created a local branch based off master branch called fix-runtime:
This saves the code changes in a patch or diff file named fix-runtime.patch
181
192
This should then be encrypted with council members' or other trusted community members that would be tasked with reviewing and testing the patch, and possibly providing council members with their assesment.
182
193
183
-
After decrypting the patch file it can be applied for review:
194
+
After decrypting the patch file it can be applied for review:
184
195
185
196
```sh
186
197
git checkout master
@@ -190,6 +201,7 @@ git diff
190
201
```
191
202
192
203
#### Using a tarball
204
+
193
205
Alternatively all required files for building the runtime can be shared in an archive or 'tarball'
194
206
195
207
```sh
@@ -207,52 +219,58 @@ tar -czf joystream.tar.gz \
207
219
## Implementing a new runtime
208
220
209
221
### General workflow
222
+
210
223
Although how actual runtime changes are not detailed here, there is a general set of steps that should
211
224
be followed below:
212
225
213
226
#### Development cycle
214
227
215
-
1. Determine the "base" branch to bulid pn. This will typically be the master branch.
216
-
1. Bump the `spec_version` component of the runtime `pub const VERSION` in `runtime/src/lib.rs`
217
-
update the version of the cargo crates (Cargo.toml) for both the runtime and bin/node
218
-
runtime `spec_name` should not be changed.
219
-
If the runtime change is only an performance enhancement with no new state or state logic, then
220
-
only the `impl_version` needs to be changed.
221
-
1. Implement runtime changes, fixes, or new feature. This must includes benchmarking code and new unit tests as appropriate.
222
-
1. Run `yarn cargo-checks` to run the linter, code formatting check and unit tests.
223
-
1. Build the node with runtime benchmarks feature enabled, see `./scripts/cargo-build-with-benchmarks.sh`
224
-
1. Generate weights for modified/new benchmark functions on reference machine, and checkin the changes of the weights.rs - helper script is available in scripts/generate-weights.sh
225
-
1. Re run code formatting with `cargo fmt --all`
226
-
1. Do another test build with modified benchmarks `yarn cargo-checks && yarn cargo-build`
227
-
1. Extract the new runtime metadata `yarn update-chain-metadata`
228
-
1. Build all npm packages: `yarn build`
229
-
1. Add any new integration tests, query-node mappings that cover the changes implemented.
230
-
1. Lint typescript `yarn lint`
231
-
1. Build the testing runtime joystream/node docker image and run the full integration test suite (see Integration tests section below)
232
-
1. Commit your changes and push new branch to your repo.
233
-
1. Open a PR from your branch on upstream repo, targetting the current runtime release (master) so there is a clear code git diff showing the changes being implemented in the new runtime.
228
+
1. Determine the "base" branch to bulid pn. This will typically be the master branch.
229
+
1. Bump the `spec_version` component of the runtime `pub const VERSION` in `runtime/src/lib.rs`
230
+
update the version of the cargo crates (Cargo.toml) for both the runtime and bin/node
231
+
runtime `spec_name` should not be changed.
232
+
If the runtime change is only an performance enhancement with no new state or state logic, then
233
+
only the `impl_version` needs to be changed.
234
+
1. Implement runtime changes, fixes, or new feature. This must includes benchmarking code and new unit tests as appropriate.
235
+
1. Run `yarn cargo-checks` to run the linter, code formatting check and unit tests.
236
+
1. Build the node with runtime benchmarks feature enabled, see `./scripts/cargo-build-with-benchmarks.sh`
237
+
1. Generate weights for modified/new benchmark functions on reference machine, and checkin the changes of the weights.rs - helper script is available in scripts/generate-weights.sh
238
+
1. Re run code formatting with `cargo fmt --all`
239
+
1. Do another test build with modified benchmarks `yarn cargo-checks && yarn cargo-build`
240
+
1. Extract the new runtime metadata `yarn update-chain-metadata`
241
+
1. Build all npm packages: `yarn build`
242
+
1. Add any new integration tests, query-node mappings that cover the changes implemented.
243
+
1. Lint typescript `yarn lint`
244
+
1. Build the testing runtime joystream/node docker image and run the full integration test suite (see Integration tests section below)
245
+
1. Commit your changes and push new branch to your repo.
246
+
1. Open a PR from your branch on upstream repo, targetting the current runtime release (master) so there is a clear code git diff showing the changes being implemented in the new runtime.
234
247
235
248
You should typically wait for community and core dev team to review before taking the next step of creating the runtime upgrade proposal, as that will require staking a substantial amount of tokens which are at risk of being slashed if the council rejects the proposal.
236
249
237
250
#### Creating the proposal
238
-
1. Build the production joystream/node docker image.
239
-
1. Extract the compressed wasm blob from the docker image image.
240
-
1. Create a proposal in [pioneer](https://pioneerapp.xyz/#/proposals/current)
241
-
Follow instructions, and provide reference to the Pull Request, and upload the compressed wasm file.
242
-
251
+
252
+
1. Build the production joystream/node docker image.
253
+
1. Extract the compressed wasm blob from the docker image image.
254
+
1. Create a proposal in [pioneer](https://pioneerapp.xyz/#/proposals/current)
255
+
Follow instructions, and provide reference to the Pull Request, and upload the compressed wasm file.
256
+
243
257
### General Points
258
+
244
259
Making runtime changes are very critical and there are lots of details to keep in mind, especially when the runtime is an upgrade of the current chain, as apposed to a new runtime for a genesis block.
245
260
Below are some points to watch out for, what you can/cannot do.. and work arounds.
246
261
247
262
### Consensus algorithm
263
+
248
264
No changes should be made to the block interval.
249
265
250
266
### Runtime type changes
267
+
251
268
As much as possible avoid changing the types that are stord in state storage. If changes are made there must be either accompanying migration code, or custom decoding implementation for the type to ensure reading existing state from storage does not fail to decode. Adding new types and migrating state from old types is encouraged.
252
269
253
270
Ref: guide on how migrations can be done: https://docs.substrate.io/reference/how-to-guides/storage-migrations/basic-storage-migration/
254
271
255
272
### Runtime event types
273
+
256
274
It is generally not safe to modify runtime event type. The primary consumer of events is the query node,
257
275
and the current implementation of they query node does not have a built in mechanism to correctly handle changing of the event type structure through runtime changes, and it should generally be avoided.
258
276
@@ -261,9 +279,11 @@ If absolutely necessary the graphql schema and event handler must be aware of su
261
279
The issue is being worked on: https://github.com/Joystream/joystream/issues/4650
262
280
263
281
### New Genesis configurable state storage
282
+
264
283
When introducing new state storage that is configurable at genesus, keep in mind that the genesis build function for the pallet will not be executed and will not be assigned. Initialing such values must be done in the `on_runtime_upgrade()` hook of the runtime.
265
284
266
285
### OnRuntimeUpgrade hook
286
+
267
287
A custom [OnRuntimeUpgrade](https://github.com/Joystream/joystream/blob/master/runtime/src/runtime_api.rs#L63) is executed once when the runtime upgrades to the new version.
268
288
269
289
This is where we can execute migration code, or any the logic such as setting new storage values if necessary.
@@ -273,31 +293,37 @@ On a related note, there is a new approach seen in substrate code where the pall
273
293
This should be researched.
274
294
275
295
### Joystream fork of substrate
296
+
276
297
By inspecting Cargo.toml of runtime/, runtime-modules/ and bin/ you will note that the dependencies on substrate comes from:
At time of writing the modifications for vesting pallet have been ported to newer versions of substrate upstream, but not the staking pallet.
284
306
285
307
This should be kept in mind when planning a runtime upgrade that also updates the core version of substrate.
286
308
287
309
### Running integration tests
310
+
288
311
The best experience for doing development and testing is on a linux/ubuntu amd64 architecture machine, especially when it comes to working with docker.
289
312
290
313
### Runtime profiles
314
+
291
315
The runtime can be compiled with several cargo feature flags, to produce slightly different configurations.
292
316
The main difference between these configurations is around the council election periods lenghts, proposal periods (voting and gracing), and block production interval.
293
317
294
318
There are 4 profiles:
295
-
- production: used in production mainnet (this is the default when no explicit feature flag is provided)
296
-
- staging: used on long running staging testnets
297
-
- playground: used when deploying simple shared development testnets
298
-
- testing: used when running integration tests
319
+
320
+
- production: used in production mainnet (this is the default when no explicit feature flag is provided)
321
+
- staging: used on long running staging testnets
322
+
- playground: used when deploying simple shared development testnets
323
+
- testing: used when running integration tests
299
324
300
325
### Integration tests
326
+
301
327
How they differ from rust/cargo unit tests.
302
328
In addition to cargo unit tests for runtime features, over time we have developed a growing suite of integration tests. This is more of an end to end testing framework for testing proper functioning of the joystream platform as a whole. It involves running a scaled down joystream network on a local development machine and running through as many flows of interaction through extrinsics and testing that components such as the runtime, query-node and storage infrastructure behave as expected.
In addition to testing the new runtime in isolation, it is imperative that it be tested through performing an actual upgrade of the existing runtime. This would be done on a test network or playground. To make it practical the proposal needs to be executed in a short period in these test environments so using a testing profile or playground profile would be best.
327
355
328
356
Specific test scenario should be written to test for any state migration code performed after the upgrade, or for any custom decoding implemented for old types.
329
357
330
358
#### Automated runtime upgrade testing
359
+
331
360
There are some scripts in `tests/network-tests/run-migration-tests.sh` that are executed by github workflow to perform such tests,
332
361
but they should also be executed locally.
333
362
334
363
### Additional Resources
364
+
335
365
Some tooling that would be useful to add to our node and runtime to improve testing capabilities:
0 commit comments