diff --git a/dot/core/interface.go b/dot/core/interface.go index abacdb0f20..e6c2d43759 100644 --- a/dot/core/interface.go +++ b/dot/core/interface.go @@ -39,8 +39,6 @@ type BlockState interface { GetBlockByHash(common.Hash) (*types.Block, error) GenesisHash() common.Hash GetSlotForBlock(common.Hash) (uint64, error) - HighestBlockHash() common.Hash - HighestBlockNumber() *big.Int GetFinalizedHeader(uint64, uint64) (*types.Header, error) GetFinalizedHash(uint64, uint64) (common.Hash, error) SetFinalizedHash(common.Hash, uint64, uint64) error diff --git a/dot/core/test_helpers.go b/dot/core/test_helpers.go index efc879389d..3da5758d64 100644 --- a/dot/core/test_helpers.go +++ b/dot/core/test_helpers.go @@ -253,8 +253,8 @@ func (s *mockSyncer) HandleBlockAnnounce(msg *network.BlockAnnounceMessage) erro return nil } -func (s *mockSyncer) ProcessBlockData(_ []*types.BlockData) error { - return nil +func (s *mockSyncer) ProcessBlockData(_ []*types.BlockData) (int, error) { + return 0, nil } func (s *mockSyncer) IsSynced() bool { diff --git a/dot/network/connmgr_test.go b/dot/network/connmgr_test.go index fa1f11653e..e6112735fb 100644 --- a/dot/network/connmgr_test.go +++ b/dot/network/connmgr_test.go @@ -52,6 +52,9 @@ func TestMaxPeers(t *testing.T) { } err = n.host.connect(*ainfo) + if err != nil { + err = n.host.connect(*ainfo) + } require.NoError(t, err, i) } diff --git a/dot/network/state.go b/dot/network/state.go index 98d389dbe7..2077ada3b3 100644 --- a/dot/network/state.go +++ b/dot/network/state.go @@ -29,6 +29,7 @@ type BlockState interface { BestBlockNumber() (*big.Int, error) GenesisHash() common.Hash HasBlockBody(common.Hash) (bool, error) + GetFinalizedHeader(round, setID uint64) (*types.Header, error) } // Syncer is implemented by the syncing service @@ -37,7 +38,7 @@ type Syncer interface { CreateBlockResponse(*BlockRequestMessage) (*BlockResponseMessage, error) // ProcessBlockData is called to process BlockData received in a BlockResponseMessage - ProcessBlockData(data []*types.BlockData) error + ProcessBlockData(data []*types.BlockData) (int, error) // HandleBlockAnnounce is called upon receipt of a BlockAnnounceMessage to process it. // If a request needs to be sent to the peer to retrieve the full block, this function will return it. diff --git a/dot/network/state_test.go b/dot/network/state_test.go index c7678211df..abc41d7a62 100644 --- a/dot/network/state_test.go +++ b/dot/network/state_test.go @@ -71,3 +71,7 @@ func (mbs *MockBlockState) BestBlockNumber() (*big.Int, error) { func (mbs *MockBlockState) HasBlockBody(common.Hash) (bool, error) { return false, nil } + +func (mbs *MockBlockState) GetFinalizedHeader(_, _ uint64) (*types.Header, error) { + return mbs.BestBlockHeader() +} diff --git a/dot/network/sync.go b/dot/network/sync.go index 0f837e9ee9..af0b02c3ab 100644 --- a/dot/network/sync.go +++ b/dot/network/sync.go @@ -148,6 +148,7 @@ func newSyncQueue(s *Service) *syncQueue { func (q *syncQueue) start() { go q.handleResponseQueue() + go q.syncAtHead() go q.processBlockRequests() go q.processBlockResponses() @@ -156,6 +157,49 @@ func (q *syncQueue) start() { go q.prunePeers() } +func (q *syncQueue) syncAtHead() { + prev, err := q.s.blockState.BestBlockHeader() + if err != nil { + logger.Error("failed to get best block header", "error", err) + return + } + + for { + select { + // sleep for average block time TODO: make this configurable from slot duration + case <-time.After(time.Second * 6): + case <-q.ctx.Done(): + return + } + + curr, err := q.s.blockState.BestBlockHeader() + if err != nil { + continue + } + + // we aren't at the head yet, sleep + if curr.Number.Int64() < q.goal { + prev = curr + continue + } + + // we have received new blocks since the last check, sleep + if prev.Number.Int64() < curr.Number.Int64() { + prev = curr + continue + } + + prev = curr + start := uint64(curr.Number.Int64()) + 1 + logger.Debug("haven't received new blocks since last check, pushing request", "start", start) + q.requestData.Store(start, requestData{ + sent: true, + received: false, + }) + q.pushRequest(start, 1, "") + } +} + func (q *syncQueue) handleResponseQueue() { for { select { @@ -173,7 +217,7 @@ func (q *syncQueue) handleResponseQueue() { if len(q.responses) == 0 { q.responseLock.Unlock() - if len(q.requestCh) == 0 { + if len(q.requestCh) == 0 && head.Int64() < q.goal { q.pushRequest(uint64(head.Int64()+1), blockRequestBufferSize, "") } continue @@ -255,6 +299,13 @@ func (q *syncQueue) benchmark() { } if before.Number.Int64() >= q.goal { + finalized, err := q.s.blockState.GetFinalizedHeader(0, 0) //nolint + if err != nil { + continue + } + + logger.Info("💤 node waiting", "head", before.Number, "finalized", finalized.Number) + time.Sleep(time.Second * 5) continue } @@ -272,6 +323,10 @@ func (q *syncQueue) benchmark() { "hashes", fmt.Sprintf("[%s ... %s]", before.Hash(), after.Hash()), ) + if q.goal-before.Number.Int64() < int64(blockRequestSize) { + continue + } + logger.Info("🚣 currently syncing", "goal", q.goal, "average blocks/second", q.benchmarker.mostRecentAverage(), @@ -320,11 +375,51 @@ func (q *syncQueue) updatePeerScore(pid peer.ID, amt int) { } func (q *syncQueue) pushRequest(start uint64, numRequests int, to peer.ID) { + best, err := q.s.blockState.BestBlockNumber() + if err != nil { + logger.Debug("failed to get best block number", "error", err) + return + } + + if q.goal < best.Int64() { + q.goal = best.Int64() + } + + if q.goal-int64(start) < int64(blockRequestSize) { + start := best.Int64() + 1 + req := createBlockRequest(start, 0) + + if d, has := q.requestData.Load(start); has { + data := d.(requestData) + // we haven't sent the request out yet, or we've already gotten the response + if !data.sent || data.sent && data.received { + logger.Debug("ignoring request, already received data", "start", start) + return + } + } + + logger.Debug("pushing request to queue", "start", start) + + q.requestData.Store(start, requestData{ + received: false, + }) + + q.requestCh <- &syncRequest{ + req: req, + to: to, + } + return + } + // all requests must start at a multiple of 128 + 1 m := start % uint64(blockRequestSize) start = start - m + 1 for i := 0; i < numRequests; i++ { + if start > uint64(q.goal) { + return + } + req := createBlockRequest(int64(start), blockRequestSize) if d, has := q.requestData.Load(start); has { @@ -352,12 +447,7 @@ func (q *syncQueue) pushRequest(start uint64, numRequests int, to peer.ID) { func (q *syncQueue) pushResponse(resp *BlockResponseMessage, pid peer.ID) error { if len(resp.BlockData) == 0 { - return fmt.Errorf("block data length is zero") - } - - head, err := q.s.blockState.BestBlockNumber() - if err != nil { - return fmt.Errorf("failed to get best block number: %w", err) + return fmt.Errorf("response data is empty") } start, end, err := resp.getStartAndEnd() @@ -375,13 +465,6 @@ func (q *syncQueue) pushResponse(resp *BlockResponseMessage, pid peer.ID) error // update peer's score q.updatePeerScore(pid, 1) - - if end < head.Int64() { - logger.Debug("throwing away BlockResponseMessage as it's below our head", "head", head, "response end", end) - q.requestData.Delete(uint64(start)) - return nil - } - q.requestData.Store(uint64(start), requestData{ sent: true, received: true, @@ -392,7 +475,7 @@ func (q *syncQueue) pushResponse(resp *BlockResponseMessage, pid peer.ID) error defer q.responseLock.Unlock() for _, bd := range resp.BlockData { - if bd.Number() == nil || bd.Number().Int64() < head.Int64() { + if bd.Number() == nil { continue } @@ -412,6 +495,11 @@ func (q *syncQueue) processBlockRequests() { continue } + if !req.req.StartingBlock.IsUint64() { + q.trySync(req) + continue + } + if d, has := q.requestData.Load(req.req.StartingBlock.Uint64()); has { data := d.(requestData) if data.sent && data.received { @@ -431,7 +519,7 @@ func (q *syncQueue) trySync(req *syncRequest) { return } - logger.Debug("beginning to send out request", "start", req.req.StartingBlock.Uint64()) + logger.Debug("beginning to send out request", "start", req.req.StartingBlock.Value()) if len(req.to) != 0 { resp, err := q.syncWithPeer(req.to, req.req) if err == nil { @@ -463,17 +551,19 @@ func (q *syncQueue) trySync(req *syncRequest) { err = q.pushResponse(resp, peer.pid) if err != nil { - logger.Debug("failed to push block response", "error", err) + logger.Trace("failed to push block response", "error", err) } else { return } } logger.Debug("failed to sync with any peer :(") - q.requestData.Store(req.req.StartingBlock.Uint64(), requestData{ - sent: true, - received: false, - }) + if req.req.StartingBlock.IsUint64() { + q.requestData.Store(req.req.StartingBlock.Uint64(), requestData{ + sent: true, + received: false, + }) + } req.to = "" q.requestCh <- req @@ -517,62 +607,83 @@ func (q *syncQueue) processBlockResponses() { for { select { case data := <-q.responseCh: - bestNum, err := q.s.blockState.BestBlockNumber() - if err != nil { - panic(err) - } + q.handleBlockData(data) + case <-q.ctx.Done(): + return + } + } +} + +func (q *syncQueue) handleBlockData(data []*types.BlockData) { + bestNum, err := q.s.blockState.BestBlockNumber() + if err != nil { + panic(err) // TODO: don't panic but try again. seems blockState needs better concurrency handling + } - end := data[len(data)-1].Number().Int64() + end := data[len(data)-1].Number().Int64() + if end <= bestNum.Int64() { + logger.Debug("ignoring block data that is below our head", "got", end, "head", bestNum.Int64()) + q.pushRequest(uint64(end+1), blockRequestBufferSize, "") + return + } - if end <= bestNum.Int64() { - logger.Debug("ignoring block data that is below our head", "got", end, "head", bestNum.Int64()) - q.pushRequest(uint64(end+1), blockRequestBufferSize, "") - q.currStart = 0 - q.currEnd = 0 - continue - } + defer func() { + q.currStart = 0 + q.currEnd = 0 + }() - q.currStart = data[0].Number().Int64() - q.currEnd = end + q.currStart = data[0].Number().Int64() + q.currEnd = end - logger.Debug("sending block data to syncer", "start", q.currStart, "end", q.currEnd) + logger.Debug("sending block data to syncer", "start", q.currStart, "end", q.currEnd) - err = q.s.syncer.ProcessBlockData(data) - if err != nil { - logger.Warn("failed to handle block data", "start", q.currStart, "end", q.currEnd, "error", err) - q.requestData.Store(uint64(q.currStart), requestData{ - sent: true, - received: false, - }) - q.pushRequest(uint64(q.currStart), 1, "") - q.currStart = 0 - q.currEnd = 0 - continue - } + idx, err := q.s.syncer.ProcessBlockData(data) + if err != nil { + q.handleBlockDataFailure(idx, err, data) + return + } - logger.Debug("finished processing block data") - m := q.currStart % int64(blockRequestSize) - start := q.currStart - m + 1 + logger.Debug("finished processing block data", "start", q.currStart, "end", q.currEnd) - var from peer.ID + var from peer.ID + d, ok := q.requestData.Load(uint64(q.currStart)) + if !ok { + // this shouldn't happen + logger.Debug("can't find request data for response!", "start", q.currStart) + } else { + from = d.(requestData).from + q.updatePeerScore(from, 2) + q.requestData.Delete(uint64(q.currStart)) + } - d, ok := q.requestData.Load(uint64(start)) - if !ok { - // this shouldn't happen - logger.Error("can't find request data for response!", "start", start) - } else { - from = d.(requestData).from - q.updatePeerScore(from, 2) - q.requestData.Delete(uint64(start)) - } + q.pushRequest(uint64(q.currEnd+1), blockRequestBufferSize, from) +} - q.pushRequest(uint64(q.currEnd+1), blockRequestBufferSize, from) - q.currStart = 0 - q.currEnd = 0 - case <-q.ctx.Done(): +func (q *syncQueue) handleBlockDataFailure(idx int, err error, data []*types.BlockData) { + logger.Warn("failed to handle block data", "failed on block", q.currStart+int64(idx), "error", err) + + if err.Error() == "failed to get parent hash: Key not found" { // TODO: unwrap err + header, err := types.NewHeaderFromOptional(data[idx].Header) + if err != nil { + logger.Debug("failed to get header from BlockData", "idx", idx, "error", err) return } + + parentHash := header.ParentHash + req := createBlockRequestWithHash(parentHash, 0) + + logger.Debug("pushing request for parent block", "parent", parentHash) + q.requestCh <- &syncRequest{ + req: req, + } + return } + + q.requestData.Store(uint64(q.currStart), requestData{ + sent: true, + received: false, + }) + q.pushRequest(uint64(q.currStart), 1, "") } // handleBlockAnnounceHandshake handles a block that a peer claims to have through a HandleBlockAnnounceHandshake @@ -608,6 +719,7 @@ func (q *syncQueue) handleBlockAnnounce(msg *BlockAnnounceMessage, from peer.ID) return } + logger.Debug("received BlockAnnounce!", "number", msg.Number, "hash", header.Hash(), "from", from) has, _ := q.s.blockState.HasBlockBody(header.Hash()) if has { return @@ -628,6 +740,13 @@ func (q *syncQueue) handleBlockAnnounce(msg *BlockAnnounceMessage, from peer.ID) } func createBlockRequest(startInt int64, size uint32) *BlockRequestMessage { + var max *optional.Uint32 + if size != 0 { + max = optional.NewUint32(true, size) + } else { + max = optional.NewUint32(false, 0) + } + start, _ := variadic.NewUint64OrHash(uint64(startInt)) blockRequest := &BlockRequestMessage{ @@ -635,7 +754,28 @@ func createBlockRequest(startInt int64, size uint32) *BlockRequestMessage { StartingBlock: start, EndBlockHash: optional.NewHash(false, common.Hash{}), Direction: 0, // ascending - Max: optional.NewUint32(true, size), + Max: max, + } + + return blockRequest +} + +func createBlockRequestWithHash(startHash common.Hash, size uint32) *BlockRequestMessage { + var max *optional.Uint32 + if size != 0 { + max = optional.NewUint32(true, size) + } else { + max = optional.NewUint32(false, 0) + } + + start, _ := variadic.NewUint64OrHash(startHash) + + blockRequest := &BlockRequestMessage{ + RequestedData: RequestedDataHeader + RequestedDataBody + RequestedDataJustification, + StartingBlock: start, + EndBlockHash: optional.NewHash(false, common.Hash{}), + Direction: 0, // ascending + Max: max, } return blockRequest @@ -647,6 +787,10 @@ func sortRequests(reqs []*syncRequest) []*syncRequest { } sort.Slice(reqs, func(i, j int) bool { + if !reqs[i].req.StartingBlock.IsUint64() || !reqs[j].req.StartingBlock.IsUint64() { + return false + } + return reqs[i].req.StartingBlock.Uint64() < reqs[j].req.StartingBlock.Uint64() }) @@ -656,6 +800,11 @@ func sortRequests(reqs []*syncRequest) []*syncRequest { return reqs } + if !reqs[i].req.StartingBlock.IsUint64() || !reqs[i+1].req.StartingBlock.IsUint64() { + i++ + continue + } + if reqs[i].req.StartingBlock.Uint64() == reqs[i+1].req.StartingBlock.Uint64() && reflect.DeepEqual(reqs[i].req.Max, reqs[i+1].req.Max) { reqs = append(reqs[:i], reqs[i+1:]...) } diff --git a/dot/network/sync_test.go b/dot/network/sync_test.go index d94e6857f0..c65d700952 100644 --- a/dot/network/sync_test.go +++ b/dot/network/sync_test.go @@ -18,6 +18,7 @@ package network import ( "context" + "fmt" "math/big" "math/rand" "testing" @@ -241,7 +242,7 @@ func TestSyncQueue_HandleBlockAnnounceHandshake(t *testing.T) { q.stop() time.Sleep(time.Second) - testNum := int64(99) + testNum := int64(128 * 7) testPeerID := peer.ID("noot") q.handleBlockAnnounceHandshake(uint32(testNum), testPeerID) @@ -357,6 +358,7 @@ func TestSyncQueue_handleResponseQueue_noRequestsOrResponses(t *testing.T) { q := newTestSyncQueue(t) q.stop() time.Sleep(time.Second) + q.goal = int64(blockRequestSize) * 10 q.ctx = context.Background() go q.handleResponseQueue() time.Sleep(time.Second * 2) @@ -367,6 +369,7 @@ func TestSyncQueue_handleResponseQueue_responseQueueAhead(t *testing.T) { q := newTestSyncQueue(t) q.stop() time.Sleep(time.Second) + q.goal = int64(blockRequestSize) * 10 q.ctx = context.Background() testHeader0 := types.Header{ @@ -392,6 +395,7 @@ func TestSyncQueue_processBlockResponses(t *testing.T) { q := newTestSyncQueue(t) q.stop() time.Sleep(time.Second) + q.goal = int64(blockRequestSize) * 10 q.ctx = context.Background() testHeader0 := types.Header{ @@ -415,3 +419,92 @@ func TestSyncQueue_processBlockResponses(t *testing.T) { time.Sleep(time.Second) require.Equal(t, blockRequestBufferSize, len(q.requestCh)) } + +func TestSyncQueue_SyncAtHead(t *testing.T) { + q := newTestSyncQueue(t) + q.stop() + time.Sleep(time.Second) + q.ctx = context.Background() + + go q.syncAtHead() + time.Sleep(time.Millisecond * 6100) + select { + case req := <-q.requestCh: + require.Equal(t, uint64(2), req.req.StartingBlock.Uint64()) + case <-time.After(TestMessageTimeout): + t.Fatal("did not queue request") + } +} + +func TestSyncQueue_PushRequest_NearHead(t *testing.T) { + q := newTestSyncQueue(t) + q.stop() + time.Sleep(time.Second) + q.ctx = context.Background() + q.goal = 0 + + q.pushRequest(1, 1, "") + select { + case req := <-q.requestCh: + require.Equal(t, uint64(2), req.req.StartingBlock.Uint64()) + case <-time.After(TestMessageTimeout): + t.Fatal("did not queue request") + } +} + +func TestSyncQueue_handleBlockData_ok(t *testing.T) { + q := newTestSyncQueue(t) + q.stop() + time.Sleep(time.Second) + q.ctx = context.Background() + q.currStart = 129 + q.goal = 1000 + + data := testBlockResponseMessage().BlockData + q.handleBlockData(data) + select { + case req := <-q.requestCh: + require.True(t, req.req.StartingBlock.IsUint64()) + require.Equal(t, uint64(129), req.req.StartingBlock.Uint64()) + case <-time.After(TestMessageTimeout): + t.Fatal("did not queue request") + } + + require.Equal(t, int64(0), q.currStart) + require.Equal(t, int64(0), q.currEnd) +} + +func TestSyncQueue_handleBlockDataFailure(t *testing.T) { + q := newTestSyncQueue(t) + q.stop() + time.Sleep(time.Second) + q.ctx = context.Background() + q.currStart = 129 + q.goal = 1000 + + data := testBlockResponseMessage().BlockData + q.handleBlockDataFailure(0, fmt.Errorf("some other error"), data) + select { + case req := <-q.requestCh: + require.True(t, req.req.StartingBlock.IsUint64()) + require.Equal(t, uint64(q.currStart), req.req.StartingBlock.Uint64()) + case <-time.After(TestMessageTimeout): + t.Fatal("did not queue request") + } +} + +func TestSyncQueue_handleBlockDataFailure_MissingParent(t *testing.T) { + q := newTestSyncQueue(t) + q.stop() + time.Sleep(time.Second) + q.ctx = context.Background() + + data := testBlockResponseMessage().BlockData + q.handleBlockDataFailure(0, fmt.Errorf("failed to get parent hash: Key not found"), data) + select { + case req := <-q.requestCh: + require.True(t, req.req.StartingBlock.IsHash()) + case <-time.After(TestMessageTimeout): + t.Fatal("did not queue request") + } +} diff --git a/dot/network/test_helpers.go b/dot/network/test_helpers.go index 7b6ab2c9d7..17df3c7a10 100644 --- a/dot/network/test_helpers.go +++ b/dot/network/test_helpers.go @@ -57,8 +57,8 @@ func (s *mockSyncer) HandleBlockAnnounce(msg *BlockAnnounceMessage) error { return nil } -func (s *mockSyncer) ProcessBlockData(data []*types.BlockData) error { - return nil +func (s *mockSyncer) ProcessBlockData(data []*types.BlockData) (int, error) { + return 0, nil } func (s *mockSyncer) IsSynced() bool { @@ -147,7 +147,7 @@ func testBlockRequestMessageDecoder(in []byte, _ peer.ID) (Message, error) { } var testBlockAnnounceMessage = &BlockAnnounceMessage{ - Number: big.NewInt(99), + Number: big.NewInt(128 * 7), } func testBlockAnnounceMessageDecoder(in []byte, _ peer.ID) (Message, error) { diff --git a/dot/rpc/modules/system_test.go b/dot/rpc/modules/system_test.go index edceeff94a..a9d6c207af 100644 --- a/dot/rpc/modules/system_test.go +++ b/dot/rpc/modules/system_test.go @@ -47,8 +47,8 @@ func (s *mockSyncer) CreateBlockResponse(msg *network.BlockRequestMessage) (*net return nil, nil } -func (s *mockSyncer) ProcessBlockData(_ []*types.BlockData) error { - return nil +func (s *mockSyncer) ProcessBlockData(_ []*types.BlockData) (int, error) { + return 0, nil } func (s *mockSyncer) HandleBlockAnnounce(msg *network.BlockAnnounceMessage) error { @@ -59,6 +59,10 @@ func (s *mockSyncer) HandleBlockAnnounceHandshake(num *big.Int) []*network.Block return nil } +func (s *mockSyncer) IsSynced() bool { + return false +} + type mockBlockState struct{} func (s *mockBlockState) BestBlockHeader() (*types.Header, error) { @@ -77,8 +81,8 @@ func (s *mockBlockState) HasBlockBody(_ common.Hash) (bool, error) { return false, nil } -func (s *mockSyncer) IsSynced() bool { - return false +func (s *mockBlockState) GetFinalizedHeader(_, _ uint64) (*types.Header, error) { + return s.BestBlockHeader() } type mockTransactionHandler struct{} @@ -348,6 +352,15 @@ func setupSystemModule(t *testing.T) *SystemModule { err = chain.Storage.StoreTrie(ts) require.NoError(t, err) + err = chain.Block.AddBlock(&types.Block{ + Header: &types.Header{ + Number: big.NewInt(1), + ParentHash: chain.Block.BestBlockHash(), + StateRoot: ts.MustRoot(), + }, + Body: &types.Body{}, + }) + require.NoError(t, err) core := newCoreService(t, chain) // TODO (ed) add transactions to txQueue and add test for those diff --git a/dot/state/block.go b/dot/state/block.go index b618f03a6e..7c2f33acd1 100644 --- a/dot/state/block.go +++ b/dot/state/block.go @@ -38,12 +38,11 @@ const pruneKeyBufferSize = 1000 // BlockState defines fields for manipulating the state of blocks, such as BlockTree, BlockDB and Header type BlockState struct { - bt *blocktree.BlockTree - baseDB chaindb.Database - db chaindb.Database - lock sync.RWMutex - genesisHash common.Hash - highestBlockHeader *types.Header + bt *blocktree.BlockTree + baseDB chaindb.Database + db chaindb.Database + lock sync.RWMutex + genesisHash common.Hash // block notifiers imported map[byte]chan<- *types.Block @@ -71,17 +70,10 @@ func NewBlockState(db chaindb.Database, bt *blocktree.BlockTree) (*BlockState, e genesisBlock, err := bs.GetBlockByNumber(big.NewInt(0)) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get genesis header: %w", err) } bs.genesisHash = genesisBlock.Header.Hash() - - // set the current highest block - bs.highestBlockHeader, err = bs.BestBlockHeader() - if err != nil { - return nil, err - } - return bs, nil } @@ -333,13 +325,6 @@ func (bs *BlockState) SetHeader(header *types.Header) error { hash := header.Hash() - // if this is the highest block we've seen, save it - if bs.highestBlockHeader == nil { - bs.highestBlockHeader = header - } else if bs.highestBlockHeader.Number.Cmp(header.Number) == -1 { - bs.highestBlockHeader = header - } - // Write the encoded header bh, err := header.Encode() if err != nil { @@ -512,7 +497,7 @@ func (bs *BlockState) AddBlockWithArrivalTime(block *types.Block, arrivalTime ti } // add block to blocktree - err = bs.bt.AddBlock(block, uint64(arrivalTime.UnixNano())) + err = bs.bt.AddBlock(block.Header, uint64(arrivalTime.UnixNano())) if err != nil { return err } @@ -550,6 +535,16 @@ func (bs *BlockState) AddBlockWithArrivalTime(block *types.Block, arrivalTime ti return bs.baseDB.Flush() } +// AddBlockToBlockTree adds the given block to the blocktree. It does not write it to the database. +func (bs *BlockState) AddBlockToBlockTree(header *types.Header) error { + arrivalTime, err := bs.GetArrivalTime(header.Hash()) + if err != nil { + arrivalTime = time.Now() + } + + return bs.bt.AddBlock(header, uint64(arrivalTime.UnixNano())) +} + // GetAllBlocksAtDepth returns all hashes with the depth of the given hash plus one func (bs *BlockState) GetAllBlocksAtDepth(hash common.Hash) []common.Hash { return bs.bt.GetAllBlocksAtDepth(hash) @@ -578,20 +573,6 @@ func (bs *BlockState) isBlockOnCurrentChain(header *types.Header) (bool, error) return true, nil } -// HighestBlockHash returns the hash of the block with the highest number we have received -// This block may not necessarily be in the blocktree. -// TODO: can probably remove this once BlockResponses are implemented -func (bs *BlockState) HighestBlockHash() common.Hash { - return bs.highestBlockHeader.Hash() -} - -// HighestBlockNumber returns the largest block number we have seen -// This block may not necessarily be in the blocktree. -// TODO: can probably remove this once BlockResponses are implemented -func (bs *BlockState) HighestBlockNumber() *big.Int { - return bs.highestBlockHeader.Number -} - // BestBlockHash returns the hash of the head of the current chain func (bs *BlockState) BestBlockHash() common.Hash { if bs.bt == nil { diff --git a/dot/state/storage.go b/dot/state/storage.go index 0074f120d5..01ffc37821 100644 --- a/dot/state/storage.go +++ b/dot/state/storage.go @@ -126,7 +126,7 @@ func (s *StorageState) TrieState(root *common.Hash) (*rtstorage.TrieState, error t := s.tries[*root] s.lock.RUnlock() - if t != nil { + if t != nil && t.MustHash() == *root { // TODO: figure out why it seems like snapshotted tries are getting modified return rtstorage.NewTrieState(t) } diff --git a/dot/sync/interface.go b/dot/sync/interface.go index 47fb0df0b8..2500d0df5d 100644 --- a/dot/sync/interface.go +++ b/dot/sync/interface.go @@ -44,6 +44,7 @@ type BlockState interface { GetJustification(common.Hash) ([]byte, error) SetJustification(hash common.Hash, data []byte) error SetFinalizedHash(hash common.Hash, round, setID uint64) error + AddBlockToBlockTree(header *types.Header) error } // StorageState is the interface for the storage state diff --git a/dot/sync/message.go b/dot/sync/message.go index 24359f8ba3..bdce09c29c 100644 --- a/dot/sync/message.go +++ b/dot/sync/message.go @@ -67,7 +67,7 @@ func (s *Service) CreateBlockResponse(blockRequest *network.BlockRequestMessage) return nil, err } - s.logger.Debug("BlockRequestMessage", "start", startHeader.Number, "end", endHeader.Number, "startHash", startHash, "endHash", endHash) + logger.Debug("handling BlockRequestMessage", "start", startHeader.Number, "end", endHeader.Number, "startHash", startHash, "endHash", endHash) // get sub-chain of block hashes subchain, err := s.blockState.SubChain(startHash, endHash) @@ -79,7 +79,7 @@ func (s *Service) CreateBlockResponse(blockRequest *network.BlockRequestMessage) subchain = subchain[:maxResponseSize] } - s.logger.Trace("subchain", "start", subchain[0], "end", subchain[len(subchain)-1]) + logger.Trace("subchain", "start", subchain[0], "end", subchain[len(subchain)-1]) responseData := []*types.BlockData{} @@ -139,7 +139,7 @@ func (s *Service) CreateBlockResponse(blockRequest *network.BlockRequestMessage) responseData = append(responseData, blockData) } - s.logger.Debug("sending BlockResponseMessage", "start", startHeader.Number, "end", endHeader.Number) + logger.Debug("sending BlockResponseMessage", "start", startHeader.Number, "end", endHeader.Number) return &network.BlockResponseMessage{ BlockData: responseData, }, nil diff --git a/dot/sync/syncer.go b/dot/sync/syncer.go index a9b4d4df1e..fb92a91b37 100644 --- a/dot/sync/syncer.go +++ b/dot/sync/syncer.go @@ -33,9 +33,10 @@ import ( log "github.com/ChainSafe/log15" ) +var logger = log.New("pkg", "sync") + // Service deals with chain syncing by sending block request messages and watching for responses. type Service struct { - logger log.Logger codeHash common.Hash // cached hash of runtime code // State interfaces @@ -90,7 +91,6 @@ func NewService(cfg *Config) (*Service, error) { cfg.BlockProducer = newMockBlockProducer() } - logger := log.New("pkg", "sync") handler := log.StreamHandler(os.Stdout, log.TerminalFormat()) handler = log.CallerFileHandler(handler) logger.SetHandler(log.LvlFilterHandler(cfg.LogLvl, handler)) @@ -101,7 +101,6 @@ func NewService(cfg *Config) (*Service, error) { } return &Service{ - logger: logger, codeHash: codeHash, blockState: cfg.BlockState, storageState: cfg.StorageState, @@ -119,7 +118,7 @@ func NewService(cfg *Config) (*Service, error) { // announce messages (block announce messages include the header but the full // block is required to execute `core_execute_block`). func (s *Service) HandleBlockAnnounce(msg *network.BlockAnnounceMessage) error { - s.logger.Debug("received BlockAnnounceMessage") + logger.Debug("received BlockAnnounceMessage") // create header from message header, err := types.NewHeader( @@ -145,7 +144,7 @@ func (s *Service) HandleBlockAnnounce(msg *network.BlockAnnounceMessage) error { if err != nil { return err } - s.logger.Debug( + logger.Debug( "saved block header to block state", "number", header.Number, "hash", header.Hash(), @@ -155,32 +154,39 @@ func (s *Service) HandleBlockAnnounce(msg *network.BlockAnnounceMessage) error { return nil } -// ProcessBlockData processes the BlockData from a BlockResponse and returns the index of the last BlockData it successfully handled. -func (s *Service) ProcessBlockData(data []*types.BlockData) error { +// ProcessBlockData processes the BlockData from a BlockResponse and returns the index of the last BlockData it handled on success, +// or the index of the block data that errored on failure. +func (s *Service) ProcessBlockData(data []*types.BlockData) (int, error) { if len(data) == 0 { - return ErrNilBlockData - } - - bestNum, err := s.blockState.BestBlockNumber() - if err != nil { - return fmt.Errorf("failed to get best block number: %w", err) + return 0, ErrNilBlockData } - // TODO: return number of last successful block that was processed - for _, bd := range data { - s.logger.Debug("starting processing of block", "hash", bd.Hash) + for i, bd := range data { + logger.Debug("starting processing of block", "hash", bd.Hash) err := s.blockState.CompareAndSetBlockData(bd) if err != nil { - return fmt.Errorf("failed to compare and set data: %w", err) + return i, fmt.Errorf("failed to compare and set data: %w", err) } hasHeader, _ := s.blockState.HasHeader(bd.Hash) hasBody, _ := s.blockState.HasBlockBody(bd.Hash) - if (hasHeader && hasBody) || (bd.Number() != nil && bd.Number().Int64() <= bestNum.Int64()) { - // TODO: the block should be re-added to the blocktree, since it might not have been stored - // properly due to abnormal shutdown - s.logger.Debug("skipping block, already have", "hash", bd.Hash) + if hasHeader && hasBody { + // TODO: fix this; sometimes when the node shuts down the "best block" isn't stored properly, + // so when the node restarts it has blocks higher than what it thinks is the best, causing it not to sync + logger.Debug("skipping block, already have", "hash", bd.Hash) + + header, err := s.blockState.GetHeader(bd.Hash) //nolint + if err != nil { + logger.Debug("failed to get header", "hash", bd.Hash, "error", err) + return i, err + } + + err = s.blockState.AddBlockToBlockTree(header) + if err != nil { + logger.Debug("failed to add block to blocktree", "hash", bd.Hash, "error", err) + } + continue } @@ -189,44 +195,44 @@ func (s *Service) ProcessBlockData(data []*types.BlockData) error { if bd.Header.Exists() && !hasHeader { header, err = types.NewHeaderFromOptional(bd.Header) if err != nil { - return err + return i, err } - s.logger.Trace("processing header", "hash", header.Hash(), "number", header.Number) + logger.Trace("processing header", "hash", header.Hash(), "number", header.Number) err = s.handleHeader(header) if err != nil { - return err + return i, err } - s.logger.Trace("header processed", "hash", bd.Hash) + logger.Trace("header processed", "hash", bd.Hash) } if bd.Body.Exists() && !hasBody { body, err := types.NewBodyFromOptional(bd.Body) //nolint if err != nil { - return err + return i, err } - s.logger.Trace("processing body", "hash", bd.Hash) + logger.Trace("processing body", "hash", bd.Hash) err = s.handleBody(body) if err != nil { - return err + return i, err } - s.logger.Trace("body processed", "hash", bd.Hash) + logger.Trace("body processed", "hash", bd.Hash) } if bd.Header.Exists() && bd.Body.Exists() { header, err = types.NewHeaderFromOptional(bd.Header) if err != nil { - return err + return i, err } body, err := types.NewBodyFromOptional(bd.Body) if err != nil { - return err + return i, err } block := &types.Block{ @@ -234,24 +240,24 @@ func (s *Service) ProcessBlockData(data []*types.BlockData) error { Body: body, } - s.logger.Debug("processing block", "hash", bd.Hash) + logger.Debug("processing block", "hash", bd.Hash) err = s.handleBlock(block) if err != nil { - s.logger.Error("failed to handle block", "number", block.Header.Number, "error", err) - return err + logger.Error("failed to handle block", "number", block.Header.Number, "error", err) + return i, err } - s.logger.Debug("block processed", "hash", bd.Hash) + logger.Debug("block processed", "hash", bd.Hash) } if bd.Justification != nil && bd.Justification.Exists() { - s.logger.Debug("handling Justification...", "number", bd.Number(), "hash", bd.Hash) + logger.Debug("handling Justification...", "number", bd.Number(), "hash", bd.Hash) s.handleJustification(header, bd.Justification.Value()) } } - return nil + return len(data) - 1, nil } // handleHeader handles headers included in BlockResponses @@ -269,7 +275,7 @@ func (s *Service) handleHeader(header *types.Header) error { func (s *Service) handleBody(body *types.Body) error { exts, err := body.AsExtrinsics() if err != nil { - s.logger.Error("cannot parse body as extrinsics", "error", err) + logger.Error("cannot parse body as extrinsics", "error", err) return err } @@ -291,7 +297,7 @@ func (s *Service) handleBlock(block *types.Block) error { return fmt.Errorf("failed to get parent hash: %w", err) } - s.logger.Trace("getting parent state", "root", parent.StateRoot) + logger.Trace("getting parent state", "root", parent.StateRoot) ts, err := s.storageState.TrieState(&parent.StateRoot) if err != nil { return err @@ -304,7 +310,7 @@ func (s *Service) handleBlock(block *types.Block) error { } s.runtime.SetContextStorage(ts) - s.logger.Trace("going to execute block", "header", block.Header, "exts", block.Body) + logger.Trace("going to execute block", "header", block.Header, "exts", block.Body) _, err = s.runtime.ExecuteBlock(block) if err != nil { @@ -315,7 +321,7 @@ func (s *Service) handleBlock(block *types.Block) error { if err != nil { return err } - s.logger.Trace("stored resulting state", "state root", ts.MustRoot()) + logger.Trace("stored resulting state", "state root", ts.MustRoot()) // TODO: batch writes in AddBlock err = s.blockState.AddBlock(block) @@ -328,7 +334,7 @@ func (s *Service) handleBlock(block *types.Block) error { return err } } else { - s.logger.Debug("🔗 imported block", "number", block.Header.Number, "hash", block.Header.Hash()) + logger.Debug("🔗 imported block", "number", block.Header.Number, "hash", block.Header.Hash()) } // handle consensus digest for authority changes @@ -346,17 +352,17 @@ func (s *Service) handleJustification(header *types.Header, justification []byte err := s.blockState.SetFinalizedHash(header.Hash(), 0, 0) if err != nil { - s.logger.Error("failed to set finalized hash", "error", err) + logger.Error("failed to set finalized hash", "error", err) return } err = s.blockState.SetJustification(header.Hash(), justification) if err != nil { - s.logger.Error("failed tostore justification", "error", err) + logger.Error("failed tostore justification", "error", err) return } - s.logger.Info("🔨 finalized block", "number", header.Number, "hash", header.Hash()) + logger.Info("🔨 finalized block", "number", header.Number, "hash", header.Hash()) } func (s *Service) handleRuntimeChanges(newState *rtstorage.TrieState) error { @@ -369,7 +375,7 @@ func (s *Service) handleRuntimeChanges(newState *rtstorage.TrieState) error { return nil } - s.logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(), "previous code hash", s.codeHash, "new code hash", currCodeHash) + logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(), "previous code hash", s.codeHash, "new code hash", currCodeHash) code := newState.LoadCode() if len(code) == 0 { return ErrEmptyRuntimeCode @@ -377,7 +383,7 @@ func (s *Service) handleRuntimeChanges(newState *rtstorage.TrieState) error { err = s.runtime.UpdateRuntimeCode(code) if err != nil { - s.logger.Crit("failed to update runtime code", "error", err) + logger.Crit("failed to update runtime code", "error", err) return err } @@ -390,13 +396,13 @@ func (s *Service) handleDigests(header *types.Header) { if d.Type() == types.ConsensusDigestType { cd, ok := d.(*types.ConsensusDigest) if !ok { - s.logger.Error("handleDigests", "index", i, "error", "cannot cast invalid consensus digest item") + logger.Error("handleDigests", "index", i, "error", "cannot cast invalid consensus digest item") continue } err := s.digestHandler.HandleConsensusDigest(cd, header) if err != nil { - s.logger.Error("handleDigests", "index", i, "digest", cd, "error", err) + logger.Error("handleDigests", "index", i, "digest", cd, "error", err) } } } diff --git a/dot/sync/syncer_test.go b/dot/sync/syncer_test.go index c641da3a6b..6beea25edf 100644 --- a/dot/sync/syncer_test.go +++ b/dot/sync/syncer_test.go @@ -138,12 +138,13 @@ func TestHandleBlockResponse(t *testing.T) { resp, err := responder.CreateBlockResponse(req) require.NoError(t, err) - err = syncer.ProcessBlockData(resp.BlockData) + _, err = syncer.ProcessBlockData(resp.BlockData) require.NoError(t, err) resp2, err := responder.CreateBlockResponse(req) require.NoError(t, err) - syncer.ProcessBlockData(resp2.BlockData) + _, err = syncer.ProcessBlockData(resp2.BlockData) + require.NoError(t, err) // response should contain blocks 13 to 20, and we should be synced require.True(t, syncer.synced) } @@ -189,7 +190,7 @@ func TestHandleBlockResponse_MissingBlocks(t *testing.T) { // request should start from block 5 (best block number + 1) syncer.synced = false - err = syncer.ProcessBlockData(resp.BlockData) + _, err = syncer.ProcessBlockData(resp.BlockData) require.True(t, errors.Is(err, chaindb.ErrKeyNotFound)) } @@ -216,7 +217,7 @@ func TestRemoveIncludedExtrinsics(t *testing.T) { BlockData: []*types.BlockData{bd}, } - err = syncer.ProcessBlockData(msg.BlockData) + _, err = syncer.ProcessBlockData(msg.BlockData) require.NoError(t, err) inQueue := syncer.transactionState.(*state.TransactionState).Pop() @@ -225,7 +226,7 @@ func TestRemoveIncludedExtrinsics(t *testing.T) { func TestHandleBlockResponse_NoBlockData(t *testing.T) { syncer := newTestSyncer(t) - err := syncer.ProcessBlockData(nil) + _, err := syncer.ProcessBlockData(nil) require.Equal(t, ErrNilBlockData, err) } @@ -248,7 +249,7 @@ func TestHandleBlockResponse_BlockData(t *testing.T) { BlockData: bd, } - err = syncer.ProcessBlockData(msg.BlockData) + _, err = syncer.ProcessBlockData(msg.BlockData) require.Nil(t, err) } diff --git a/lib/babe/state.go b/lib/babe/state.go index 13c7e36264..c34b4cb13f 100644 --- a/lib/babe/state.go +++ b/lib/babe/state.go @@ -41,8 +41,6 @@ type BlockState interface { GetArrivalTime(common.Hash) (time.Time, error) GenesisHash() common.Hash GetSlotForBlock(common.Hash) (uint64, error) - HighestBlockHash() common.Hash - HighestBlockNumber() *big.Int GetFinalizedHeader(uint64, uint64) (*types.Header, error) IsDescendantOf(parent, child common.Hash) (bool, error) } diff --git a/lib/blocktree/blocktree.go b/lib/blocktree/blocktree.go index 5a19c98715..6e9547da35 100644 --- a/lib/blocktree/blocktree.go +++ b/lib/blocktree/blocktree.go @@ -76,17 +76,17 @@ func (bt *BlockTree) GenesisHash() Hash { // AddBlock inserts the block as child of its parent node // Note: Assumes block has no children -func (bt *BlockTree) AddBlock(block *types.Block, arrivalTime uint64) error { +func (bt *BlockTree) AddBlock(header *types.Header, arrivalTime uint64) error { bt.Lock() defer bt.Unlock() - parent := bt.getNode(block.Header.ParentHash) + parent := bt.getNode(header.ParentHash) if parent == nil { return ErrParentNotFound } // Check if it already exists - n := bt.getNode(block.Header.Hash()) + n := bt.getNode(header.Hash()) if n != nil { return ErrBlockExists } @@ -95,7 +95,7 @@ func (bt *BlockTree) AddBlock(block *types.Block, arrivalTime uint64) error { depth.Add(parent.depth, big.NewInt(1)) n = &node{ - hash: block.Header.Hash(), + hash: header.Hash(), parent: parent, children: []*node{}, depth: depth, @@ -230,7 +230,7 @@ func (bt *BlockTree) longestPath() []*node { //nolint } // subChain returns the path from the node with Hash start to the node with Hash end -func (bt *BlockTree) subChain(start Hash, end Hash) ([]*node, error) { +func (bt *BlockTree) subChain(start, end Hash) ([]*node, error) { sn := bt.getNode(start) if sn == nil { return nil, ErrStartNodeNotFound @@ -243,7 +243,7 @@ func (bt *BlockTree) subChain(start Hash, end Hash) ([]*node, error) { } // SubBlockchain returns the path from the node with Hash start to the node with Hash end -func (bt *BlockTree) SubBlockchain(start Hash, end Hash) ([]Hash, error) { +func (bt *BlockTree) SubBlockchain(start, end Hash) ([]Hash, error) { bt.RLock() defer bt.RUnlock() diff --git a/lib/blocktree/blocktree_test.go b/lib/blocktree/blocktree_test.go index 3a1468d6cc..a65551049e 100644 --- a/lib/blocktree/blocktree_test.go +++ b/lib/blocktree/blocktree_test.go @@ -78,18 +78,15 @@ func createFlatTree(t *testing.T, depth int) (*BlockTree, []common.Hash) { hashes := []common.Hash{bt.head.hash} for i := 1; i <= depth; i++ { - block := &types.Block{ - Header: &types.Header{ - ParentHash: previousHash, - Number: big.NewInt(int64(i)), - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: previousHash, + Number: big.NewInt(int64(i)), } - hash := block.Header.Hash() + hash := header.Hash() hashes = append(hashes, hash) - err := bt.AddBlock(block, 0) + err := bt.AddBlock(header, 0) require.Nil(t, err) previousHash = hash } @@ -132,16 +129,13 @@ func TestBlockTree_GetBlock(t *testing.T) { func TestBlockTree_AddBlock(t *testing.T) { bt, hashes := createFlatTree(t, 1) - block := &types.Block{ - Header: &types.Header{ - ParentHash: hashes[1], - Number: big.NewInt(1), - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: hashes[1], + Number: big.NewInt(1), } - hash := block.Header.Hash() - err := bt.AddBlock(block, 0) + hash := header.Hash() + err := bt.AddBlock(header, 0) require.Nil(t, err) node := bt.getNode(hash) @@ -177,16 +171,13 @@ func TestBlockTree_LongestPath(t *testing.T) { bt, hashes := createFlatTree(t, 3) // Insert a block to create a competing path - extraBlock := &types.Block{ - Header: &types.Header{ - ParentHash: hashes[0], - Number: big.NewInt(1), - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: hashes[0], + Number: big.NewInt(1), } - extraBlock.Header.Hash() - err := bt.AddBlock(extraBlock, 0) + header.Hash() + err := bt.AddBlock(header, 0) require.NotNil(t, err) longestPath := bt.longestPath() @@ -203,15 +194,12 @@ func TestBlockTree_Subchain(t *testing.T) { expectedPath := hashes[1:] // Insert a block to create a competing path - extraBlock := &types.Block{ - Header: &types.Header{ - ParentHash: hashes[0], - Number: big.NewInt(1), - }, - Body: &types.Body{}, + extraBlock := &types.Header{ + ParentHash: hashes[0], + Number: big.NewInt(1), } - extraBlock.Header.Hash() + extraBlock.Hash() err := bt.AddBlock(extraBlock, 0) require.NotNil(t, err) @@ -256,16 +244,13 @@ func TestBlockTree_GetNode(t *testing.T) { bt, branches := createTestBlockTree(testHeader, 16, nil) for _, branch := range branches { - block := &types.Block{ - Header: &types.Header{ - ParentHash: branch.hash, - Number: branch.depth, - StateRoot: Hash{0x1}, - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: branch.hash, + Number: branch.depth, + StateRoot: Hash{0x1}, } - err := bt.AddBlock(block, 0) + err := bt.AddBlock(header, 0) require.Nil(t, err) } } @@ -291,17 +276,14 @@ func TestBlockTree_GetAllBlocksAtDepth(t *testing.T) { previousHash := btHashes[4] for i := 4; i <= btDepth; i++ { - block := &types.Block{ - Header: &types.Header{ - ParentHash: previousHash, - Number: big.NewInt(int64(i)), - Digest: types.Digest{newMockDigestItem(9)}, - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: previousHash, + Number: big.NewInt(int64(i)), + Digest: types.Digest{newMockDigestItem(9)}, } - hash := block.Header.Hash() - bt.AddBlock(block, 0) + hash := header.Hash() + bt.AddBlock(header, 0) previousHash = hash if i == desiredDepth-1 { @@ -313,17 +295,14 @@ func TestBlockTree_GetAllBlocksAtDepth(t *testing.T) { previousHash = btHashes[2] for i := 2; i <= btDepth; i++ { - block := &types.Block{ - Header: &types.Header{ - ParentHash: previousHash, - Number: big.NewInt(int64(i)), - Digest: types.Digest{newMockDigestItem(7)}, - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: previousHash, + Number: big.NewInt(int64(i)), + Digest: types.Digest{newMockDigestItem(7)}, } - hash := block.Header.Hash() - bt.AddBlock(block, 0) + hash := header.Hash() + bt.AddBlock(header, 0) previousHash = hash if i == desiredDepth-1 { diff --git a/lib/blocktree/database_test.go b/lib/blocktree/database_test.go index 2333194553..3906677cd0 100644 --- a/lib/blocktree/database_test.go +++ b/lib/blocktree/database_test.go @@ -28,16 +28,13 @@ func createTestBlockTree(header *types.Header, depth int, db chaindb.Database) ( // create base tree for i := 1; i <= depth; i++ { - block := &types.Block{ - Header: &types.Header{ - ParentHash: previousHash, - Number: big.NewInt(int64(i)), - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: previousHash, + Number: big.NewInt(int64(i)), } - hash := block.Header.Hash() - bt.AddBlock(block, 0) + hash := header.Hash() + bt.AddBlock(header, 0) previousHash = hash isBranch := r.Intn(2) @@ -54,17 +51,14 @@ func createTestBlockTree(header *types.Header, depth int, db chaindb.Database) ( previousHash = branch.hash for i := int(branch.depth.Uint64()); i <= depth; i++ { - block := &types.Block{ - Header: &types.Header{ - ParentHash: previousHash, - Number: big.NewInt(int64(i)), - Digest: types.Digest{newMockDigestItem(rand.Intn(256))}, - }, - Body: &types.Body{}, + header := &types.Header{ + ParentHash: previousHash, + Number: big.NewInt(int64(i)), + Digest: types.Digest{newMockDigestItem(rand.Intn(256))}, } - hash := block.Header.Hash() - bt.AddBlock(block, 0) + hash := header.Hash() + bt.AddBlock(header, 0) previousHash = hash } }