|
|
@@ -20,6 +20,7 @@ import (
|
|
|
"bytes"
|
|
|
"encoding/binary"
|
|
|
"math/big"
|
|
|
+ "sort"
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
@@ -702,6 +703,102 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
|
|
|
DeleteTd(db, hash, number)
|
|
|
}
|
|
|
|
|
|
+const badBlockToKeep = 10
|
|
|
+
|
|
|
+type badBlock struct {
|
|
|
+ Header *types.Header
|
|
|
+ Body *types.Body
|
|
|
+}
|
|
|
+
|
|
|
+// badBlockList implements the sort interface to allow sorting a list of
|
|
|
+// bad blocks by their number in the reverse order.
|
|
|
+type badBlockList []*badBlock
|
|
|
+
|
|
|
+func (s badBlockList) Len() int { return len(s) }
|
|
|
+func (s badBlockList) Less(i, j int) bool {
|
|
|
+ return s[i].Header.Number.Uint64() < s[j].Header.Number.Uint64()
|
|
|
+}
|
|
|
+func (s badBlockList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
+
|
|
|
+// ReadBadBlock retrieves the bad block with the corresponding block hash.
|
|
|
+func ReadBadBlock(db ethdb.Reader, hash common.Hash) *types.Block {
|
|
|
+ blob, err := db.Get(badBlockKey)
|
|
|
+ if err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var badBlocks badBlockList
|
|
|
+ if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ for _, bad := range badBlocks {
|
|
|
+ if bad.Header.Hash() == hash {
|
|
|
+ return types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// ReadAllBadBlocks retrieves all the bad blocks in the database.
|
|
|
+// All returned blocks are sorted in reverse order by number.
|
|
|
+func ReadAllBadBlocks(db ethdb.Reader) []*types.Block {
|
|
|
+ blob, err := db.Get(badBlockKey)
|
|
|
+ if err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var badBlocks badBlockList
|
|
|
+ if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ var blocks []*types.Block
|
|
|
+ for _, bad := range badBlocks {
|
|
|
+ blocks = append(blocks, types.NewBlockWithHeader(bad.Header).WithBody(bad.Body.Transactions, bad.Body.Uncles))
|
|
|
+ }
|
|
|
+ return blocks
|
|
|
+}
|
|
|
+
|
|
|
+// WriteBadBlock serializes the bad block into the database. If the cumulated
|
|
|
+// bad blocks exceeds the limitation, the oldest will be dropped.
|
|
|
+func WriteBadBlock(db ethdb.KeyValueStore, block *types.Block) {
|
|
|
+ blob, err := db.Get(badBlockKey)
|
|
|
+ if err != nil {
|
|
|
+ log.Warn("Failed to load old bad blocks", "error", err)
|
|
|
+ }
|
|
|
+ var badBlocks badBlockList
|
|
|
+ if len(blob) > 0 {
|
|
|
+ if err := rlp.DecodeBytes(blob, &badBlocks); err != nil {
|
|
|
+ log.Crit("Failed to decode old bad blocks", "error", err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for _, b := range badBlocks {
|
|
|
+ if b.Header.Number.Uint64() == block.NumberU64() && b.Header.Hash() == block.Hash() {
|
|
|
+ log.Info("Skip duplicated bad block", "number", block.NumberU64(), "hash", block.Hash())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ badBlocks = append(badBlocks, &badBlock{
|
|
|
+ Header: block.Header(),
|
|
|
+ Body: block.Body(),
|
|
|
+ })
|
|
|
+ sort.Sort(sort.Reverse(badBlocks))
|
|
|
+ if len(badBlocks) > badBlockToKeep {
|
|
|
+ badBlocks = badBlocks[:badBlockToKeep]
|
|
|
+ }
|
|
|
+ data, err := rlp.EncodeToBytes(badBlocks)
|
|
|
+ if err != nil {
|
|
|
+ log.Crit("Failed to encode bad blocks", "err", err)
|
|
|
+ }
|
|
|
+ if err := db.Put(badBlockKey, data); err != nil {
|
|
|
+ log.Crit("Failed to write bad blocks", "err", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// DeleteBadBlocks deletes all the bad blocks from the database
|
|
|
+func DeleteBadBlocks(db ethdb.KeyValueWriter) {
|
|
|
+ if err := db.Delete(badBlockKey); err != nil {
|
|
|
+ log.Crit("Failed to delete bad blocks", "err", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// FindCommonAncestor returns the last common ancestor of two block headers
|
|
|
func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
|
|
|
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
|