Golang consensus layer implementation and staking contracts for the Story L1 blockchain.
You may find pre-built binaries for our latest stable release in our release page.
Story draws inspiration from ETH PoS in decoupling execution and consensus clients. The execution client relays EVM blocks into the story
consesus client via Engine ABI, using an ABCI++ adapter to make EVM state compatible with that of CometBFT. With this architecture, consensus efficiency is no longer bottlenecked by execution transaction throughput.
Building the story
client requires Go version 1.22 or higher (see go.mod
), which can be installed on almost any OS. Once installed, build the binary by running
go build -o story ./client
Component | Details |
---|---|
CPU | 4 Cores |
RAM | 32 GB |
Disk | 200 GB |
Bandwidth | 10 MBit/s |
The default story
root folder used for configuring consensus network setup can be found in the following paths:
- OS X:
~/Library/Story/story
- Linux:
~/.story/story
- Windows:
%USERPROFILE%\AppData\Roaming\Story\story
Note: This directory is located in the same parent directory as the Story execution client directory root.
To connect to Iliad, initialize story
with the --network iliad
flag, which will generate the genesis file for the Iliad test network:
./story init --network iliad
Afterwards, run the story-geth
execution client in full
sync mode with the iliad
network flag:
./geth --iliad --syncmode full
Now you should be able to sync to the Iliad network with the following:
./story run
For quickly spinning up a local network, we recommend initializing story
with the --network local
setting, which will define the genesis state of a private local network with a single validator:
./story init --network local
Once done, run the execution client with the local
network flag:
./geth --local
Now you can sync to your local network running the following:
./story run
By default, network configurations and data are stored relative to the story
root data folder:
story/
├── config/
│ ├── config.toml # networking & consesus settings
│ ├── genesis.json # blockchain genesis state
│ ├── story.toml # client configs
│ ├── node_key.json # p2p node key [SENSITIVE]
│ ├── priv_validator_key.json # validator key [SENSITIVE]
├── data/
│ ├── snapshots/
│ └── priv_validator_state.json # tracks validator state
To use your own custom root directory, you can pass the --home
flag when initializing and running the node:
./story init --home ${YOUR_DATA_DIR}
- This will initialize
story
data files in the passed in folder - in case files already exist and you wish to overwrite them, you can add the--force
suffix
- This will initialize
./story run --home ${YOUR_DATA_DIR}
- This will spin up the consensus node according to the passed in network and validator configurations
For ensuring synchronization with the execution client, two settings are particularly important:
engine-jwt-endpoint:
This configures the JSON-RPC engine API endpoint, which facilitates commuication between the consensus and execution layer. By default, it is set to http://localhost:8551
, but may be changed by overriding the value in story.toml
or through the --engine-endpoint
CLI flag.
engine-jwt-file:
To authenticate the engine API, a JWT must be passed which is created on execution client initialization. By default, this points to the default execution client JWT file path. For example, if initializing a local network via story init --local
on Linux, this will default to ~/.story/geth/local/geth/jwtsecret
. To override, either change the value in story.toml
or by specifying the --engine-jwt-file
CLI flag.
Operating your own private network involves 4 key components:
- Defining the network genesis state
- Defining the seed nodes
- Running the nodes
- Running validators
Below, we'll go over each section briefly.
By default the consensus genesis state is saved under story/config/genesis.json
. All nodes MUST share this same state file to be able to communicate with one another. For creating a private network, here are the attributes you will want to consider for modification (referenced using JSON key paths):
.chain_id
:- uniquely identifies your blockchain
.app_state.auth.accounts
:- all the staking accounts you wish to provision
.app_state.bank.balances
:- amount of tokens to prefund for the different staking accounts
.app_state.bank.supply
:- total supply of tokens to stake - ensure it's equal to the sum of balances
.app_state.genutil.gentxs
:- the validators that should be created on genesis time - for each validator you should make sure to assign an intuitive
moniker
and correctdelegator_address
,validator_address
, andpubkey.key
- the validators that should be created on genesis time - for each validator you should make sure to assign an intuitive
execution_block_hash
:- the base64-encoded execution genesis blockhash, whose hex-encoded value can be found by attaching to your execution console via
geth attach $GETH_IPC_PATH
and runningeth.getBlock(0).hash
- ALWAYS DOUBLE-CHECK THIS VALUE
- the base64-encoded execution genesis blockhash, whose hex-encoded value can be found by attaching to your execution console via
You will need a set of seed nodes for connecting new nodes to the network. Any node can be a seed node, as long as it is discoverable. By default the story/config/config.toml
file has a set of default seed nodes configured for you to manually override. When running networks locally, these may be set to ${SEED_NODE_ID}@127.0.0.1:${SEED_NODE_PORT}
.
To find your node ID, you can install cometBFT and run show_node_id --home ${STORY_ROOT_DATA_DIR}
. It is important to note that seed nodes themselves require no seed nodes to be configured.
Once your genesis state, network configs, and seed nodes are defined, you can start the network.
First, run your execution client, making sure the path to the engine-API JWT file as well as the genesis state itself is correctly setup on the consesus side. Similar to story
, you may override the default execution client configs by passing a --config
flag to your alternate story-geth
data folder:
./geth --config ${GETH_ROOT_DATA_DIR}
Once done, you should be able to run each of your consensus nodes with the same logic, making sure to pass in the --home
flag if you used an alternate story
data folder:
./story run --home ${STORY_ROOT_DATA_DIR}
Once the network is setup, you will be able to run validator operations using the validator key generated in story/data/priv_validator_key.json
. In fact, story
comes with a validator
subcommand that allows you to streamline this process. For more information on running validators, read our official docs.
Please see our contribution guidelines.
We would like to express our sincere gratitude to the Omni team for their valuable research and contributions to the blockchain space. The Story blockchain's CECS (Consensus Execution Client Separation) architecture was influenced by the Omni's team pioneering work on the EVM staking module using the engine API. Their early efforts showcased the potential of this architecture and served as an inspiration to us. Our initial codebase was, in fact, forked from an early version of their work.
We built the story
consensus client leveraging the Cosmos SDK using CometBFT as the consensus engine. Thanks to all the open source contributors and their wonderful work.
The story
consensus implementation is licensed under the GNU Lesser General Public License v3.0.
To simplify local development and testing, developers can use the Story-Localnet project, which allows multiple Story nodes to run locally using Docker. This setup facilitates easy testing and experimentation.
Find more information and setup instructions here: Story-Localnet GitHub Repository.