Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions controller/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,22 @@ func (c *Controller) ProduceProposal(evidence *bft.ByzantineEvidence, vdf *crypt
if e != nil {
return false, e
}
// load the previous quorum height quorum certificate from the indexer
lastCertificate, e := c.FSM.LoadCertificateHashesOnly(c.FSM.Height() - 1)
if e != nil {
return false, e
// The first consensus block has no prior certificate or indexed block.
// Later heights must resolve both predecessors from the indexer.
var lastCertificate *lib.QuorumCertificate
lastBlock := &lib.BlockResult{BlockHeader: new(lib.BlockHeader)}
if c.FSM.Height() > 1 {
lastCertificate, e = c.FSM.LoadCertificateHashesOnly(c.FSM.Height() - 1)
if e != nil {
return false, e
}
lastBlock, e = c.FSM.LoadBlock(c.FSM.Height() - 1)
if e != nil {
return false, e
}
}
// validate the verifiable delay function from the bft module
if vdf != nil {
if c.FSM.Height() > 1 && vdf != nil {
// if the verifiable delay function is NOT valid for using the last block hash
if !crypto.VerifyVDF(lastCertificate.BlockHash, vdf.Output, vdf.Proof, int(vdf.Iterations)) {
// nullify the bad VDF
Expand All @@ -156,11 +165,6 @@ func (c *Controller) ProduceProposal(evidence *bft.ByzantineEvidence, vdf *crypt
c.log.Error(lib.ErrInvalidVDF().Error())
}
}
// load the last block from the indexer
lastBlock, e := c.FSM.LoadBlock(c.FSM.Height() - 1)
if e != nil {
return false, e
}
// replace the VDF and last certificate in the header
p.Block.BlockHeader.LastQuorumCertificate, p.Block.BlockHeader.Vdf = lastCertificate, vdf
p.Block.BlockHeader.TotalVdfIterations = vdf.GetIterations() + lastBlock.BlockHeader.TotalVdfIterations
Expand Down
13 changes: 9 additions & 4 deletions fsm/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,15 @@ func (s *StateMachine) ApplyBlock(ctx context.Context, b *lib.Block, allowOversi
if !rootStartTime.IsZero() {
s.Metrics.UpdateFSMApplyBlockRootTime(rootStartTime)
}
// load the last block from the indexer
lastBlock, err := s.LoadBlock(s.height - 1)
if err != nil {
return nil, nil, err
// The state committed from genesis is version 1, but there is no indexed
// block yet. Treat the predecessor of the first consensus block as an empty
// genesis boundary; later heights must always resolve their prior block.
lastBlock := &lib.BlockResult{BlockHeader: new(lib.BlockHeader)}
if s.height > 1 {
lastBlock, err = s.LoadBlock(s.height - 1)
if err != nil {
return nil, nil, err
}
}
// get the transaction root
transactionRoot, err := r.TransactionRoot()
Expand Down
38 changes: 38 additions & 0 deletions fsm/state_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fsm

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -287,6 +288,43 @@ func TestApplyBlock(t *testing.T) {
}
}

func TestApplyFirstBlockWithoutIndexedPredecessor(t *testing.T) {
log := lib.NewDefaultLogger()
db, err := store.NewStoreInMemory(log)
require.NoError(t, err)
defer db.Close()
sm := StateMachine{
store: db,
ProtocolVersion: CurrentProtocolVersion,
NetworkID: 1,
height: 1,
slashTracker: NewSlashTracker(),
events: new(lib.EventsTracker),
Config: lib.Config{
MainConfig: lib.DefaultMainConfig(),
StateMachineConfig: lib.DefaultStateMachineConfig(),
},
log: log,
cache: &cache{
accounts: make(map[uint64]*Account),
pools: make(map[uint64]*Pool),
},
}
require.NoError(t, sm.SetParams(DefaultParams()))

block := &lib.Block{BlockHeader: &lib.BlockHeader{
Time: uint64(time.Now().UnixMicro()),
ProposerAddress: newTestAddressBytes(t),
}}
header, result, err := sm.ApplyBlock(context.Background(), block, false)
require.NoError(t, err)
require.Empty(t, result.Failed)
require.EqualValues(t, 1, header.Height)
require.Zero(t, header.TotalTxs)
require.Zero(t, header.TotalVdfIterations)
require.Equal(t, bytes.Repeat([]byte("F"), crypto.HashSize), []byte(header.LastBlockHash))
}

func TestApplyTransactions_DoesNotReturnCheckErrors(t *testing.T) {
sm := newTestStateMachine(t)
kg := newTestKeyGroup(t)
Expand Down
Loading