# 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