Parachain 🚀 Launch Cheatsheet

My notes made during Yerba parachain setup. If you’re going to launch your own parachain, this might save your time finding the happy path!

Yerba-specific option values are marked with #yerba:

General Info

Some links to the docs:

In theory, you should be able to launch the parachain following these guides… But, the devil is in nuances, so keep attention to the special tricks mentioned below.

Running Locally

  1. Start relaychain following this guide.

  2. Start & connect parachain following this guide.

    1. Keep in mind that when you run locally, you have to connect your polkadotjs/apps to different RPC ports for para- and relay- chains.

    2. Register it: first make ParaThread, then promote it to parachain by firing sudo/parathreadUpgrade extrinsic.

      (There is also a way of doing this w\o the “Parathread” step: see docs)

      It should emit paras.PvfCheckStarted, then paras.PvfCheckAccepted event.

      See polkadot_runtime_common::paras_registrar docs for more details.

    3. NOTE that only the account which claimed ParaId (“para owner”) would be able to call for ParaThread registration afterwards!

    4. NOTE also that in addition to the reserved balance for the ParaId, the account claiming ParaThread will have to lock an additional storage deposit for uploading parachain runtime code and genesis state onto the relay chain.

      Which on Rococo was as much as +32 ROC more deposit (in addition to 40 ROC reserved for the Paraid itself), taken for our ~1Mb runtime+genesis state files.

    5. Following the guide linked above, you should end up running your collator producing your parachain blocks.

  3. As a sanity test, send some funds from relaychain account to parachain account.

    Use pallet_xcm::pallet::dispatchables::limited_reserve_transfer_assets extrinsic.

    Hint 1: the XCM message to be sent (pseudocode):

       let dest = MultiLocation {parents: 0, interior: X1 { Parachain(2000) }};
       let beneficiary = MultiLocation {parents: 0, interior: X1 { AccountID32('5FHneW46xGXgs5mUivwyyyyeU4sbTyGBzmstUspZC92UhjJM694ty') }};
       let asset = MultiAsset {id: Concrete(MultiLocation {parents: 0, interior: Here}), fun: Fungible(100500u128)};

    Hint 2: if you get FailedToTransactAsset error, start the node with -lxcm=trace flag, then it would give you more info to the logs, like e.g.

       2023-09-19 12:00:24.030 TRACE tokio-runtime-worker xcm::execute_xcm_in_credit: Execution errored at 1: FailedToTransactAsset("Account cannot exist with the
       funds that would be given") (original_origin: MultiLocation { parents: 0, interior: X1(AccountId32 { network: Some(Rococo), id: [142, 175, 4, 21, 22, 135, 115, 99, 38, 201, 254,
       161, 126, 37, 252, 82, 135, 97, 54, 147, 201, 18, 144, 156, 178, 38, 170, 71, 148, 242, 106, 72] }) })

    Very likely this means that you are trying to send between differently named tokens. (At least in my case it was because parachain spec had other token name than the relay chain.) You can tweak your relay chain token by modifying plain chain spec (copy-paste needed parts from real life rococo spec) and converting it to raw chain spec, as described in the docs.


         ./target/release/node-template build-spec --chain chain-spec-plain.json --raw > chain-spec.json

Go Live!

  1. Build .json spec file,

    Important! you need to build runtime wasm with --profile production, as it runs needed optimizations on the wasm code.

    Meaning run this command to build your parachain node binary:

       cd yerba
       cargo build --profile production

    Generate .json spec file for your chain:

       ./target/production/yerba-parachain build-spec --chain yerba-rococo-genesis > yerba-rococo.json

    Then generate runtime wasm and genesis state files to be uploaded on-chain (when claiming ParaThread):

       ./target/production/yerba-parachain export-genesis-state para-genesis-state
       ./target/production/yerba-parachain export-genesis-wasm para.wasm
  2. Keep in mind validator reference hardware requirements.

  3. #yerba: You might want to get the relay chain DB snapshot first, to save time on syncing your node. See docs on Chain DB snapshots for that.

  4. Check that your node will get the right PeerId when using the private key provided to it.

    You can generate your node key in advance (e.g. with subkey utility), or just let your node do it for you. Latter is easier, just don’t forget to backup securely the generated private key somewhere from the keystore (it stores it to $BASE_PATH/chains/yerba-rococo/keystore/), e.g. for using on a backup node if any.

    If you decide to give the node an existing key you generated earlier, below are some tips you may want to know.

    To get a PeerId for your node’s key:

       yerba-parachain key inspect-node-key -h

    NOTE: Keep in mind that the key is used in two ways: for Aura protocol participation, and for libp2p communications. And there are two locations in the keystore you should add your keys to.

  5. For Aura, you need to insert the collator key to the node’s keystore as follows:

       /yerba-parachain key insert -d chain_data --key-type aura --scheme sr25519

    It will prompt you to enter the secret seed, which in case of hex string should be prepended with 0x suffix, otherwise it raises an error.

  6. For libp2p, put your key into $BASE_PATH/chains/yerba-rococo/network/secret_ed25519.

    NOTE: make sure the file with your hex-encoded key string does not have EOL added to it.

    (You can also start your node with --node-key-file flag, but it’s probably better to do so only when initially debugging your node setup.)

  7. Finally, run your collator:

       yerba-parachain --collator --base-path chain_data -- --chain rococo
  8. Set your node running as a systemd service.

  9. If you feel brave enough, consider adding your node to the bootnodes list of the chain spec.

    For that you need to know the node’s PeerId. To get PeerId of your node, run:

       subkey inspect-node-key --file secret.key

    You can also do it with your node’s binary. The node generates this key if not provided to it explicitly by the command (more on that above). Also see docs.


  1. Node’s libp2p key wrong => PeerId differs from the one specified in the chain spec for the boot node.

    You face this problem when you see this in the logs:

       [Parachain] 💔 The bootnode you want to connect to at `/ip4/` provided a different peer
       ID `12D3KooWKNCFQDDbKq8yVAnroXWZFYUPPj16pFpwgxvBca1XBCkn` than the one you expect `12D3KooWA7XewwJnj4MezAERDgQ2cg5VVNASbMuPWtuMvB3sBq4P`

    Solution: add the following flag to the command --node-key-file <file with key> to easy play with different keys. When found out the solution, persist the key into keystore as described above.

  2. If you get this error:

       Error: Service(Network(Key decoding error: failed to parse Ed25519 secret key))

    Make sure there is no EOL after the hex string of the private key in the file specified. One way of doing so is

       vim -b <file>
       :set noeol
  3. If your collator node just don’t participate in the block production, without any errors being reported to the logs.

    In this case it’s most likely means that your aura key stored in the node’s keystore does not match the one specified in the chain spec. Double-check you insert the key into the keystore with --key-type aura --scheme sr25519. Re-check that the inserted seed is the right one for the collator public key being set in the chain spec.

  4. If you get the following error:

       [Parachain] Could not find the header of the genesis block in the database!

    This means that you’re running your node with the genesis state different from the one relay chain expects for your parachain (see this issue for details).

    Query the latter one from the relay chain via RPC (chainstate/paras/heads(ParaId) method), and then diff it with the one your node produces via export-genesis-state subcommand.