diff --git a/core/engine.go b/core/engine.go index bebdb11..393dd60 100644 --- a/core/engine.go +++ b/core/engine.go @@ -12,6 +12,7 @@ import ( type Engine struct { genesisHeight uint64 + genesisTime time.Time genesisBlockBurst uint64 stopHeight uint64 blockRate time.Duration @@ -20,11 +21,12 @@ type Engine struct { finalBlock *types.Block } -func NewEngine(genesisHeight, genesisBlockBurst, stopHeight uint64, rate int) Engine { +func NewEngine(genesisHeight uint64, genesisTime time.Time, genesisBlockBurst uint64, stopHeight uint64, rate int) Engine { blockRate := time.Minute / time.Duration(rate) return Engine{ genesisHeight: genesisHeight, + genesisTime: genesisTime, genesisBlockBurst: genesisBlockBurst, stopHeight: stopHeight, blockRate: blockRate, @@ -80,7 +82,7 @@ func (e *Engine) Subscription() <-chan *types.Block { func (e *Engine) createBlocks() (out []*types.Block) { if e.prevBlock == nil { - genesisBlock := types.GenesisBlock(e.genesisHeight) + genesisBlock := types.GenesisBlock(e.genesisHeight, e.genesisTime) logrus.WithField("block", blockRef{genesisBlock.Header.Hash, e.genesisHeight}).Info("starting from genesis block height") e.prevBlock = genesisBlock e.finalBlock = genesisBlock @@ -151,6 +153,7 @@ func (e *Engine) newBlock(height uint64, nonce *uint64, parent *types.Block) *ty PrevHash: &parent.Header.Hash, FinalNum: e.finalBlock.Header.Height, FinalHash: e.finalBlock.Header.Hash, + Timestamp: e.genesisTime.Add(e.blockRate * time.Duration(height)), }, Transactions: []types.Transaction{}, } diff --git a/core/node.go b/core/node.go index 6b0ca0b..4fff631 100644 --- a/core/node.go +++ b/core/node.go @@ -3,6 +3,7 @@ package core import ( "context" "fmt" + "time" "github.com/sirupsen/logrus" "github.com/streamingfast/dummy-blockchain/tracer" @@ -20,15 +21,16 @@ func NewNode( storeDir string, blockRate int, genesisHeight uint64, + genesisTime time.Time, genesisBlockBurst uint64, stopHeight uint64, serverAddr string, tracer tracer.Tracer, ) *Node { - store := NewStore(storeDir, genesisHeight) + store := NewStore(storeDir, genesisHeight, genesisTime) return &Node{ - engine: NewEngine(genesisHeight, genesisBlockBurst, stopHeight, blockRate), + engine: NewEngine(genesisHeight, genesisTime, genesisBlockBurst, stopHeight, blockRate), store: store, server: NewServer(store, serverAddr), tracer: tracer, @@ -37,7 +39,7 @@ func NewNode( func (node *Node) Initialize() error { logrus. - WithField("genesis_height", node.store.genesisHeight). + WithField("genesis_height", node.store.meta.GenesisHeight). Info("initializing node") logrus.Info("initializing store") @@ -61,7 +63,7 @@ func (node *Node) Initialize() error { final := node.store.meta.FinalHeight if final == 0 { // We are uninitialized, so we need to create a genesis block - final = node.store.genesisHeight + final = node.store.meta.GenesisHeight } logrus.WithField("final", final).Info("loading final block") diff --git a/core/store.go b/core/store.go index c7c3c7e..e3d4589 100644 --- a/core/store.go +++ b/core/store.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "time" "github.com/sirupsen/logrus" "github.com/streamingfast/dummy-blockchain/types" @@ -14,26 +15,33 @@ const ( filesPerDir = 1000 ) +type StoreMeta struct { + GenesisHeight uint64 `json:"genesis_height"` + GenesisTimeNanos int64 `json:"genesis_time_nanos"` + FinalHeight uint64 `json:"final_height"` + HeadHeight uint64 `json:"head_height"` +} + type Store struct { - rootDir string - blocksDir string - metaPath string - currentGroup int - genesisHeight uint64 - - meta struct { - FinalHeight uint64 `json:"final_height"` - HeadHeight uint64 `json:"head_height"` - } + rootDir string + blocksDir string + metaPath string + currentGroup int + + meta StoreMeta } -func NewStore(rootDir string, genesisHeight uint64) *Store { +func NewStore(rootDir string, genesisHeight uint64, genesisTime time.Time) *Store { return &Store{ - rootDir: rootDir, - blocksDir: filepath.Join(rootDir, "blocks"), - metaPath: filepath.Join(rootDir, "meta.json"), - currentGroup: -1, - genesisHeight: genesisHeight, + rootDir: rootDir, + blocksDir: filepath.Join(rootDir, "blocks"), + metaPath: filepath.Join(rootDir, "meta.json"), + currentGroup: -1, + + meta: StoreMeta{ + GenesisHeight: genesisHeight, + GenesisTimeNanos: genesisTime.UnixNano(), + }, } } @@ -89,8 +97,8 @@ func (store *Store) CurrentBlock() (*types.Block, error) { } func (store *Store) ReadBlock(height uint64) (*types.Block, error) { - if height == store.genesisHeight { - return types.GenesisBlock(store.genesisHeight), nil + if height == store.meta.GenesisHeight { + return types.GenesisBlock(store.meta.GenesisHeight, time.Unix(0, store.meta.GenesisTimeNanos)), nil } block := &types.Block{} diff --git a/main.go b/main.go index 03b3135..c959ba5 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,11 @@ package main import ( "context" "errors" + "fmt" "os" "os/signal" "syscall" + "time" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -13,8 +15,9 @@ import ( "github.com/streamingfast/dummy-blockchain/tracer" ) -var cliOpts = struct { +type Flags struct { GenesisHeight uint64 + GenesisTimeRaw string GenesisBlockBurst uint64 LogLevel string StoreDir string @@ -22,7 +25,22 @@ var cliOpts = struct { ServerAddr string Tracer string StopHeight uint64 -}{} +} + +func (f *Flags) GenesisTime() (time.Time, error) { + if f.GenesisTimeRaw == "" { + return time.Now(), nil + } + + genesisTime, err := time.Parse(time.RFC3339, f.GenesisTimeRaw) + if err != nil { + return time.Time{}, fmt.Errorf("parse %q: %w", f.GenesisTimeRaw, err) + } + + return genesisTime, nil +} + +var cliOpts Flags func main() { root := &cobra.Command{ @@ -54,6 +72,7 @@ func initFlags(root *cobra.Command) error { flags := root.PersistentFlags() flags.Uint64Var(&cliOpts.GenesisHeight, "genesis-height", 1, "Blockchain genesis height") + flags.StringVar(&cliOpts.GenesisTimeRaw, "genesis-time", "", "Blockchain genesis time in RFC3339 time format, leave empty for current time") flags.Uint64Var(&cliOpts.GenesisBlockBurst, "genesis-block-burst", 0, "The amount of block to produce when initially starting from genesis block") flags.StringVar(&cliOpts.LogLevel, "log-level", "info", "Logging level") flags.StringVar(&cliOpts.StoreDir, "store-dir", "./data", "Directory for storing blockchain state") @@ -84,12 +103,18 @@ func makeInitCommand() *cobra.Command { Short: "Initialize local blockchain state", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { + genesisTime, err := cliOpts.GenesisTime() + if err != nil { + return fmt.Errorf("get genesis time: %w", err) + } + logrus. WithField("dir", cliOpts.StoreDir). WithField("genesis_height", cliOpts.GenesisHeight). + WithField("genesis_time", genesisTime). Info("initializing chain store") - store := core.NewStore(cliOpts.StoreDir, cliOpts.GenesisHeight) + store := core.NewStore(cliOpts.StoreDir, cliOpts.GenesisHeight, genesisTime) return store.Initialize() }, } @@ -123,6 +148,11 @@ func makeStartComand() *cobra.Command { return errors.New("block rate option must be greater than 1") } + genesisTime, err := cliOpts.GenesisTime() + if err != nil { + return fmt.Errorf("get genesis time: %w", err) + } + var blockTracer tracer.Tracer if cliOpts.Tracer == "firehose" { blockTracer = &tracer.FirehoseTracer{} @@ -132,6 +162,7 @@ func makeStartComand() *cobra.Command { cliOpts.StoreDir, cliOpts.BlockRate, cliOpts.GenesisHeight, + genesisTime, cliOpts.GenesisBlockBurst, cliOpts.StopHeight, cliOpts.ServerAddr, diff --git a/pb/last_generate.txt b/pb/last_generate.txt index b08bb36..720ee53 100644 --- a/pb/last_generate.txt +++ b/pb/last_generate.txt @@ -1,2 +1,2 @@ -generate.sh - Mon Jan 8 15:38:28 EST 2024 - maoueh -firehose-acme/proto revision: 1e24bd4 +generate.sh - Tue Apr 30 16:05:24 EDT 2024 - maoueh +firehose-acme/proto revision: b03c6d4 diff --git a/pb/sf/acme/type/v1/type.pb.go b/pb/sf/acme/type/v1/type.pb.go index 9c7b67b..5a1cd57 100644 --- a/pb/sf/acme/type/v1/type.pb.go +++ b/pb/sf/acme/type/v1/type.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.32.0 -// protoc v4.25.0 +// protoc v4.25.3 // source: sf/acme/type/v1/type.proto package pbacme @@ -31,7 +31,7 @@ type BlockHeader struct { PreviousHash *string `protobuf:"bytes,4,opt,name=previous_hash,json=previousHash,proto3,oneof" json:"previous_hash,omitempty"` FinalNum uint64 `protobuf:"varint,5,opt,name=final_num,json=finalNum,proto3" json:"final_num,omitempty"` FinalHash string `protobuf:"bytes,6,opt,name=final_hash,json=finalHash,proto3" json:"final_hash,omitempty"` - Timestamp uint64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Timestamp int64 `protobuf:"varint,7,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *BlockHeader) Reset() { @@ -108,7 +108,7 @@ func (x *BlockHeader) GetFinalHash() string { return "" } -func (x *BlockHeader) GetTimestamp() uint64 { +func (x *BlockHeader) GetTimestamp() int64 { if x != nil { return x.Timestamp } @@ -449,7 +449,7 @@ var file_sf_acme_type_v1_type_proto_rawDesc = []byte{ 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6e, 0x75, 0x6d, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x22, 0x7f, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, diff --git a/tracer/firehose_tracer.go b/tracer/firehose_tracer.go index 741bb9b..31db26d 100644 --- a/tracer/firehose_tracer.go +++ b/tracer/firehose_tracer.go @@ -1,12 +1,11 @@ package tracer import ( - "encoding/base64" "fmt" + "time" pbacme "github.com/streamingfast/dummy-blockchain/pb/sf/acme/type/v1" "github.com/streamingfast/dummy-blockchain/types" - "google.golang.org/protobuf/proto" ) var _ Tracer = &FirehoseTracer{} @@ -30,30 +29,33 @@ func (t *FirehoseTracer) OnBlockEnd(blk *types.Block, finalBlockHeader *types.Bl header := t.activeBlock.Header - previousNum := uint64(0) - if header.PreviousNum != nil { - previousNum = *header.PreviousNum - } - - previousHash := "" - if header.PreviousHash != nil { - previousHash = *header.PreviousHash - } - - blockPayload, err := proto.Marshal(t.activeBlock) - if err != nil { - panic(fmt.Errorf("unable to marshal block: %w", err)) - } - - fmt.Printf("FIRE BLOCK %d %s %d %s %d %d %s\n", - header.Height, - header.Hash, - previousNum, - previousHash, - header.FinalNum, - header.Timestamp, - base64.StdEncoding.EncodeToString(blockPayload), - ) + // previousNum := uint64(0) + // if header.PreviousNum != nil { + // previousNum = *header.PreviousNum + // } + + // previousHash := "" + // if header.PreviousHash != nil { + // previousHash = *header.PreviousHash + // } + + // blockPayload, err := proto.Marshal(t.activeBlock) + // if err != nil { + // panic(fmt.Errorf("unable to marshal block: %w", err)) + // } + + blockTime := time.Unix(0, int64(header.Timestamp)) + fmt.Println(blockTime) + + // fmt.Printf("FIRE BLOCK %d %s %d %s %d %d %s\n", + // header.Height, + // header.Hash, + // previousNum, + // previousHash, + // header.FinalNum, + // header.Timestamp, + // base64.StdEncoding.EncodeToString(blockPayload), + // ) t.activeBlock = nil t.activeTrx = nil @@ -71,7 +73,7 @@ func (t *FirehoseTracer) OnBlockStart(header *types.BlockHeader) { Hash: header.Hash, FinalNum: header.FinalNum, FinalHash: header.FinalHash, - Timestamp: uint64(header.Timestamp.UnixNano()), + Timestamp: header.Timestamp.UnixNano(), }, } diff --git a/types/types.go b/types/types.go index 06ba086..f5dd8f0 100644 --- a/types/types.go +++ b/types/types.go @@ -22,12 +22,7 @@ func MakeHashNonce(data interface{}, nonce *uint64) string { return fmt.Sprintf("%x", shaSum) } -func GenesisBlock(height uint64) *Block { - genesisTime, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z") - if err != nil { - panic(err) - } - +func GenesisBlock(height uint64, genesisTime time.Time) *Block { header := &BlockHeader{ Height: height, Hash: MakeHash(height),