# Gossamer Workshop #### Polkadot Decoded May 2021 #### Elizabeth github: @noot twitter: @elizabethereum ## 0. Introduction ![](https://i.imgur.com/f0aCiCv.png) ### What is gossamer? - Go implementation of the Polkadot Host; equivalent to substrate A Polkadot Host provides two main functions: 1. Import a runtime and easily launch a chain with custom logic 2. Create a Polkadot-compatible chain that could potentially become a parachain ### Current status of Gossamer: - interoperable with substrate nodes! - gossamer is able to act as a Polkadot/Kusama full node - compatible with existing tooling such as polkadot.js ### Future plans for gossamer: - interoperate with Polkadot as an authority node - currently running internal gossamer devnet - afterwards, we plan to run cross-client devnet to ensure interoperability between authority functionality A Polkadot Host cannot become a parachain on its own; it requires an additional library which connects the chain to the relay chain. - cumulus: https://github.com/paritytech/cumulus - in the future: go-cumulus! https://medium.com/chainsafe-systems/building-parachains-with-go-cumulus-9998735fa5a4 ### Agenda 1. How to import a runtime into gossamer 2. How to launch a gossamer development chain with a custom runtime 3. How to interact with the chain using polkadot.js 4. How to run a local non-authority node to connect with authority dev node 5. How to run a Polkadot/Kusama full node #### Workshop prerequisities - go 1.15 - node.js ## Building gossamer ``` git clone https://github.com/ChainSafe/gossamer cd gossamer make gossamer ``` This places the gossamer binary in `./bin/`. ## 1. Importing a runtime ### Obtaining a runtime Custom runtimes are built using FRAME, a domain-specific language for creating runtimes that satisfy the runtime API. They are then compiled into wasm for use with a Polkadot Host. API looks like this in Go: ```go Version() (Version, error) Metadata() ([]byte, error) BabeConfiguration() (*types.BabeConfiguration, error) GrandpaAuthorities() ([]*types.Authority, error) ValidateTransaction(e types.Extrinsic) (*transaction.Validity, error) InitializeBlock(header *types.Header) error InherentExtrinsics(data []byte) ([]byte, error) ApplyExtrinsic(data types.Extrinsic) ([]byte, error) FinalizeBlock() (*types.Header, error) ExecuteBlock(block *types.Block) ([]byte, error) ``` Creating a custom runtime is out of the scope of this workshop; I will be using a slightly modified substrate node runtime: https://github.com/paritytech/substrate/tree/master/bin/node/runtime Note: gossamer is compatible with the Polkadot v0.8 runtime, so we will need to checkout that branch/tag. ``` git clone substrate cd substrate git checkout sub-v.0.8.24.patch ``` or ``` git checkout 56205634082f7f64ae2eb1f09a57d79d04dc8597 ``` Modifications made to the substrate node runtime: 1. changed `impl_name` to `gossamer-node` 2. update `BabeGenesisConfiguration` `c` value to be `(1, 1)` - the `c` value is a tuple (a, b) where the probability of a slot being empty is `1 - (a/b)` - therefore `c = (1, 1)` means the probability of an empty slot is 0, thus a block is produced every slot - this is nice for a development chain ```rust pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("gossamer-node"), authoring_version: 10, // Per convention: if the runtime behavior changes, increment spec_version // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 261, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 2, }; fn configuration() -> sp_consensus_babe::BabeGenesisConfiguration { // The choice of `c` parameter (where `1 - c` represents the // probability of a slot being empty), is done in accordance to the // slot duration and expected target block time, for safely // resisting network delays of maximum two seconds. // <https://research.web3.foundation/en/latest/polkadot/BABE/Babe/#6-practical-results> sp_consensus_babe::BabeGenesisConfiguration { slot_duration: Babe::slot_duration(), epoch_length: EpochDuration::get(), c: (1, 1), genesis_authorities: Babe::authorities(), randomness: Babe::randomness(), allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots, } } ``` See the modified source code here: https://github.com/noot/substrate/blob/noot/v0.8-dev-runtime/bin/node/runtime/src/lib.rs To compile the modified runtime, you must have rust installed: ``` rustup update rustup default nightly-2020-10-01 rustup target add wasm32-unknown-unknown cargo build --target wasm32-unknown-unknown --release ``` This places runtime in the `substrate/target/` directory. Note: if there is an error compiling, do `cargo clean` For this workshop, you can download the pre-compiled runtime here: https://github.com/noot/substrate/blob/noot/v0.8-dev-runtime/target/wasm32-unknown-unknown/release/wbuild/node-runtime/node_runtime.compact.wasm ### Creating the genesis file The compiled runtime bytecode of a Polkadot chain is placed into the state trie at the key `:code`. Importing a runtime into gossamer is as simple as placing the wasm bytecode into the genesis state at the `:code` key. Gossamer has a nice sub-command to do this for you: https://chainsafe.github.io/gossamer/usage/import-runtime/ ``` ./bin/gossamer import-runtime <custom-runtime.wasm> > genesis.json ``` eg. ``` ./bin/gossamer import-runtime ~/substrate/target/debug/wbuild/node-runtime/node_runtime.compact.wasm > genesis.json ``` Note: this uses the genesis parameters from chain/gssmr/genesis.json This places the wasm bytecode into the genesis file at `:code` for you. If you open up the genesis file, you will also see other infomration such as the token symbol, authorities, and genesis balances: ```json "properties": { "ss58Format": 17, "tokenDecimals": 12, "tokenSymbol": "GOSS" }, ``` ```json "Babe": { "Authorities": [ [ "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1 ], [ "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 1 ], [ "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 1 ], [ "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", 1 ], [ "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", 1 ], [ "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", 1 ], [ "5DeWA2u2f8Bb1DXBEohUHWXW3NUzKNg75jYSHaEZWKdE6ZuR", 1 ], [ "5GMykc9TjNsmDftompy44gdWtJHnHxZ5wo7h7mJRKdowjz4z", 1 ], [ "5CULg8vw1SMeCsUHcXR6YZP43eMxjdMLqMriZZe5q9HA3V3c", 1 ] ] }, "grandpa": { "authorities": [ [ "5DFNv4Txc4b88qHqQ6GG4D646QcT4fN3jjS2G3r1PyZkfDut", 1 ] ] }, "palletBalances": { "balances": [ [ "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", 1152921504606847000 ], [ "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", 1152921504606847000 ], [ "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", 1152921504606847000 ], [ "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", 1152921504606847000 ], [ "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", 1152921504606847000 ], [ "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", 1152921504606847000 ], [ "5DeWA2u2f8Bb1DXBEohUHWXW3NUzKNg75jYSHaEZWKdE6ZuR", 1152921504606847000 ], [ "5GMykc9TjNsmDftompy44gdWtJHnHxZ5wo7h7mJRKdowjz4z", 1152921504606847000 ], [ "5CULg8vw1SMeCsUHcXR6YZP43eMxjdMLqMriZZe5q9HA3V3c", 1152921504606847000 ] ] }, ``` These accounts correspond to the built-in keys (which are the same as substrate and subkey). The built in keys are named `alice`, `bob`, `charlie`, `dave`, `eve`, `ferdie`, `george`, and `ian`. Tip: if you have [subkey](https://github.com/polkascan/subkey) installed, you can show the keys using `subkey inspect //Alice`: ``` $ subkey inspect //Alice Secret Key URI `//Alice` is account: Secret seed: 0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a Public key (hex): 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d Account ID: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d SS58 Address: 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY ``` ## 2. Initializing and starting the node Let's modify the genesis file to have 1 grandpa authority so that blocks get finalized each round. We can also remove the other BABE authorities, but if we are just running one dev node, it doesn't make a difference. Now that we have the genesis file, we can initialize and start the node as an authority: ``` ./bin/gossamer init --genesis genesis.json ./bin/gossamer --key alice ``` The node is now running and should be producing a block each slot! #### using --chain dev The previous steps have been condensed for you into a `--chain dev` option which uses the genesis previously generated: ``` ./bin/gossamer init --chain dev --force ./bin/gossamer --chain dev ``` The `--chain` option uses the genesis and config files found in the `chain/` directory for the specified chain. Tip: if you wish you use your own config file, you can use `--config path-to-config.toml` ## 3. Interacting with the node ### Using polkadot.js in node.js Tested using node `14.16.1`. Also see documentation here: https://polkadot.js.org/docs/api/examples/promise ``` mkdir gossamer-polkadotjs cd gossamer-polkadotjs npm init -y npm i @polkadot/api@4.6.1 ``` <!-- TODO: try 4.6? --> create a file `transfer.js`: ```js const { ApiPromise, WsProvider, Keyring } = require('@polkadot/api'); async function main() { const wsProvider = new WsProvider('ws://127.0.0.1:8546'); const api = await ApiPromise.create({ provider: wsProvider }); const keyring = new Keyring({ type: 'sr25519' }); const aliceKey = keyring.addFromUri('//Alice'); const addrAlice = aliceKey.publicKey const addrBob = keyring.addFromUri('//Bob').publicKey; let account = await api.query.system.account(addrAlice); console.log(`alice balance before: ${account.data.free}`) account = await api.query.system.account(addrBob); console.log(`bob balance before: ${account.data.free}`) const txHash = await api.tx.balances.transfer(addrBob, 12345) .signAndSend(aliceKey); console.log(`submitted with hash ${txHash}`); const unsub = await api.rpc.chain.subscribeNewHeads(async (lastHeader) => { console.log(`latest block: #${lastHeader.number} `); let {block} = await api.rpc.chain.getBlock(); if (block.extrinsics.length > 1) { console.log(`found extrinsic in block ${lastHeader.number}`) unsub() } account = await api.query.system.account(addrAlice); console.log(`alice balance after: ${account.data.free}`) account = await api.query.system.account(addrBob); console.log(`bob balance after: ${account.data.free}`) }); } main(); ``` Then, do ``` node transfer.js ``` You should see the transfer get submitted, get the tx hash, see what block it was included in, and the balance update: ``` $ node transfer.js balance: 19342813113832646479343115 submitted with hash 0xf75b1aa7417e08f7b1339664030ab688f90bcb0fd33abc69741323566eb527b3 all done latest block: #41 balance: 19342813113832646479343115 latest block: #42 found extrinsic in block 42 balance: 19342813113831226163401053 ``` ### Using polkadot.js apps UI See documentation here: https://chainsafe.github.io/gossamer/integrate/connect-to-polkadot-js/ Note: currently the UI works fully with api v4.6.x; we are hosting our own UI here: http://gossamer-dev.chainsafe.io:3000/#/explorer or http://gossamer-dev1.chainsafe.io:3000/#/explorer ## 4. Running a non-authority node The node we previously started is an **authority**, ie. it authors blocks and participates in finality voting. Let's now see how to run a non-authority node that will sync with our existing node. We need to firstly specify the node is not an authority with `--roles 1`: > roles: > 0: no network > 1: non-authority (full node) > 4: authority node ``` ./bin/gossamer --chain dev --roles 1 --port 7002 --basepath /tmp/gossamer_node --rpc=false --ws=false ``` Can also add `--bootnodes <multiaddr-of-first-node>` for the nodes to directly connect as opposed to discovery over mDNS. The newly launched node should connect to the first node and begin to sync! ## 5. Running a Polkadot/Kusama full node Running a Polkadot/Kusama node is as simple as specifying the chain name with `--chain`: ``` ./bin/gossamer --chain polkadot ``` ``` ./bin/gossamer --chain kusama ``` Once the node is running, you can see it on Telemetry: https://telemetry.polkadot.io/#/Polkadot ## End Thank you for joining the workshop! Feel free to email me elizabeth@chainsafe.io Github if you would like to contribute: github.com/ChainSafe/gossamer