diff --git a/dot/core/digest_test.go b/dot/core/digest_test.go index 10a37005768..1ee131eb0a5 100644 --- a/dot/core/digest_test.go +++ b/dot/core/digest_test.go @@ -88,7 +88,7 @@ func TestDigestHandler_BABEScheduledChange(t *testing.T) { headers := addTestBlocksToState(t, 2, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } auths := handler.babe.Authorities() @@ -97,7 +97,7 @@ func TestDigestHandler_BABEScheduledChange(t *testing.T) { // authorities should change on start of block 3 from start headers = addTestBlocksToState(t, 1, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } time.Sleep(time.Millisecond * 100) @@ -222,7 +222,7 @@ func TestDigestHandler_BABEPauseAndResume(t *testing.T) { headers := addTestBlocksToState(t, 3, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } time.Sleep(time.Millisecond * 100) @@ -279,7 +279,7 @@ func TestDigestHandler_GrandpaScheduledChange(t *testing.T) { headers := addTestBlocksToState(t, 2, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } auths := handler.grandpa.Authorities() @@ -288,7 +288,7 @@ func TestDigestHandler_GrandpaScheduledChange(t *testing.T) { // authorities should change on start of block 3 from start headers = addTestBlocksToState(t, 1, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } time.Sleep(time.Millisecond * 100) @@ -413,7 +413,7 @@ func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) { headers := addTestBlocksToState(t, 3, handler.blockState) for _, h := range headers { - handler.blockState.SetFinalizedHash(h.Hash(), 0) + handler.blockState.SetFinalizedHash(h.Hash(), 0, 0) } time.Sleep(time.Millisecond * 100) diff --git a/dot/core/interface.go b/dot/core/interface.go index a253a209e2b..26888323969 100644 --- a/dot/core/interface.go +++ b/dot/core/interface.go @@ -43,9 +43,9 @@ type BlockState interface { GetSlotForBlock(common.Hash) (uint64, error) HighestBlockHash() common.Hash HighestBlockNumber() *big.Int - GetFinalizedHeader(uint64) (*types.Header, error) - GetFinalizedHash(uint64) (common.Hash, error) - SetFinalizedHash(common.Hash, uint64) error + GetFinalizedHeader(uint64, uint64) (*types.Header, error) + GetFinalizedHash(uint64, uint64) (common.Hash, error) + SetFinalizedHash(common.Hash, uint64, uint64) error RegisterImportedChannel(ch chan<- *types.Block) (byte, error) UnregisterImportedChannel(id byte) RegisterFinalizedChannel(ch chan<- *types.Header) (byte, error) diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go index e64ec1cdec8..6d14f057fd9 100644 --- a/dot/rpc/modules/api.go +++ b/dot/rpc/modules/api.go @@ -25,7 +25,7 @@ type BlockAPI interface { BestBlockHash() common.Hash GetBlockByHash(hash common.Hash) (*types.Block, error) GetBlockHash(blockNumber *big.Int) (*common.Hash, error) - GetFinalizedHash(uint64) (common.Hash, error) + GetFinalizedHash(uint64, uint64) (common.Hash, error) RegisterImportedChannel(ch chan<- *types.Block) (byte, error) UnregisterImportedChannel(id byte) } diff --git a/dot/rpc/modules/chain.go b/dot/rpc/modules/chain.go index 7b2b63664e7..2b3cb46058b 100644 --- a/dot/rpc/modules/chain.go +++ b/dot/rpc/modules/chain.go @@ -130,7 +130,7 @@ func (cm *ChainModule) GetHead(r *http.Request, req *ChainBlockNumberRequest, re // GetFinalizedHead returns the most recently finalized block hash func (cm *ChainModule) GetFinalizedHead(r *http.Request, req *EmptyRequest, res *ChainHashResponse) error { - h, err := cm.blockAPI.GetFinalizedHash(0) + h, err := cm.blockAPI.GetFinalizedHash(0, 0) if err != nil { return err } @@ -139,9 +139,11 @@ func (cm *ChainModule) GetFinalizedHead(r *http.Request, req *EmptyRequest, res return nil } -// GetFinalizedHeadByRound returns the hash of the block finalized at the given round -func (cm *ChainModule) GetFinalizedHeadByRound(r *http.Request, req *ChainIntRequest, res *ChainHashResponse) error { - h, err := cm.blockAPI.GetFinalizedHash(uint64(*req)) +// GetFinalizedHeadByRound returns the hash of the block finalized at the given round and setID +func (cm *ChainModule) GetFinalizedHeadByRound(r *http.Request, req *[]ChainIntRequest, res *ChainHashResponse) error { + round := (uint64)((*req)[0]) + setID := (uint64)((*req)[1]) + h, err := cm.blockAPI.GetFinalizedHash(round, setID) if err != nil { return err } diff --git a/dot/rpc/modules/chain_test.go b/dot/rpc/modules/chain_test.go index 539cf9a355c..ab868456f95 100644 --- a/dot/rpc/modules/chain_test.go +++ b/dot/rpc/modules/chain_test.go @@ -233,17 +233,17 @@ func TestChainGetFinalizedHeadByRound(t *testing.T) { svc := NewChainModule(chain.Block) var res ChainHashResponse - req := ChainIntRequest(0) + req := []ChainIntRequest{0, 0} err := svc.GetFinalizedHeadByRound(nil, &req, &res) require.NoError(t, err) expected := genesisHeader.Hash() require.Equal(t, common.BytesToHex(expected[:]), res) testhash := common.Hash{1, 2, 3, 4} - err = chain.Block.SetFinalizedHash(testhash, 77) + err = chain.Block.SetFinalizedHash(testhash, 77, 1) require.NoError(t, err) - req = ChainIntRequest(77) + req = []ChainIntRequest{77, 1} err = svc.GetFinalizedHeadByRound(nil, &req, &res) require.NoError(t, err) require.Equal(t, common.BytesToHex(testhash[:]), res) diff --git a/dot/rpc/websocket_test.go b/dot/rpc/websocket_test.go index eb4a11c977d..33cdda33d35 100644 --- a/dot/rpc/websocket_test.go +++ b/dot/rpc/websocket_test.go @@ -89,7 +89,7 @@ func (m *MockBlockAPI) GetBlockByHash(hash common.Hash) (*types.Block, error) { func (m *MockBlockAPI) GetBlockHash(blockNumber *big.Int) (*common.Hash, error) { return nil, nil } -func (m *MockBlockAPI) GetFinalizedHash(uint64) (common.Hash, error) { +func (m *MockBlockAPI) GetFinalizedHash(uint64, uint64) (common.Hash, error) { return common.Hash{}, nil } func (m *MockBlockAPI) RegisterImportedChannel(ch chan<- *types.Block) (byte, error) { diff --git a/dot/services.go b/dot/services.go index 3f71ab07d15..b583270a3b4 100644 --- a/dot/services.go +++ b/dot/services.go @@ -326,6 +326,7 @@ func createGRANDPAService(cfg *Config, rt *runtime.Runtime, st *state.Service, d LogLvl: lvl, BlockState: st.Block, DigestHandler: dh, + SetID: 1, Voters: voters, Keypair: keys[0].(*ed25519.Keypair), } diff --git a/dot/state/block.go b/dot/state/block.go index 35872ba584b..02ffa9c6091 100644 --- a/dot/state/block.go +++ b/dot/state/block.go @@ -142,7 +142,7 @@ func NewBlockStateFromGenesis(db chaindb.Database, header *types.Header) (*Block bs.genesisHash = header.Hash() // set the latest finalized head to the genesis header - err = bs.SetFinalizedHash(bs.genesisHash, 0) + err = bs.SetFinalizedHash(bs.genesisHash, 0, 0) if err != nil { return nil, err } @@ -194,11 +194,13 @@ func arrivalTimeKey(hash common.Hash) []byte { return append(arrivalTimePrefix, hash.ToBytes()...) } -// finalizedHashKey = hashkey + round (LE encoded) -func finalizedHashKey(round uint64) []byte { +// finalizedHashKey = hashkey + round + setID (LE encoded) +func finalizedHashKey(round, setID uint64) []byte { buf := make([]byte, 8) binary.LittleEndian.PutUint64(buf, round) - return append(common.FinalizedBlockHashKey, buf...) + key := append(common.FinalizedBlockHashKey, buf...) + binary.LittleEndian.PutUint64(buf, setID) + return append(key, buf...) } // GenesisHash returns the hash of the genesis block @@ -397,8 +399,8 @@ func (bs *BlockState) SetBlockBody(hash common.Hash, body *types.Body) error { } // GetFinalizedHeader returns the latest finalized block header -func (bs *BlockState) GetFinalizedHeader(round uint64) (*types.Header, error) { - h, err := bs.GetFinalizedHash(round) +func (bs *BlockState) GetFinalizedHeader(round, setID uint64) (*types.Header, error) { + h, err := bs.GetFinalizedHash(round, setID) if err != nil { return nil, err } @@ -412,7 +414,8 @@ func (bs *BlockState) GetFinalizedHeader(round uint64) (*types.Header, error) { } // GetFinalizedHash gets the latest finalized block header -func (bs *BlockState) GetFinalizedHash(round uint64) (common.Hash, error) { +func (bs *BlockState) GetFinalizedHash(round, setID uint64) (common.Hash, error) { + // get current round r, err := bs.GetRound() if err != nil { return common.Hash{}, err @@ -423,7 +426,7 @@ func (bs *BlockState) GetFinalizedHash(round uint64) (common.Hash, error) { return common.Hash{}, fmt.Errorf("round not yet finalized") } - h, err := bs.db.Get(finalizedHashKey(round)) + h, err := bs.db.Get(finalizedHashKey(round, setID)) if err != nil { return common.Hash{}, err } @@ -432,7 +435,7 @@ func (bs *BlockState) GetFinalizedHash(round uint64) (common.Hash, error) { } // SetFinalizedHash sets the latest finalized block header -func (bs *BlockState) SetFinalizedHash(hash common.Hash, round uint64) error { +func (bs *BlockState) SetFinalizedHash(hash common.Hash, round, setID uint64) error { go bs.notifyFinalized(hash) if round > 0 { err := bs.SetRound(round) @@ -449,7 +452,7 @@ func (bs *BlockState) SetFinalizedHash(hash common.Hash, round uint64) error { } } - return bs.db.Put(finalizedHashKey(round), hash[:]) + return bs.db.Put(finalizedHashKey(round, setID), hash[:]) } // SetRound sets the latest finalized GRANDPA round in the db diff --git a/dot/state/block_notify_test.go b/dot/state/block_notify_test.go index 07682a0a956..ce6f486a126 100644 --- a/dot/state/block_notify_test.go +++ b/dot/state/block_notify_test.go @@ -61,7 +61,7 @@ func TestFinalizedChannel(t *testing.T) { chain, _ := AddBlocksToState(t, bs, 3) for _, b := range chain { - bs.SetFinalizedHash(b.Hash(), 0) + bs.SetFinalizedHash(b.Hash(), 0, 0) } for i := 0; i < 1; i++ { @@ -128,7 +128,7 @@ func TestFinalizedChannel_Multi(t *testing.T) { } chain, _ := AddBlocksToState(t, bs, 1) - bs.SetFinalizedHash(chain[0].Hash(), 0) + bs.SetFinalizedHash(chain[0].Hash(), 0, 0) var wg sync.WaitGroup wg.Add(num) diff --git a/dot/state/block_test.go b/dot/state/block_test.go index cc2b814479d..a88dc4d3380 100644 --- a/dot/state/block_test.go +++ b/dot/state/block_test.go @@ -270,15 +270,15 @@ func TestAddBlock_BlockNumberToHash(t *testing.T) { func TestFinalizedHash(t *testing.T) { bs := newTestBlockState(t, testGenesisHeader) - h, err := bs.GetFinalizedHash(0) + h, err := bs.GetFinalizedHash(0, 0) require.NoError(t, err) require.Equal(t, testGenesisHeader.Hash(), h) testhash := common.Hash{1, 2, 3, 4} - err = bs.SetFinalizedHash(testhash, 1) + err = bs.SetFinalizedHash(testhash, 1, 1) require.NoError(t, err) - h, err = bs.GetFinalizedHash(1) + h, err = bs.GetFinalizedHash(1, 1) require.NoError(t, err) require.Equal(t, testhash, h) } @@ -304,7 +304,7 @@ func TestFinalization_DeleteBlock(t *testing.T) { // pick block to finalize fin := leaves[len(leaves)-1] - err := bs.SetFinalizedHash(fin, 1) + err := bs.SetFinalizedHash(fin, 1, 1) require.NoError(t, err) // assert that every block except finalized has been deleted diff --git a/lib/babe/build.go b/lib/babe/build.go index f6821af5c9b..f51eb589771 100644 --- a/lib/babe/build.go +++ b/lib/babe/build.go @@ -226,7 +226,7 @@ func (b *Service) buildBlockInherents(slot Slot) error { } // add finalnum - fin, err := b.blockState.GetFinalizedHeader(0) + fin, err := b.blockState.GetFinalizedHeader(0, 0) if err != nil { return err } diff --git a/lib/babe/state.go b/lib/babe/state.go index 6f74b3b023b..399622ed15e 100644 --- a/lib/babe/state.go +++ b/lib/babe/state.go @@ -42,7 +42,7 @@ type BlockState interface { GetSlotForBlock(common.Hash) (uint64, error) HighestBlockHash() common.Hash HighestBlockNumber() *big.Int - GetFinalizedHeader(uint64) (*types.Header, error) + GetFinalizedHeader(uint64, uint64) (*types.Header, error) IsDescendantOf(parent, child common.Hash) (bool, error) } diff --git a/lib/grandpa/grandpa.go b/lib/grandpa/grandpa.go index 56f6049f9d4..6450b5de1e9 100644 --- a/lib/grandpa/grandpa.go +++ b/lib/grandpa/grandpa.go @@ -100,7 +100,7 @@ func NewService(cfg *Config) (*Service, error) { logger.Info("creating service", "key", cfg.Keypair.Public().Hex(), "voter set", Voters(cfg.Voters)) // get latest finalized header - head, err := cfg.BlockState.GetFinalizedHeader(0) + head, err := cfg.BlockState.GetFinalizedHeader(0, 0) if err != nil { return nil, err } @@ -436,7 +436,7 @@ func (s *Service) attemptToFinalize() error { } // if we haven't received a finalization message for this block yet, broadcast a finalization message - s.logger.Debug("finalized block!!!", "round", s.state.round, "hash", s.head.Hash()) + s.logger.Debug("finalized block!!!", "setID", s.state.setID, "round", s.state.round, "hash", s.head.Hash()) msg := s.newFinalizationMessage(s.head, s.state.round) // TODO: safety @@ -595,13 +595,13 @@ func (s *Service) finalize() error { } // set finalized head for round in db - err = s.blockState.SetFinalizedHash(bfc.hash, s.state.round) + err = s.blockState.SetFinalizedHash(bfc.hash, s.state.round, s.state.setID) if err != nil { return err } // set latest finalized head in db - return s.blockState.SetFinalizedHash(bfc.hash, 0) + return s.blockState.SetFinalizedHash(bfc.hash, 0, 0) } // derivePrimary returns the primary for the current round diff --git a/lib/grandpa/message.go b/lib/grandpa/message.go index 1a9081f1a19..8a22e0b4046 100644 --- a/lib/grandpa/message.go +++ b/lib/grandpa/message.go @@ -136,10 +136,12 @@ type catchUpResponse struct { } func (s *Service) newCatchUpResponse(round, setID uint64) (*catchUpResponse, error) { - // TODO: update blockState.GetFinalizedHeader to accept setID, use that instead, since mapping only stores from current setID - b := s.bestFinalCandidate[round] + header, err := s.blockState.GetFinalizedHeader(round, setID) + if err != nil { + return nil, err + } - has, err := s.blockState.HasJustification(b.hash) + has, err := s.blockState.HasJustification(header.Hash()) if err != nil { return nil, err } @@ -148,7 +150,7 @@ func (s *Service) newCatchUpResponse(round, setID uint64) (*catchUpResponse, err return nil, ErrNoJustification } - just, err := s.blockState.GetJustification(b.hash) + just, err := s.blockState.GetJustification(header.Hash()) if err != nil { return nil, err } @@ -174,7 +176,7 @@ func (s *Service) newCatchUpResponse(round, setID uint64) (*catchUpResponse, err SetID: setID, PreVoteJustification: pvj, PreCommitJustification: pcj, - Hash: b.hash, - Number: b.number, + Hash: header.Hash(), + Number: header.Number.Uint64(), }, nil } diff --git a/lib/grandpa/message_handler.go b/lib/grandpa/message_handler.go index 21b5cba2ddc..b7f38df25b0 100644 --- a/lib/grandpa/message_handler.go +++ b/lib/grandpa/message_handler.go @@ -46,13 +46,13 @@ func (h *MessageHandler) HandleMessage(msg *ConsensusMessage) error { fm, ok := m.(*FinalizationMessage) if ok { // set finalized head for round in db - err = h.blockState.SetFinalizedHash(fm.Vote.hash, fm.Round) + err = h.blockState.SetFinalizedHash(fm.Vote.hash, fm.Round, h.grandpa.state.setID) if err != nil { return err } // set latest finalized head in db - err = h.blockState.SetFinalizedHash(fm.Vote.hash, 0) + err = h.blockState.SetFinalizedHash(fm.Vote.hash, 0, 0) if err != nil { return err } diff --git a/lib/grandpa/message_handler_test.go b/lib/grandpa/message_handler_test.go index 1444f0ef9c6..594daa4bfca 100644 --- a/lib/grandpa/message_handler_test.go +++ b/lib/grandpa/message_handler_test.go @@ -154,15 +154,15 @@ func TestMessageHandler_FinalizationMessage(t *testing.T) { cm, err := fm.ToConsensusMessage() require.NoError(t, err) - h := NewMessageHandler(nil, st.Block) + h := NewMessageHandler(gs, st.Block) err = h.HandleMessage(cm) require.NoError(t, err) - hash, err := st.Block.GetFinalizedHash(0) + hash, err := st.Block.GetFinalizedHash(0, 0) require.NoError(t, err) require.Equal(t, fm.Vote.hash, hash) - hash, err = st.Block.GetFinalizedHash(fm.Round) + hash, err = st.Block.GetFinalizedHash(fm.Round, gs.state.setID) require.NoError(t, err) require.Equal(t, fm.Vote.hash, hash) } diff --git a/lib/grandpa/message_test.go b/lib/grandpa/message_test.go index 63a46855196..325465614b2 100644 --- a/lib/grandpa/message_test.go +++ b/lib/grandpa/message_test.go @@ -1,8 +1,10 @@ package grandpa import ( + "math/big" "testing" + "github.com/ChainSafe/gossamer/dot/state" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" "github.com/ChainSafe/gossamer/lib/keystore" @@ -131,12 +133,19 @@ func TestNewCatchUpResponse(t *testing.T) { round := uint64(1) setID := uint64(1) + testHeader := &types.Header{ + Number: big.NewInt(1), + } + v := &Vote{ - hash: common.Hash{0x77}, + hash: testHeader.Hash(), number: 1, } - gs.bestFinalCandidate[round] = v + err = gs.blockState.SetFinalizedHash(testHeader.Hash(), round, setID) + require.NoError(t, err) + err = gs.blockState.(*state.BlockState).SetHeader(testHeader) + require.NoError(t, err) pvj := []*Justification{ { diff --git a/lib/grandpa/state.go b/lib/grandpa/state.go index b92477fc523..4324887080e 100644 --- a/lib/grandpa/state.go +++ b/lib/grandpa/state.go @@ -31,8 +31,8 @@ type BlockState interface { GetHeaderByNumber(num *big.Int) (*types.Header, error) IsDescendantOf(parent, child common.Hash) (bool, error) HighestCommonAncestor(a, b common.Hash) (common.Hash, error) - GetFinalizedHeader(uint64) (*types.Header, error) - SetFinalizedHash(common.Hash, uint64) error + GetFinalizedHeader(uint64, uint64) (*types.Header, error) + SetFinalizedHash(common.Hash, uint64, uint64) error BestBlockHeader() (*types.Header, error) BestBlockHash() common.Hash Leaves() []common.Hash diff --git a/tests/utils/chain.go b/tests/utils/chain.go index f6f197161f3..57a3ea98d92 100644 --- a/tests/utils/chain.go +++ b/tests/utils/chain.go @@ -79,9 +79,10 @@ func GetFinalizedHead(t *testing.T, node *Node) common.Hash { } // GetFinalizedHeadByRound calls the endpoint chain_getFinalizedHeadByRound to get the finalized head at a given round +// TODO: add setID, hard-coded at 1 for now func GetFinalizedHeadByRound(t *testing.T, node *Node, round uint64) (common.Hash, error) { p := strconv.Itoa(int(round)) - respBody, err := PostRPC(ChainGetFinalizedHeadByRound, NewEndpoint(node.RPCPort), "["+p+"]") + respBody, err := PostRPC(ChainGetFinalizedHeadByRound, NewEndpoint(node.RPCPort), "["+p+",1]") require.NoError(t, err) var hash string diff --git a/tests/utils/gossamer_utils.go b/tests/utils/gossamer_utils.go index 17906c1ab21..21eb42b156a 100644 --- a/tests/utils/gossamer_utils.go +++ b/tests/utils/gossamer_utils.go @@ -195,23 +195,19 @@ func StartGossamer(t *testing.T, node *Node) error { errWriter := bufio.NewWriter(errfile) go io.Copy(errWriter, stderrPipe) //nolint - logger.Debug("wait few secs for node to come up", "cmd.Process.Pid", node.Process.Process.Pid) var started bool - for i := 0; i < maxRetries; i++ { time.Sleep(time.Second) if err = CheckNodeStarted(t, "http://"+HOSTNAME+":"+node.RPCPort); err == nil { started = true break - } else { - logger.Debug("Waiting for Gossamer to start", "err", err) } } if started { - logger.Debug("Gossamer started", "key", key, "cmd.Process.Pid", node.Process.Process.Pid) + logger.Info("node started", "key", key, "cmd.Process.Pid", node.Process.Process.Pid) } else { - logger.Crit("Gossamer didn't start!", "err", err) + logger.Crit("node didn't start!", "err", err) return err }