Skip to content

plexsysio/sakhali

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sakhali

Sakhali is a minimal external consensus client for an EVM chain backed by a separate execution-layer process.

The current shape is a simple PoS-flavored qBFT client:

  • a static validator set defines who can propose and vote
  • the proposer schedule is round-robin over that validator set by height, round
  • the proposer assembles an execution payload from Erigon, reconstructs the block locally, and runs qBFT over the payload hash before insertion
  • validator and non-validator nodes both follow the chain by accepting committed blocks and updating forkchoice
  • libp2p gossip pubsub carries protobuf-encoded consensus messages, and a libp2p stream protocol handles protobuf sync catch-up

Erigon internals still provide the execution engine. Sakhali uses the execution-facing operations an external consensus layer needs:

  • AssembleBlock / GetAssembledBlock
  • CurrentHeader
  • InsertBlocks
  • ValidateChain
  • UpdateForkChoice

Components

  • internal/el: separate execution-layer runtime and gRPC server exposing execution.Execution
  • internal/engine: thin execution gRPC client adapter used by the consensus process
  • internal/consensus: validator set, qBFT round state, and the orchestration service
  • internal/builder: payload-to-block reconstruction and timestamp validation
  • internal/store: Pebble-backed durable state
  • internal/p2p: libp2p host + gossipsub transport with mDNS/static peer bootstrap
  • internal/gen/sakhali/v1: protobuf code generated by buf
  • internal/app: process configuration and flag parsing

The responsibilities are intentionally separated:

  • the engine adapter only talks to Erigon
  • the builder only turns an execution payload into a local candidate block and checks that it matches the expected parent/number/timestamp window
  • qBFT reasons about proposer validity, votes, round changes, quorum, and commit decisions
  • the service coordinates block production, durable vote state, sync catch-up, insertion, validation, and forkchoice updates
  • transport signs every qBFT protobuf message explicitly and verifies that signature against the pubsub author on receipt

Current Assumptions

  • Erigon exposes the execution gRPC service on an internal address such as 127.0.0.1:9092
  • validator identities are libp2p peer IDs configured in proposer order
  • validator count satisfies qBFT's 3f+1 sizing, for example 4 or 7
  • block timestamps are treated as millisecond-precision values, so sub-second periods such as 500ms are allowed
  • consensus state is persisted locally in Pebble and reloaded on restart
  • payload validity before consensus is limited to local reconstruction and structural checks before final execution validation after commit

Quick Start

Start the execution-layer node:

go run ./cmd/sakhali-el \
  --data-dir data/execution \
  --genesis infrastructure/deployment/generated/localnet/genesis.json \
  --grpc.addr 127.0.0.1:9092

Then start the consensus node:

go run ./cmd/sakhali \
  --execution.addr 127.0.0.1:9092 \
  --consensus.block-period 500ms \
  --consensus.validators <peer-id-1>,<peer-id-2>,<peer-id-3>,<peer-id-4> \
  --p2p.private-key <hex-encoded-libp2p-private-key>

The EL process boots the execution runtime, serves app-facing JSON-RPC and websocket APIs, and exposes the execution.Execution gRPC service on --grpc.addr. The consensus process waits for that gRPC service to report ready, then boots libp2p and starts proposing or validating blocks depending on whether its peer ID is in the validator set.

Protobuf

Wire and persistence schemas live under proto/. Regenerate Go code with:

buf generate

Deployment

Local deployment assets live under infrastructure/deployment.

The first packaged environment is a Docker-based localnet with:

  • 4 validator nodes
  • 1 non-validator follower
  • one sakhali consensus container and one execution-layer container per node

See infrastructure/deployment/README.md for the generator, Docker image build, and docker compose workflow.

Important Flags

Consensus (cmd/sakhali):

  • --execution.addr: Erigon execution gRPC address
  • --consensus.validators: ordered validator peer IDs used for proposer rotation and vote quorum
  • --consensus.block-period: minimum block spacing used when proposing new payloads, including sub-second values like 500ms
  • --consensus.round-timeout: base timeout before broadcasting a round change
  • --consensus.round-timeout-step: extra timeout added per higher round
  • --consensus.state-dir: Pebble directory for durable consensus state
  • --consensus.sync-batch-size: number of committed blocks sent per streamed sync batch
  • --consensus.fee-recipient: fee recipient for locally assembled payloads
  • --poll-interval: retry interval for gRPC busy responses
  • --ready-poll-interval: retry interval while waiting for Erigon to become ready
  • --validation-timeout: timeout for ValidateChain
  • --forkchoice-timeout: timeout Erigon should spend on a forkchoice request before returning busy
  • --p2p.topic: gossip topic for qBFT messages
  • --p2p.discovery: mdns, static, or none
  • --p2p.listen-addrs: comma-separated libp2p listen multiaddrs
  • --p2p.static-peers: comma-separated static peers to dial on startup
  • --p2p.private-key: hex-encoded private key bytes for a stable peer ID

Execution (cmd/sakhali-el):

  • --data-dir: execution-layer datadir
  • --genesis: path to the genesis JSON file
  • --grpc.addr: gRPC address exposing execution.Execution
  • --http.addr, --http.port, --http.api: public JSON-RPC listener settings
  • --ws, --ws.port: websocket RPC settings
  • --authrpc.addr, --authrpc.port, --authrpc.jwtsecret: authenticated Engine API settings
  • --p2p.listen-addr, --p2p.max-peers, --p2p.max-pending-peers, --p2p.no-discovery: execution-layer devp2p settings

Operational Notes

  • qBFT currently runs as proposal -> prepare -> commit, with timeout-driven round changes
  • proposing is event-driven: the next proposer is scheduled immediately after finalization or round advancement, and only waits for the block timestamp window when necessary
  • the proposal digest is the reconstructed execution payload block hash
  • consensus and sync messages are protobuf, not JSON
  • blocks are inserted only after commit quorum is reached
  • committed payloads are stored locally and served to peers through a dedicated libp2p sync stream
  • restart recovery first replays local committed payloads, then opens a sync stream to peers and applies streamed committed batches until caught up
  • final execution validity still comes from ValidateChain and UpdateForkChoice after insertion
  • future work should add stronger qBFT prepared-certificate handling, stronger identity/authentication, and more robust discovery beyond local mDNS/static peers

About

Custom chain based on qBFT consensus

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages