Skip to content

Commit

Permalink
Added block skips and forks generation, fixes final height on reboot
Browse files Browse the repository at this point in the history
  • Loading branch information
maoueh committed Jan 9, 2024
1 parent 6c190e8 commit 509f583
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 28 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ To enable firehose instrumentation:
Firehose logger statement will be printed as blocks are executed/produced. This mode is meant to be run
using by a Firehose `reader-node`, see https://github.com/streamingfast/firehose-acme.

### Block Skipping and Forks

The dummy chain skips a block that are divisible by 13 so for example we are at block #25 (abc) the next produced block will be #27 (def) and #26 will never be produced.

THe dummy chain also generates forks at regular interval, every 17 blocks. The generated fork sequence is of 1 block if the current block is odd otherwise if it's even, a 2 blocks fork sequence will be generated.

## Tracer

This project showcase a "fake" blockchain's node codebase. For developers looking into integrating a native Firehose integration, we suggest to integrate in blockchain's client code directly by some form of tracing plugin that is able to receive all the important callback's while transactions are execution integrating as deeply as wanted.
Expand Down
81 changes: 56 additions & 25 deletions core/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ func (e *Engine) StartBlockProduction(ctx context.Context) {
for {
select {
case <-ticker.C:
block := e.createBlock()
e.blockChan <- block

if e.stopHeight > 0 && block.Header.Height >= e.stopHeight {
logrus.Info("reached stop height")
ticker.Stop()
for _, block := range e.createBlocks() {
e.blockChan <- block

if e.stopHeight > 0 && block.Header.Height >= e.stopHeight {
logrus.Info("reached stop height")
ticker.Stop()
return
}
}
case <-ctx.Done():
logrus.Info("stopping block producer")
Expand All @@ -78,52 +80,77 @@ func (e *Engine) Subscription() <-chan *types.Block {
return e.blockChan
}

func (e *Engine) createBlock() *types.Block {
func (e *Engine) createBlocks() (out []*types.Block) {
if e.prevBlock == nil {
logrus.WithField("height", e.genesisHeight).Info("starting from genesis block height")
genesisBlock := types.GenesisBlock(e.genesisHeight)
logrus.WithField("block", blockRef{genesisBlock.Header.Hash, e.genesisHeight}).Info("starting from genesis block height")
e.prevBlock = genesisBlock
e.finalBlock = genesisBlock

return genesisBlock
out = append(out, genesisBlock)
return
}

block := &types.Block{
Header: &types.BlockHeader{
Height: e.prevBlock.Header.Height + 1,
Hash: types.MakeHash(e.prevBlock.Header.Height + 1),
PrevNum: &e.prevBlock.Header.Height,
PrevHash: &e.prevBlock.Header.Hash,
FinalNum: e.finalBlock.Header.Height,
FinalHash: e.finalBlock.Header.Hash,
},
Transactions: []types.Transaction{},
heightToProduce := e.prevBlock.Header.Height + 1
if heightToProduce%13 == 0 {
heightToProduce += 1
logrus.Info(fmt.Sprintf("skipping block #%d that is a multiple of 13, producing %d instead", heightToProduce-1, heightToProduce))
}

trxCount := min(block.Header.Height%10, 500)
if heightToProduce%17 == 0 {
if heightToProduce%2 == 0 {
logrus.Info("producing 2 block fork sequence")
firstFork := e.newBlock(heightToProduce, ptr(uint64(1)), e.prevBlock)
secondFork := e.newBlock(heightToProduce+1, ptr(uint64(2)), firstFork)

out = append(out, firstFork, secondFork)
} else {
logrus.Info("producing 1 block fork sequence")
out = append(out, e.newBlock(heightToProduce, ptr(uint64(1)), e.prevBlock))
}
}

block := e.newBlock(heightToProduce, nil, e.prevBlock)

trxCount := min(heightToProduce%10, 500)
for i := uint64(0); i < trxCount; i++ {
tx := types.Transaction{
Type: "transfer",
Hash: types.MakeHash(fmt.Sprintf("%v-%v", block.Header.Height, i)),
Hash: types.MakeHash(fmt.Sprintf("%v-%v", heightToProduce, i)),
Sender: "0xDEADBEEF",
Receiver: "0xBAAAAAAD",
Amount: big.NewInt(int64(i * 1000000000)),
Fee: big.NewInt(10000),
Success: true,
Events: e.generateEvents(block.Header.Height),
Events: e.generateEvents(heightToProduce),
}

block.Transactions = append(block.Transactions, tx)
}

e.prevBlock = block
out = append(out, block)

e.prevBlock = block
if block.Header.Height%10 == 0 {
logrus.WithField("height", block.Header.Height).Info("produced block is now the final block")
logrus.WithField("block", blockRef{block.Header.Hash, block.Header.Height}).Info("produced block is now the final block")
e.finalBlock = block
}

return block
return
}

func (e *Engine) newBlock(height uint64, nonce *uint64, parent *types.Block) *types.Block {
return &types.Block{
Header: &types.BlockHeader{
Height: height,
Hash: types.MakeHashNonce(height, nonce),
PrevNum: &parent.Header.Height,
PrevHash: &parent.Header.Hash,
FinalNum: e.finalBlock.Header.Height,
FinalHash: e.finalBlock.Header.Hash,
},
Transactions: []types.Transaction{},
}
}

func (e *Engine) generateEvents(height uint64) []types.Event {
Expand Down Expand Up @@ -158,3 +185,7 @@ func (e *Engine) generateEvents(height uint64) []types.Event {

return events
}

func ptr[T any](t T) *T {
return &t
}
38 changes: 36 additions & 2 deletions core/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,9 @@ func (node *Node) Start(ctx context.Context) error {

func (node *Node) processBlock(block *types.Block) error {
logrus.
WithField("height", block.Header.Height).
WithField("hash", block.Header.Hash).
WithField("block", blockRef{block.Header.Hash, block.Header.Height}).
WithField("parent_block", blockRef{valueOr(block.Header.PrevHash, ""), valueOr(block.Header.PrevNum, 0)}).
WithField("final_block", blockRef{block.Header.FinalHash, block.Header.FinalNum}).
Info("processing block")

if err := node.store.WriteBlock(block); err != nil {
Expand All @@ -139,3 +140,36 @@ func (node *Node) processBlock(block *types.Block) error {

return nil
}

func shortHash(in string) string {
return in[:6] + "..." + in[len(in)-6:]
}

func shortHashPtr(in *string) string {
if in == nil {
return "<nil>"
}

return shortHash(*in)
}

func valueOr[T any](t *T, def T) T {
if t == nil {
return def
}

return *t
}

type blockRef struct {
hash string
number uint64
}

func (ref blockRef) String() string {
if ref.hash == "" {
return "<nil>"
}

return fmt.Sprintf("#%d (%s)", ref.number, shortHash(ref.hash))
}
1 change: 1 addition & 0 deletions core/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (store *Store) Initialize() error {

func (store *Store) WriteBlock(block *types.Block) error {
store.meta.HeadHeight = block.Header.Height
store.meta.FinalHeight = block.Header.FinalNum

raw, err := store.encodeBlock(block)
if err != nil {
Expand Down
12 changes: 11 additions & 1 deletion types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,23 @@ package types

import (
"crypto/sha256"
"encoding/binary"
"fmt"
"math/big"
"time"
)

func MakeHash(data interface{}) string {
shaSum := sha256.Sum256([]byte(fmt.Sprintf("%v", data)))
return MakeHashNonce(data, nil)
}

func MakeHashNonce(data interface{}, nonce *uint64) string {
content := []byte(fmt.Sprintf("%v", data))
if nonce != nil {
content = binary.LittleEndian.AppendUint64(content, *nonce)

Check failure on line 18 in types/types.go

View workflow job for this annotation

GitHub Actions / Test (1.18.x, ubuntu-latest)

binary.LittleEndian.AppendUint64 undefined (type binary.littleEndian has no field or method AppendUint64)

Check failure on line 18 in types/types.go

View workflow job for this annotation

GitHub Actions / Build Release (1.18.x, ubuntu-latest)

binary.LittleEndian.AppendUint64 undefined (type binary.littleEndian has no field or method AppendUint64)
}

shaSum := sha256.Sum256(content)
return fmt.Sprintf("%x", shaSum)
}

Expand Down

0 comments on commit 509f583

Please sign in to comment.