Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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
136 changes: 0 additions & 136 deletions dot/state/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,6 @@ func NewBlockStateFromGenesis(db chaindb.Database, header *types.Header) (*Block
return nil, err
}

err = bs.SetRound(0)
if err != nil {
return nil, err
}

return bs, nil
}

Expand Down Expand Up @@ -168,16 +163,6 @@ func arrivalTimeKey(hash common.Hash) []byte {
return append(arrivalTimePrefix, hash.ToBytes()...)
}

// finalizedHashKey = hashkey + round + setID (LE encoded)
func finalizedHashKey(round, setID uint64) []byte {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, round)
key := append(common.FinalizedBlockHashKey, buf...)
buf2 := make([]byte, 8)
binary.LittleEndian.PutUint64(buf2, setID)
return append(key, buf2...)
}

// GenesisHash returns the hash of the genesis block
func (bs *BlockState) GenesisHash() common.Hash {
return bs.genesisHash
Expand Down Expand Up @@ -373,127 +358,6 @@ func (bs *BlockState) SetBlockBody(hash common.Hash, body *types.Body) error {
return bs.db.Put(blockBodyKey(hash), body.AsOptional().Value())
}

// HasFinalizedBlock returns true if there is a finalised block for a given round and setID, false otherwise
func (bs *BlockState) HasFinalizedBlock(round, setID uint64) (bool, error) {
return bs.db.Has(finalizedHashKey(round, setID))
}

// NumberIsFinalised checks if a block number is finalised or not
func (bs *BlockState) NumberIsFinalised(num *big.Int) (bool, error) {
header, err := bs.GetFinalizedHeader(0, 0)
if err != nil {
return false, err
}

return num.Cmp(header.Number) <= 0, nil
}

// GetFinalizedHeader returns the latest finalised block header
func (bs *BlockState) GetFinalizedHeader(round, setID uint64) (*types.Header, error) {
h, err := bs.GetFinalizedHash(round, setID)
if err != nil {
return nil, err
}

header, err := bs.GetHeader(h)
if err != nil {
return nil, err
}

return header, nil
}

// GetFinalizedHash gets the latest finalised block header
func (bs *BlockState) GetFinalizedHash(round, setID uint64) (common.Hash, error) {
h, err := bs.db.Get(finalizedHashKey(round, setID))
if err != nil {
return common.Hash{}, err
}

return common.NewHash(h), nil
}

// SetFinalizedHash sets the latest finalised block header
func (bs *BlockState) SetFinalizedHash(hash common.Hash, round, setID uint64) error {
bs.Lock()
defer bs.Unlock()

has, _ := bs.HasHeader(hash)
if !has {
return fmt.Errorf("cannot finalise unknown block %s", hash)
}

// if nothing was previously finalised, set the first slot of the network to the
// slot number of block 1, which is now being set as final
if bs.lastFinalised.Equal(bs.genesisHash) && !hash.Equal(bs.genesisHash) {
err := bs.setFirstSlotOnFinalisation()
if err != nil {
return err
}
}

if round > 0 {
go bs.notifyFinalized(hash, round, setID)

err := bs.SetRound(round)
if err != nil {
return err
}
}

pruned := bs.bt.Prune(hash)
for _, rem := range pruned {
header, err := bs.GetHeader(rem)
if err != nil {
return err
}

err = bs.DeleteBlock(rem)
if err != nil {
return err
}

logger.Trace("pruned block", "hash", rem, "number", header.Number)
bs.pruneKeyCh <- header
}

bs.lastFinalised = hash
return bs.db.Put(finalizedHashKey(round, setID), hash[:])
}

func (bs *BlockState) setFirstSlotOnFinalisation() error {
header, err := bs.GetHeaderByNumber(big.NewInt(1))
if err != nil {
return err
}

slot, err := types.GetSlotFromHeader(header)
if err != nil {
return err
}

return bs.baseState.storeFirstSlot(slot)
}

// SetRound sets the latest finalised GRANDPA round in the db
// TODO: this needs to use both setID and round
func (bs *BlockState) SetRound(round uint64) error {
buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, round)
return bs.db.Put(common.LatestFinalizedRoundKey, buf)
}

// GetRound gets the latest finalised GRANDPA round from the db
func (bs *BlockState) GetRound() (uint64, error) {
r, err := bs.db.Get(common.LatestFinalizedRoundKey)
if err != nil {
return 0, err
}

round := binary.LittleEndian.Uint64(r)
return round, nil
}

// CompareAndSetBlockData will compare empty fields and set all elements in a block data to db
func (bs *BlockState) CompareAndSetBlockData(bd *types.BlockData) error {
hasReceipt, _ := bs.HasReceipt(bd.Hash)
Expand Down
9 changes: 0 additions & 9 deletions dot/state/block_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ func (bs *BlockState) HasReceipt(hash common.Hash) (bool, error) {

// SetReceipt sets a Receipt in the database
func (bs *BlockState) SetReceipt(hash common.Hash, data []byte) error {
bs.Lock()
defer bs.Unlock()

err := bs.db.Put(prefixKey(hash, receiptPrefix), data)
if err != nil {
return err
Expand All @@ -60,9 +57,6 @@ func (bs *BlockState) HasMessageQueue(hash common.Hash) (bool, error) {

// SetMessageQueue sets a MessageQueue in the database
func (bs *BlockState) SetMessageQueue(hash common.Hash, data []byte) error {
bs.Lock()
defer bs.Unlock()

err := bs.db.Put(prefixKey(hash, messageQueuePrefix), data)
if err != nil {
return err
Expand All @@ -88,9 +82,6 @@ func (bs *BlockState) HasJustification(hash common.Hash) (bool, error) {

// SetJustification sets a Justification in the database
func (bs *BlockState) SetJustification(hash common.Hash, data []byte) error {
bs.Lock()
defer bs.Unlock()

err := bs.db.Put(prefixKey(hash, justificationPrefix), data)
if err != nil {
return err
Expand Down
112 changes: 112 additions & 0 deletions dot/state/block_finalisation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package state

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: add copyright notice


import (
"fmt"
"math/big"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
)

// finalizedHashKey = FinalizedBlockHashKey + round + setID (LE encoded)
func finalizedHashKey(round, setID uint64) []byte {
return append(common.FinalizedBlockHashKey, roundSetIDKey(round, setID)...)
}

// HasFinalizedBlock returns true if there is a finalised block for a given round and setID, false otherwise
func (bs *BlockState) HasFinalizedBlock(round, setID uint64) (bool, error) {
return bs.db.Has(finalizedHashKey(round, setID))
}

// NumberIsFinalised checks if a block number is finalised or not
func (bs *BlockState) NumberIsFinalised(num *big.Int) (bool, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we finalising on finalize or finalise?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

substrate uses finalized but we changed our linter a while back to british english so now our codebase is finalised....

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we make the code consistent then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you mean? it should use finalised in our codebase

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be an issue created to refactor existing finalized to finalised?

@timwu20 timwu20 Jul 9, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the two functions above use Finalized

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

header, err := bs.GetFinalizedHeader(0, 0)
if err != nil {
return false, err
}

return num.Cmp(header.Number) <= 0, nil
}

// GetFinalizedHeader returns the finalised block header by round and setID
func (bs *BlockState) GetFinalizedHeader(round, setID uint64) (*types.Header, error) {
h, err := bs.GetFinalizedHash(round, setID)
if err != nil {
return nil, err
}

header, err := bs.GetHeader(h)
if err != nil {
return nil, err
}

return header, nil
}

// GetFinalizedHash gets the finalised block header by round and setID
func (bs *BlockState) GetFinalizedHash(round, setID uint64) (common.Hash, error) {
h, err := bs.db.Get(finalizedHashKey(round, setID))
if err != nil {
return common.Hash{}, err
}

return common.NewHash(h), nil
}

// SetFinalizedHash sets the latest finalised block header
// Note that using round=0 and setID=0 would refer to the latest finalised hash
func (bs *BlockState) SetFinalizedHash(hash common.Hash, round, setID uint64) error {
bs.Lock()
defer bs.Unlock()

has, _ := bs.HasHeader(hash)
if !has {
return fmt.Errorf("cannot finalise unknown block %s", hash)
}

// if nothing was previously finalised, set the first slot of the network to the
// slot number of block 1, which is now being set as final
if bs.lastFinalised.Equal(bs.genesisHash) && !hash.Equal(bs.genesisHash) {
err := bs.setFirstSlotOnFinalisation()
if err != nil {
return err
}
}

if round > 0 {
bs.notifyFinalized(hash, round, setID)
}

pruned := bs.bt.Prune(hash)
for _, rem := range pruned {
header, err := bs.GetHeader(rem)
if err != nil {
return err
}

err = bs.DeleteBlock(rem)
if err != nil {
return err
}

logger.Trace("pruned block", "hash", rem, "number", header.Number)
bs.pruneKeyCh <- header

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could block if storageState is lagging behind or not running at all. Do we need a timeout just in case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the storage state just deletes a key from the map on prune, so generally I don't think it would lag. how would you implement a timeout for this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

select {
   case <-bs.pruneKeyCh:
   case <-time.After(5 * time.Second):
}

May be a bit overkill for this case, but that's how I would do it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the code is writing to the channel, and your code is reading from it? is there a way to do a timeout when writing? either way I don't think it's too necessary for this case

}

bs.lastFinalised = hash
return bs.db.Put(finalizedHashKey(round, setID), hash[:])
}

func (bs *BlockState) setFirstSlotOnFinalisation() error {
header, err := bs.GetHeaderByNumber(big.NewInt(1))
if err != nil {
return err
}

slot, err := types.GetSlotFromHeader(header)
if err != nil {
return err
}

return bs.baseState.storeFirstSlot(slot)
}
13 changes: 0 additions & 13 deletions dot/state/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,6 @@ func TestFinalizedHash(t *testing.T) {
require.Equal(t, testhash, h)
}

func TestLatestFinalizedRound(t *testing.T) {
bs := newTestBlockState(t, testGenesisHeader)
r, err := bs.GetRound()
require.NoError(t, err)
require.Equal(t, uint64(0), r)

err = bs.SetRound(99)
require.NoError(t, err)
r, err = bs.GetRound()
require.NoError(t, err)
require.Equal(t, uint64(99), r)
}

func TestFinalization_DeleteBlock(t *testing.T) {
bs := newTestBlockState(t, testGenesisHeader)
AddBlocksToState(t, bs, 5)
Expand Down
Loading