mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
commit
3c868707f2
21 changed files with 751 additions and 234 deletions
270
cmd/tomoclean/main.go
Normal file
270
cmd/tomoclean/main.go
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/hashicorp/golang-lru"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dir = flag.String("dir", "", "dir to mainet chain data")
|
||||||
|
cacheSize = flag.Int("size", 1000000, "dir to mainet chain data")
|
||||||
|
)
|
||||||
|
|
||||||
|
type TrieRoot struct {
|
||||||
|
trie *trie.SecureTrie
|
||||||
|
number uint64
|
||||||
|
}
|
||||||
|
type StateNode struct {
|
||||||
|
node trie.Node
|
||||||
|
path []byte
|
||||||
|
}
|
||||||
|
type ResultProcessNode struct {
|
||||||
|
index int
|
||||||
|
number int
|
||||||
|
newNodes [17]*StateNode
|
||||||
|
keys [17]*[]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var sercureKey = []byte("secure-key-")
|
||||||
|
var nWorker = runtime.NumCPU() / 2
|
||||||
|
var cleanAddress = []common.Address{common.HexToAddress(common.BlockSigners)}
|
||||||
|
var cache *lru.Cache
|
||||||
|
var finish = int32(0)
|
||||||
|
var running = true
|
||||||
|
var stateRoots = make(chan TrieRoot)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
lddb, _ := ethdb.NewLDBDatabase(*dir, eth.DefaultConfig.DatabaseCache, utils.MakeDatabaseHandles())
|
||||||
|
head := core.GetHeadBlockHash(lddb)
|
||||||
|
currentHeader := core.GetHeader(lddb, head, core.GetBlockNumber(lddb, head))
|
||||||
|
tridb := trie.NewDatabase(lddb)
|
||||||
|
catchEventInterupt(lddb.LDB())
|
||||||
|
cache, _ = lru.New(*cacheSize)
|
||||||
|
go func() {
|
||||||
|
for i := uint64(1); i <= currentHeader.Number.Uint64(); i++ {
|
||||||
|
hash := core.GetCanonicalHash(lddb, i)
|
||||||
|
root := core.GetHeader(lddb, hash, i).Root
|
||||||
|
trieRoot, err := trie.NewSecure(root, tridb, 0)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
stateRoots <- TrieRoot{trieRoot, i}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if running {
|
||||||
|
close(stateRoots)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
for trieRoot := range stateRoots {
|
||||||
|
atomic.StoreInt32(&finish, 1)
|
||||||
|
if running {
|
||||||
|
for _, address := range cleanAddress {
|
||||||
|
enc := trieRoot.trie.Get(address.Bytes())
|
||||||
|
var data state.Account
|
||||||
|
rlp.DecodeBytes(enc, &data)
|
||||||
|
fmt.Println(time.Now().Format(time.RFC3339), "Start clean state address ", address.Hex(), " at block ", trieRoot.number)
|
||||||
|
signerRoot, err := resolveHash(data.Root[:], lddb.LDB())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(time.Now().Format(time.RFC3339), "Not found clean state address ", address.Hex(), " at block ", trieRoot.number)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
batch := new(leveldb.Batch)
|
||||||
|
count := 1
|
||||||
|
list := []*StateNode{{node: signerRoot}}
|
||||||
|
for len(list) > 0 {
|
||||||
|
newList, total := findNewNodes(list, lddb.LDB(), batch)
|
||||||
|
count = count + 17*len(newList)
|
||||||
|
list = removeNodesNil(newList, total)
|
||||||
|
}
|
||||||
|
fmt.Println(time.Now().Format(time.RFC3339), "Finish clean state address ", address.Hex(), " at block ", trieRoot.number, " keys ", count)
|
||||||
|
err = lddb.LDB().Write(batch, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(time.Now().Format(time.RFC3339), "Write batch leveldb error", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
atomic.StoreInt32(&finish, 0)
|
||||||
|
}
|
||||||
|
fmt.Println(time.Now(), "compact")
|
||||||
|
lddb.LDB().CompactRange(util.Range{})
|
||||||
|
lddb.Close()
|
||||||
|
fmt.Println(time.Now(), "end")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeNodesNil(list [][17]*StateNode, length int) []*StateNode {
|
||||||
|
results := make([]*StateNode, length)
|
||||||
|
index := 0
|
||||||
|
for _, nodes := range list {
|
||||||
|
for _, node := range nodes {
|
||||||
|
if node != nil {
|
||||||
|
results[index] = node
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
func catchEventInterupt(db *leveldb.DB) {
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
go func() {
|
||||||
|
for sig := range c {
|
||||||
|
fmt.Println("catch event interrupt ", sig, running, finish)
|
||||||
|
running = false
|
||||||
|
if atomic.LoadInt32(&finish) == 0 {
|
||||||
|
close(stateRoots)
|
||||||
|
db.Close()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
func resolveHash(n trie.HashNode, db *leveldb.DB) (trie.Node, error) {
|
||||||
|
if cache.Contains(common.BytesToHash(n)) {
|
||||||
|
return nil, &trie.MissingNodeError{}
|
||||||
|
}
|
||||||
|
enc, err := db.Get(n, nil)
|
||||||
|
if err != nil || enc == nil {
|
||||||
|
return nil, &trie.MissingNodeError{}
|
||||||
|
}
|
||||||
|
return trie.MustDecodeNode(n, enc, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllChilds(n StateNode, db *leveldb.DB) ([17]*StateNode, error) {
|
||||||
|
childs := [17]*StateNode{}
|
||||||
|
switch node := n.node.(type) {
|
||||||
|
case *trie.FullNode:
|
||||||
|
// Full Node, move to the first non-nil child.
|
||||||
|
for i := 0; i < len(node.Children); i++ {
|
||||||
|
child := node.Children[i]
|
||||||
|
if child != nil {
|
||||||
|
childNode := child
|
||||||
|
var err error = nil
|
||||||
|
if _, ok := child.(trie.HashNode); ok {
|
||||||
|
childNode, err = resolveHash(child.(trie.HashNode), db)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
childs[i] = &StateNode{node: childNode, path: append(n.path, byte(i))}
|
||||||
|
} else if err != nil {
|
||||||
|
_, ok := err.(*trie.MissingNodeError)
|
||||||
|
if !ok {
|
||||||
|
return childs, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *trie.ShortNode:
|
||||||
|
// Short Node, return the pointer singleton child
|
||||||
|
childNode := node.Val
|
||||||
|
var err error = nil
|
||||||
|
if _, ok := node.Val.(trie.HashNode); ok {
|
||||||
|
childNode, err = resolveHash(node.Val.(trie.HashNode), db)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
childs[0] = &StateNode{node: childNode, path: append(n.path, node.Key...)}
|
||||||
|
} else if err != nil {
|
||||||
|
_, ok := err.(*trie.MissingNodeError)
|
||||||
|
if !ok {
|
||||||
|
return childs, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return childs, nil
|
||||||
|
}
|
||||||
|
func processNodes(node StateNode, db *leveldb.DB) ([17]*StateNode, [17]*[]byte, int) {
|
||||||
|
hash, _ := node.node.Cache()
|
||||||
|
commonHash := common.BytesToHash(hash)
|
||||||
|
newNodes := [17]*StateNode{}
|
||||||
|
keys := [17]*[]byte{}
|
||||||
|
number := 0
|
||||||
|
if !cache.Contains(commonHash) {
|
||||||
|
childNodes, err := getAllChilds(node, db)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error when get all childs node : ", common.Bytes2Hex(node.path), err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for i, child := range childNodes {
|
||||||
|
if child != nil {
|
||||||
|
if _, ok := child.node.(trie.ValueNode); ok {
|
||||||
|
buf := append(sercureKey, child.path...)
|
||||||
|
keys[i] = &buf
|
||||||
|
} else {
|
||||||
|
hash, _ := child.node.Cache()
|
||||||
|
var bytes []byte = hash
|
||||||
|
keys[i] = &bytes
|
||||||
|
newNodes[i] = child
|
||||||
|
number++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache.Add(commonHash, true)
|
||||||
|
}
|
||||||
|
return newNodes, keys, number
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNewNodes(nodes []*StateNode, db *leveldb.DB, batchlvdb *leveldb.Batch) ([][17]*StateNode, int) {
|
||||||
|
length := len(nodes)
|
||||||
|
chunkSize := length / nWorker
|
||||||
|
if len(nodes)%nWorker != 0 {
|
||||||
|
chunkSize++
|
||||||
|
}
|
||||||
|
childNodes := make([][17]*StateNode, length)
|
||||||
|
results := make(chan ResultProcessNode)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(length)
|
||||||
|
for i := 0; i < nWorker; i++ {
|
||||||
|
from := i * chunkSize
|
||||||
|
to := from + chunkSize
|
||||||
|
if to > length {
|
||||||
|
to = length
|
||||||
|
}
|
||||||
|
go func(from int, to int) {
|
||||||
|
for j := from; j < to; j++ {
|
||||||
|
childs, keys, number := processNodes(*nodes[j], db)
|
||||||
|
go func(result ResultProcessNode) {
|
||||||
|
results <- result
|
||||||
|
}(ResultProcessNode{j, number, childs, keys})
|
||||||
|
}
|
||||||
|
}(from, to)
|
||||||
|
}
|
||||||
|
total := 0
|
||||||
|
go func() {
|
||||||
|
for result := range results {
|
||||||
|
childNodes[result.index] = result.newNodes
|
||||||
|
total = total + result.number
|
||||||
|
for _, key := range result.keys {
|
||||||
|
if key != nil {
|
||||||
|
batchlvdb.Delete(*key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
return childNodes, total
|
||||||
|
}
|
||||||
|
|
@ -747,9 +747,9 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for tomo and returns half of the allowance to assign to the database.
|
// for tomo and returns half of the allowance to assign to the database.
|
||||||
func makeDatabaseHandles() int {
|
func MakeDatabaseHandles() int {
|
||||||
limit, err := fdlimit.Current()
|
limit, err := fdlimit.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||||
|
|
@ -1066,7 +1066,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
||||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||||
}
|
}
|
||||||
cfg.DatabaseHandles = makeDatabaseHandles()
|
cfg.DatabaseHandles = MakeDatabaseHandles()
|
||||||
|
|
||||||
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
||||||
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
||||||
|
|
@ -1212,7 +1212,7 @@ func SetupNetwork(ctx *cli.Context) {
|
||||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
var (
|
var (
|
||||||
cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||||
handles = makeDatabaseHandles()
|
handles = MakeDatabaseHandles()
|
||||||
)
|
)
|
||||||
name := "chaindata"
|
name := "chaindata"
|
||||||
if ctx.GlobalBool(LightModeFlag.Name) {
|
if ctx.GlobalBool(LightModeFlag.Name) {
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,13 @@ const (
|
||||||
LimitThresholdNonceInQueue = 10
|
LimitThresholdNonceInQueue = 10
|
||||||
DefaultMinGasPrice = 2500
|
DefaultMinGasPrice = 2500
|
||||||
MergeSignRange = 15
|
MergeSignRange = 15
|
||||||
|
RangeReturnSigner = 150
|
||||||
|
MinimunMinerBlockPerEpoch = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var TIP2019Block = big.NewInt(1050000)
|
var TIP2019Block = big.NewInt(1050000)
|
||||||
var IsTestnet = false
|
var TIPSigning = big.NewInt(3000000)
|
||||||
|
var IsTestnet bool = false
|
||||||
var StoreRewardFolder string
|
var StoreRewardFolder string
|
||||||
var RollbackHash Hash
|
var RollbackHash Hash
|
||||||
var MinGasPrice int64
|
var MinGasPrice int64
|
||||||
|
|
|
||||||
|
|
@ -225,11 +225,12 @@ type Posv struct {
|
||||||
signFn clique.SignerFn // Signer function to authorize hashes with
|
signFn clique.SignerFn // Signer function to authorize hashes with
|
||||||
lock sync.RWMutex // Protects the signer fields
|
lock sync.RWMutex // Protects the signer fields
|
||||||
|
|
||||||
BlockSigners *lru.Cache
|
BlockSigners *lru.Cache
|
||||||
HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) (error, map[string]interface{})
|
HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) (error, map[string]interface{})
|
||||||
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
|
HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error)
|
||||||
HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
|
HookPenaltyTIPSigning func(chain consensus.ChainReader, header *types.Header, candidate []common.Address) ([]common.Address, error)
|
||||||
HookVerifyMNs func(header *types.Header, signers []common.Address) error
|
HookValidator func(header *types.Header, signers []common.Address) ([]byte, error)
|
||||||
|
HookVerifyMNs func(header *types.Header, signers []common.Address) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a PoSV proof-of-stake-voting consensus engine with the initial
|
// New creates a PoSV proof-of-stake-voting consensus engine with the initial
|
||||||
|
|
@ -397,9 +398,15 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.
|
||||||
}
|
}
|
||||||
// If the block is a checkpoint block, verify the signer list
|
// If the block is a checkpoint block, verify the signer list
|
||||||
if number%c.config.Epoch == 0 {
|
if number%c.config.Epoch == 0 {
|
||||||
|
signers := snap.GetSigners()
|
||||||
penPenalties := []common.Address{}
|
penPenalties := []common.Address{}
|
||||||
if c.HookPenalty != nil {
|
if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
|
||||||
penPenalties, err = c.HookPenalty(chain, number)
|
var err error = nil
|
||||||
|
if chain.Config().IsTIPSigning(header.Number) {
|
||||||
|
penPenalties, err = c.HookPenaltyTIPSigning(chain, header, signers)
|
||||||
|
} else {
|
||||||
|
penPenalties, err = c.HookPenalty(chain, number)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -411,7 +418,6 @@ func (c *Posv) verifyCascadingFields(chain consensus.ChainReader, header *types.
|
||||||
return errInvalidCheckpointPenalties
|
return errInvalidCheckpointPenalties
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signers := snap.GetSigners()
|
|
||||||
signers = common.RemoveItemFromArray(signers, penPenalties)
|
signers = common.RemoveItemFromArray(signers, penPenalties)
|
||||||
for i := 1; i <= common.LimitPenaltyEpoch; i++ {
|
for i := 1; i <= common.LimitPenaltyEpoch; i++ {
|
||||||
if number > uint64(i)*c.config.Epoch {
|
if number > uint64(i)*c.config.Epoch {
|
||||||
|
|
@ -788,8 +794,14 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error
|
||||||
header.Extra = header.Extra[:extraVanity]
|
header.Extra = header.Extra[:extraVanity]
|
||||||
masternodes := snap.GetSigners()
|
masternodes := snap.GetSigners()
|
||||||
if number >= c.config.Epoch && number%c.config.Epoch == 0 {
|
if number >= c.config.Epoch && number%c.config.Epoch == 0 {
|
||||||
if c.HookPenalty != nil {
|
if c.HookPenalty != nil || c.HookPenaltyTIPSigning != nil {
|
||||||
penMasternodes, err := c.HookPenalty(chain, number)
|
var penMasternodes []common.Address = nil
|
||||||
|
var err error = nil
|
||||||
|
if chain.Config().IsTIPSigning(header.Number) {
|
||||||
|
penMasternodes, err = c.HookPenaltyTIPSigning(chain, header, masternodes)
|
||||||
|
} else {
|
||||||
|
penMasternodes, err = c.HookPenalty(chain, number)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -797,7 +809,7 @@ func (c *Posv) Prepare(chain consensus.ChainReader, header *types.Header) error
|
||||||
// penalize bad masternode(s)
|
// penalize bad masternode(s)
|
||||||
masternodes = common.RemoveItemFromArray(masternodes, penMasternodes)
|
masternodes = common.RemoveItemFromArray(masternodes, penMasternodes)
|
||||||
for _, address := range penMasternodes {
|
for _, address := range penMasternodes {
|
||||||
log.Debug("Penalty status", "address", address, "block number", number)
|
log.Debug("Penalty status", "address", address, "number", number)
|
||||||
}
|
}
|
||||||
header.Penalties = common.ExtractAddressToBytes(penMasternodes)
|
header.Penalties = common.ExtractAddressToBytes(penMasternodes)
|
||||||
}
|
}
|
||||||
|
|
@ -1036,8 +1048,8 @@ func (c *Posv) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Hea
|
||||||
return masternodes
|
return masternodes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) error {
|
func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction {
|
||||||
var signTxs []*types.Transaction
|
signTxs := []*types.Transaction{}
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
if tx.IsSigningTransaction() {
|
if tx.IsSigningTransaction() {
|
||||||
var b uint
|
var b uint
|
||||||
|
|
@ -1063,7 +1075,19 @@ func (c *Posv) CacheData(header *types.Header, txs []*types.Transaction, receipt
|
||||||
log.Debug("Save tx signers to cache", "hash", header.Hash().String(), "number", header.Number, "len(txs)", len(signTxs))
|
log.Debug("Save tx signers to cache", "hash", header.Hash().String(), "number", header.Number, "len(txs)", len(signTxs))
|
||||||
c.BlockSigners.Add(header.Hash(), signTxs)
|
c.BlockSigners.Add(header.Hash(), signTxs)
|
||||||
|
|
||||||
return nil
|
return signTxs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Posv) CacheSigner(hash common.Hash, txs []*types.Transaction) []*types.Transaction {
|
||||||
|
signTxs := []*types.Transaction{}
|
||||||
|
for _, tx := range txs {
|
||||||
|
if tx.IsSigningTransaction() {
|
||||||
|
signTxs = append(signTxs, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debug("Save tx signers to cache", "hash", hash.String(), "len(txs)", len(signTxs))
|
||||||
|
c.BlockSigners.Add(hash, signTxs)
|
||||||
|
return signTxs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Posv) GetDb() ethdb.Database {
|
func (c *Posv) GetDb() ethdb.Database {
|
||||||
|
|
|
||||||
|
|
@ -320,45 +320,23 @@ func GetRewardForCheckpoint(c *posv.Posv, chain consensus.ChainReader, header *t
|
||||||
for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- {
|
for i := prevCheckpoint + (rCheckpoint * 2) - 1; i >= startBlockNumber; i-- {
|
||||||
header = chain.GetHeader(header.ParentHash, i)
|
header = chain.GetHeader(header.ParentHash, i)
|
||||||
mapBlkHash[i] = header.Hash()
|
mapBlkHash[i] = header.Hash()
|
||||||
if signData, ok := c.BlockSigners.Get(header.Hash()); ok {
|
signData, ok := c.BlockSigners.Get(header.Hash())
|
||||||
txs := signData.([]*types.Transaction)
|
if !ok {
|
||||||
for _, tx := range txs {
|
|
||||||
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
||||||
from := *tx.From()
|
|
||||||
data[blkHash] = append(data[blkHash], from)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
|
log.Debug("Failed get from cached", "hash", header.Hash().String(), "number", i)
|
||||||
block := chain.GetBlock(header.Hash(), i)
|
block := chain.GetBlock(header.Hash(), i)
|
||||||
txs := block.Transactions()
|
txs := block.Transactions()
|
||||||
receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i)
|
if !chain.Config().IsTIPSigning(header.Number) {
|
||||||
|
receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i)
|
||||||
var signTxs []*types.Transaction
|
signData = c.CacheData(header, txs, receipts)
|
||||||
for _, tx := range txs {
|
} else {
|
||||||
if tx.IsSigningTransaction() {
|
signData = c.CacheSigner(header.Hash(), txs)
|
||||||
var b uint
|
|
||||||
for _, r := range receipts {
|
|
||||||
if r.TxHash == tx.Hash() {
|
|
||||||
if len(r.PostState) > 0 {
|
|
||||||
b = types.ReceiptStatusSuccessful
|
|
||||||
} else {
|
|
||||||
b = r.Status
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if b == types.ReceiptStatusFailed {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
signTxs = append(signTxs, tx)
|
|
||||||
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
|
||||||
from := *tx.From()
|
|
||||||
data[blkHash] = append(data[blkHash], from)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.BlockSigners.Add(header.Hash(), signTxs)
|
}
|
||||||
|
txs := signData.([]*types.Transaction)
|
||||||
|
for _, tx := range txs {
|
||||||
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
||||||
|
from := *tx.From()
|
||||||
|
data[blkHash] = append(data[blkHash], from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
header = chain.GetHeader(header.ParentHash, prevCheckpoint)
|
header = chain.GetHeader(header.ParentHash, prevCheckpoint)
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
||||||
bc.currentBlock.Store(block)
|
bc.currentBlock.Store(block)
|
||||||
|
|
||||||
// save cache BlockSigners
|
// save cache BlockSigners
|
||||||
if bc.chainConfig.Posv != nil {
|
if bc.chainConfig.Posv != nil && !bc.chainConfig.IsTIPSigning(block.Number()) {
|
||||||
engine := bc.Engine().(*posv.Posv)
|
engine := bc.Engine().(*posv.Posv)
|
||||||
engine.CacheData(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash()))
|
engine.CacheData(block.Header(), block.Transactions(), bc.GetReceiptsByHash(block.Hash()))
|
||||||
}
|
}
|
||||||
|
|
@ -1019,6 +1019,11 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
if status == CanonStatTy {
|
if status == CanonStatTy {
|
||||||
bc.insert(block)
|
bc.insert(block)
|
||||||
}
|
}
|
||||||
|
// save cache BlockSigners
|
||||||
|
if bc.chainConfig.Posv != nil && bc.chainConfig.IsTIPSigning(block.Number()) {
|
||||||
|
engine := bc.Engine().(*posv.Posv)
|
||||||
|
engine.CacheSigner(block.Header().Hash(), block.Transactions())
|
||||||
|
}
|
||||||
bc.futureBlocks.Remove(block.Hash())
|
bc.futureBlocks.Remove(block.Hash())
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -357,6 +357,14 @@ func (self *StateDB) deleteStateObject(stateObject *stateObject) {
|
||||||
self.setError(self.trie.TryDelete(addr[:]))
|
self.setError(self.trie.TryDelete(addr[:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAddress removes the address from the state trie.
|
||||||
|
func (self *StateDB) DeleteAddress(addr common.Address) {
|
||||||
|
stateObject := self.getStateObject(addr)
|
||||||
|
if stateObject != nil && !stateObject.deleted {
|
||||||
|
self.deleteStateObject(stateObject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve a state object given my the address. Returns nil if not found.
|
// Retrieve a state object given my the address. Returns nil if not found.
|
||||||
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
|
||||||
// Prefer 'live' objects.
|
// Prefer 'live' objects.
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
misc.ApplyDAOHardFork(statedb)
|
misc.ApplyDAOHardFork(statedb)
|
||||||
}
|
}
|
||||||
|
if common.TIPSigning.Cmp(header.Number) == 0 {
|
||||||
|
statedb.DeleteAddress(common.HexToAddress(common.BlockSigners))
|
||||||
|
}
|
||||||
InitSignerInTransactions(p.config, header, block.Transactions())
|
InitSignerInTransactions(p.config, header, block.Transactions())
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
statedb.Prepare(tx.Hash(), block.Hash(), i)
|
||||||
|
|
@ -101,6 +104,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
misc.ApplyDAOHardFork(statedb)
|
misc.ApplyDAOHardFork(statedb)
|
||||||
}
|
}
|
||||||
|
if common.TIPSigning.Cmp(header.Number) == 0 {
|
||||||
|
statedb.DeleteAddress(common.HexToAddress(common.BlockSigners))
|
||||||
|
}
|
||||||
if cBlock.stop {
|
if cBlock.stop {
|
||||||
return nil, nil, 0, ErrStopPreparingBlock
|
return nil, nil, 0, ErrStopPreparingBlock
|
||||||
}
|
}
|
||||||
|
|
@ -132,6 +138,9 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated
|
||||||
// for the transaction, gas used and an error if the transaction failed,
|
// for the transaction, gas used and an error if the transaction failed,
|
||||||
// indicating the block was invalid.
|
// indicating the block was invalid.
|
||||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
|
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) {
|
||||||
|
if tx.To() != nil && tx.To().String() == common.BlockSigners && config.IsTIPSigning(header.Number) {
|
||||||
|
return ApplySignTransaction(config, statedb, header, tx, usedGas)
|
||||||
|
}
|
||||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
|
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|
@ -171,6 +180,41 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, author *common
|
||||||
return receipt, gas, err
|
return receipt, gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ApplySignTransaction(config *params.ChainConfig, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error) {
|
||||||
|
// Update the state with pending changes
|
||||||
|
var root []byte
|
||||||
|
if config.IsByzantium(header.Number) {
|
||||||
|
statedb.Finalise(true)
|
||||||
|
} else {
|
||||||
|
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes()
|
||||||
|
}
|
||||||
|
from, err := types.Sender(types.MakeSigner(config, header.Number), tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
nonce := statedb.GetNonce(from)
|
||||||
|
if nonce < tx.Nonce() {
|
||||||
|
return nil, 0, ErrNonceTooHigh
|
||||||
|
} else if nonce > tx.Nonce() {
|
||||||
|
return nil, 0, ErrNonceTooLow
|
||||||
|
}
|
||||||
|
statedb.SetNonce(from, nonce+1)
|
||||||
|
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||||
|
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
||||||
|
receipt := types.NewReceipt(root, false, *usedGas)
|
||||||
|
receipt.TxHash = tx.Hash()
|
||||||
|
receipt.GasUsed = 0
|
||||||
|
// if the transaction created a contract, store the creation address in the receipt.
|
||||||
|
// Set the receipt logs and create a bloom for filtering
|
||||||
|
log := &types.Log{}
|
||||||
|
log.Address = common.HexToAddress(common.BlockSigners)
|
||||||
|
log.BlockNumber = header.Number.Uint64()
|
||||||
|
statedb.AddLog(log)
|
||||||
|
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||||
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
|
return receipt, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func InitSignerInTransactions(config *params.ChainConfig, header *types.Header, txs types.Transactions) {
|
func InitSignerInTransactions(config *params.ChainConfig, header *types.Header, txs types.Transactions) {
|
||||||
nWorker := runtime.NumCPU()
|
nWorker := runtime.NumCPU()
|
||||||
signer := types.MakeSigner(config, header.Number)
|
signer := types.MakeSigner(config, header.Number)
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
|
||||||
contractAction = "contract creation"
|
contractAction = "contract creation"
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction
|
// Increment the nonce for the next transaction
|
||||||
nonce = st.state.GetNonce(sender.Address())+1
|
nonce = st.state.GetNonce(sender.Address()) + 1
|
||||||
st.state.SetNonce(sender.Address(), nonce)
|
st.state.SetNonce(sender.Address(), nonce)
|
||||||
ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
|
ret, st.gas, vmerr = evm.Call(sender, st.to().Address(), st.data, st.gas, st.value)
|
||||||
contractAction = "contract call"
|
contractAction = "contract call"
|
||||||
|
|
|
||||||
|
|
@ -218,8 +218,8 @@ type TxPool struct {
|
||||||
|
|
||||||
wg sync.WaitGroup // for shutdown sync
|
wg sync.WaitGroup // for shutdown sync
|
||||||
|
|
||||||
homestead bool
|
homestead bool
|
||||||
IsMasterNode func(address common.Address) bool
|
IsSigner func(address common.Address) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||||
|
|
@ -592,7 +592,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
// Drop non-local transactions under our own minimal accepted gas price
|
// Drop non-local transactions under our own minimal accepted gas price
|
||||||
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
|
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
|
||||||
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
|
||||||
if !tx.IsSpecialTransaction() || (pool.IsMasterNode != nil && !pool.IsMasterNode(from)) {
|
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
|
||||||
return ErrUnderpriced
|
return ErrUnderpriced
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -661,7 +661,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
from, _ := types.Sender(pool.signer, tx) // already validated
|
from, _ := types.Sender(pool.signer, tx) // already validated
|
||||||
if tx.IsSpecialTransaction() && pool.IsMasterNode != nil && pool.IsMasterNode(from) && pool.pendingState.GetNonce(from) == tx.Nonce() {
|
if tx.IsSpecialTransaction() && pool.IsSigner != nil && pool.IsSigner(from) && pool.pendingState.GetNonce(from) == tx.Nonce() {
|
||||||
return pool.promoteSpecialTx(from, tx)
|
return pool.promoteSpecialTx(from, tx)
|
||||||
}
|
}
|
||||||
// If the transaction pool is full, discard underpriced transactions
|
// If the transaction pool is full, discard underpriced transactions
|
||||||
|
|
|
||||||
128
eth/backend.go
128
eth/backend.go
|
|
@ -20,6 +20,10 @@ package eth
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"math/big"
|
"math/big"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -29,19 +33,16 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/consensus/posv"
|
"github.com/ethereum/go-ethereum/consensus/posv"
|
||||||
"github.com/ethereum/go-ethereum/contracts"
|
"github.com/ethereum/go-ethereum/contracts"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
//"github.com/ethereum/go-ethereum/core/state"
|
//"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -51,7 +52,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -208,12 +208,13 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
if eth.chainConfig.Posv != nil {
|
if eth.chainConfig.Posv != nil {
|
||||||
c := eth.engine.(*posv.Posv)
|
c := eth.engine.(*posv.Posv)
|
||||||
signHook := func(block *types.Block) error {
|
signHook := func(block *types.Block) error {
|
||||||
ok, err := eth.ValidateMasternode()
|
eb, err := eth.Etherbase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Can't verify masternode permission: %v", err)
|
log.Error("Cannot get etherbase for append m2 header", "err", err)
|
||||||
|
return fmt.Errorf("etherbase missing: %v", err)
|
||||||
}
|
}
|
||||||
|
ok := eth.txPool.IsSigner != nil && eth.txPool.IsSigner(eb)
|
||||||
if !ok {
|
if !ok {
|
||||||
// silently return as this node doesn't have masternode permission to sign block
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if block.NumberU64()%common.MergeSignRange == 0 || !eth.chainConfig.IsTIP2019(block.Number()) {
|
if block.NumberU64()%common.MergeSignRange == 0 || !eth.chainConfig.IsTIP2019(block.Number()) {
|
||||||
|
|
@ -309,6 +310,113 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
return []common.Address{}, nil
|
return []common.Address{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hook scans for bad masternodes and decide to penalty them
|
||||||
|
c.HookPenaltyTIPSigning = func(chain consensus.ChainReader, header *types.Header, candidates []common.Address) ([]common.Address, error) {
|
||||||
|
prevEpoc := header.Number.Uint64() - chain.Config().Posv.Epoch
|
||||||
|
combackEpoch := uint64(0)
|
||||||
|
comebackLength := uint64((common.LimitPenaltyEpoch + 1) * chain.Config().Posv.Epoch)
|
||||||
|
if header.Number.Uint64() > comebackLength {
|
||||||
|
combackEpoch = header.Number.Uint64() - comebackLength
|
||||||
|
}
|
||||||
|
if prevEpoc >= 0 {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
listBlockHash := make([]common.Hash, chain.Config().Posv.Epoch)
|
||||||
|
|
||||||
|
// get list block hash & stats total created block
|
||||||
|
statMiners := make(map[common.Address]int)
|
||||||
|
listBlockHash[0] = header.ParentHash
|
||||||
|
parentnumber := header.Number.Uint64() - 1
|
||||||
|
parentHash := header.ParentHash
|
||||||
|
for i := uint64(1); i < chain.Config().Posv.Epoch; i++ {
|
||||||
|
parentHeader := chain.GetHeader(parentHash, parentnumber)
|
||||||
|
miner, _ := c.RecoverSigner(parentHeader)
|
||||||
|
value, exist := statMiners[miner]
|
||||||
|
if exist {
|
||||||
|
value = value + 1
|
||||||
|
} else {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
statMiners[miner] = value
|
||||||
|
parentHash = parentHeader.ParentHash
|
||||||
|
parentnumber--
|
||||||
|
listBlockHash[i] = parentHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// add list not miner to penalties
|
||||||
|
prevHeader := chain.GetHeaderByNumber(prevEpoc)
|
||||||
|
preMasternodes := c.GetMasternodes(chain, prevHeader)
|
||||||
|
penalties := []common.Address{}
|
||||||
|
for miner, total := range statMiners {
|
||||||
|
if total < common.MinimunMinerBlockPerEpoch {
|
||||||
|
log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total)
|
||||||
|
penalties = append(penalties, miner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, addr := range preMasternodes {
|
||||||
|
if _, exist := statMiners[addr]; !exist {
|
||||||
|
log.Debug("Find a node don't create block", "addr", addr.Hex())
|
||||||
|
penalties = append(penalties, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get list check penalties signing block & list master nodes wil comeback
|
||||||
|
penComebacks := []common.Address{}
|
||||||
|
if combackEpoch > 0 {
|
||||||
|
combackHeader := chain.GetHeaderByNumber(combackEpoch)
|
||||||
|
penalties := common.ExtractAddressFromBytes(combackHeader.Penalties)
|
||||||
|
for _, penaltie := range penalties {
|
||||||
|
for _, addr := range candidates {
|
||||||
|
if penaltie == addr {
|
||||||
|
penComebacks = append(penComebacks, penaltie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop for each block to check missing sign. with comeback nodes
|
||||||
|
mapBlockHash := map[common.Hash]bool{}
|
||||||
|
for i := common.RangeReturnSigner - 1; i >= 0; i-- {
|
||||||
|
if len(penComebacks) > 0 {
|
||||||
|
blockNumber := header.Number.Uint64() - uint64(i) - 1
|
||||||
|
bhash := listBlockHash[i]
|
||||||
|
if blockNumber%common.MergeSignRange == 0 {
|
||||||
|
mapBlockHash[bhash] = true
|
||||||
|
}
|
||||||
|
signData, ok := c.BlockSigners.Get(bhash)
|
||||||
|
if !ok {
|
||||||
|
block := chain.GetBlock(bhash, blockNumber)
|
||||||
|
txs := block.Transactions()
|
||||||
|
signData = c.CacheSigner(bhash, txs)
|
||||||
|
}
|
||||||
|
txs := signData.([]*types.Transaction)
|
||||||
|
// Check signer signed?
|
||||||
|
for _, tx := range txs {
|
||||||
|
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
|
||||||
|
from := *tx.From()
|
||||||
|
if mapBlockHash[blkHash] {
|
||||||
|
for j, addr := range penComebacks {
|
||||||
|
if from == addr {
|
||||||
|
// Remove it from dupSigners.
|
||||||
|
penComebacks = append(penComebacks[:j], penComebacks[j+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Time Calculated HookPenaltyTIPSigning ", "block", header.Number, "hash", header.Hash().Hex(), "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start)))
|
||||||
|
penalties = append(penalties, penComebacks...)
|
||||||
|
return penComebacks, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return []common.Address{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Hook calculates reward for masternodes
|
// Hook calculates reward for masternodes
|
||||||
c.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, header *types.Header) (error, map[string]interface{}) {
|
c.HookReward = func(chain consensus.ChainReader, stateBlock *state.StateDB, header *types.Header) (error, map[string]interface{}) {
|
||||||
parentHeader := eth.blockchain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
parentHeader := eth.blockchain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
||||||
|
|
@ -382,7 +490,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.txPool.IsMasterNode = func(address common.Address) bool {
|
eth.txPool.IsSigner = func(address common.Address) bool {
|
||||||
currentHeader := eth.blockchain.CurrentHeader()
|
currentHeader := eth.blockchain.CurrentHeader()
|
||||||
header := currentHeader
|
header := currentHeader
|
||||||
// Sometimes, the latest block hasn't been inserted to chain yet
|
// Sometimes, the latest block hasn't been inserted to chain yet
|
||||||
|
|
@ -630,7 +738,9 @@ func (s *Ethereum) StartStaking(local bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) StopStaking() { s.miner.Stop() }
|
func (s *Ethereum) StopStaking() {
|
||||||
|
s.miner.Stop()
|
||||||
|
}
|
||||||
func (s *Ethereum) IsStaking() bool { return s.miner.Mining() }
|
func (s *Ethereum) IsStaking() bool { return s.miner.Mining() }
|
||||||
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -692,7 +692,7 @@ func (s *PublicBlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block
|
||||||
if prevCheckpointBlock != nil {
|
if prevCheckpointBlock != nil {
|
||||||
masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().Posv.Epoch)
|
masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().Posv.Epoch)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("Undefined POSV consensus engine")
|
log.Error("Undefined POSV consensus engine")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1044,37 +1044,44 @@ func (s *PublicBlockChainAPI) rpcOutputBlockSigners(b *types.Block, ctx context.
|
||||||
var signers []common.Address
|
var signers []common.Address
|
||||||
var filterSigners []common.Address
|
var filterSigners []common.Address
|
||||||
if b.Number().Int64() > 0 {
|
if b.Number().Int64() > 0 {
|
||||||
curBlockNumber := b.Number().Uint64()
|
blockNumber := b.Number().Uint64()
|
||||||
prevBlockNumber := curBlockNumber + (common.MergeSignRange - (curBlockNumber % common.MergeSignRange))
|
signedBlockNumber := blockNumber + (common.MergeSignRange - (blockNumber % common.MergeSignRange))
|
||||||
latestBlockNumber := s.b.CurrentBlock().Number().Uint64()
|
latestBlockNumber := s.b.CurrentBlock().Number()
|
||||||
if prevBlockNumber >= latestBlockNumber || !s.b.ChainConfig().IsTIP2019(b.Number()) {
|
if signedBlockNumber >= latestBlockNumber.Uint64() || !s.b.ChainConfig().IsTIP2019(b.Number()) {
|
||||||
prevBlockNumber = curBlockNumber
|
signedBlockNumber = blockNumber
|
||||||
}
|
}
|
||||||
if engine, ok := s.b.GetEngine().(*posv.Posv); ok {
|
if engine, ok := s.b.GetEngine().(*posv.Posv); ok {
|
||||||
prevBlock, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(prevBlockNumber))
|
// Get block epoc latest.
|
||||||
if err != nil {
|
lastCheckpointNumber := signedBlockNumber - (signedBlockNumber % s.b.ChainConfig().Posv.Epoch)
|
||||||
log.Error("Fail to get previous block", "error", err)
|
prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber))
|
||||||
return []common.Address{}, err
|
if prevCheckpointBlock != nil {
|
||||||
}
|
masternodes := engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), blockNumber, s.b.ChainConfig().Posv.Epoch)
|
||||||
addrBlockSigner := common.HexToAddress(common.BlockSigners)
|
signedBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(signedBlockNumber))
|
||||||
signers, err = contracts.GetSignersByExecutingEVM(addrBlockSigner, client, prevBlock.Hash())
|
if s.b.ChainConfig().IsTIPSigning(latestBlockNumber) {
|
||||||
if err != nil {
|
signers, err = GetSignersFromBlocks(s.b, signedBlock.NumberU64(), signedBlock.Hash(), masternodes)
|
||||||
log.Error("Fail to get signers from block signer SC.", "error", err)
|
} else {
|
||||||
return []common.Address{}, err
|
signers, err = contracts.GetSignersByExecutingEVM(common.HexToAddress(common.BlockSigners), client, signedBlock.Hash())
|
||||||
}
|
}
|
||||||
validator, _ := engine.RecoverValidator(b.Header())
|
if err != nil {
|
||||||
creator, _ := engine.RecoverSigner(b.Header())
|
log.Error("Fail to get signers from block signer SC.", "error", err)
|
||||||
signers = append(signers, validator)
|
return nil, err
|
||||||
signers = append(signers, creator)
|
}
|
||||||
for _, masternode := range masternodes {
|
validator, _ := engine.RecoverValidator(b.Header())
|
||||||
for _, signer := range signers {
|
creator, _ := engine.RecoverSigner(b.Header())
|
||||||
if signer == masternode {
|
signers = append(signers, validator)
|
||||||
filterSigners = append(filterSigners, masternode)
|
signers = append(signers, creator)
|
||||||
break
|
countFinality := 0
|
||||||
|
for _, masternode := range masternodes {
|
||||||
|
for _, signer := range signers {
|
||||||
|
if signer == masternode {
|
||||||
|
countFinality++
|
||||||
|
filterSigners = append(filterSigners, masternode)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("Undefined POSV consensus engine")
|
log.Error("Undefined POSV consensus engine")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1712,3 +1719,52 @@ func (s *PublicNetAPI) PeerCount() hexutil.Uint {
|
||||||
func (s *PublicNetAPI) Version() string {
|
func (s *PublicNetAPI) Version() string {
|
||||||
return fmt.Sprintf("%d", s.networkVersion)
|
return fmt.Sprintf("%d", s.networkVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash, masternodes []common.Address) ([]common.Address, error) {
|
||||||
|
var addrs []common.Address
|
||||||
|
mapMN := map[common.Address]bool{}
|
||||||
|
for _, node := range masternodes {
|
||||||
|
mapMN[node] = true
|
||||||
|
}
|
||||||
|
if engine, ok := b.GetEngine().(*posv.Posv); ok {
|
||||||
|
limitNumber := blockNumber - blockNumber%b.ChainConfig().Posv.Epoch + 2*b.ChainConfig().Posv.Epoch - 1
|
||||||
|
currentNumber := b.CurrentBlock().NumberU64()
|
||||||
|
if limitNumber > currentNumber {
|
||||||
|
limitNumber = currentNumber
|
||||||
|
}
|
||||||
|
for i := blockNumber + 1; i <= limitNumber; i++ {
|
||||||
|
header, err := b.HeaderByNumber(nil, rpc.BlockNumber(i))
|
||||||
|
if err != nil {
|
||||||
|
return addrs, err
|
||||||
|
}
|
||||||
|
signData, ok := engine.BlockSigners.Get(header.Hash())
|
||||||
|
var signTxs []*types.Transaction = nil
|
||||||
|
if !ok {
|
||||||
|
blockData, err := b.BlockByNumber(nil, rpc.BlockNumber(i))
|
||||||
|
if err != nil {
|
||||||
|
return addrs, err
|
||||||
|
}
|
||||||
|
signTxs = []*types.Transaction{}
|
||||||
|
for _, tx := range blockData.Transactions() {
|
||||||
|
if tx.IsSigningTransaction() {
|
||||||
|
signTxs = append(signTxs, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
signTxs = signData.([]*types.Transaction)
|
||||||
|
}
|
||||||
|
for _, signtx := range signTxs {
|
||||||
|
blkHash := common.BytesToHash(signtx.Data()[len(signtx.Data())-32:])
|
||||||
|
from := *signtx.From()
|
||||||
|
if blkHash == blockHash && mapMN[from] {
|
||||||
|
addrs = append(addrs, from)
|
||||||
|
delete(mapMN, from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(mapMN) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addrs, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -583,6 +584,9 @@ func (self *worker) commitNewWork() {
|
||||||
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
||||||
misc.ApplyDAOHardFork(work.state)
|
misc.ApplyDAOHardFork(work.state)
|
||||||
}
|
}
|
||||||
|
if common.TIPSigning.Cmp(header.Number) == 0 {
|
||||||
|
work.state.DeleteAddress(common.HexToAddress(common.BlockSigners))
|
||||||
|
}
|
||||||
// won't grasp txs at checkpoint
|
// won't grasp txs at checkpoint
|
||||||
var (
|
var (
|
||||||
txs *types.TransactionsByPriceAndNonce
|
txs *types.TransactionsByPriceAndNonce
|
||||||
|
|
@ -671,6 +675,17 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||||
log.Trace("Ignoring reply protected special transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)
|
log.Trace("Ignoring reply protected special transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if tx.To().Hex() == common.BlockSigners {
|
||||||
|
if len(tx.Data()) < 68 {
|
||||||
|
log.Trace("Data special transaction invalid length", "hash", tx.Hash(), "data", len(tx.Data()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
blkNumber := binary.BigEndian.Uint64(tx.Data()[8:40])
|
||||||
|
if blkNumber >= env.header.Number.Uint64() || blkNumber <= env.header.Number.Uint64()-env.config.Posv.Epoch*2 {
|
||||||
|
log.Trace("Data special transaction invalid number", "hash", tx.Hash(), "blkNumber", blkNumber, "miner", env.header.Number)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
// Start executing the transaction
|
// Start executing the transaction
|
||||||
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
|
||||||
nonce := env.state.GetNonce(from)
|
nonce := env.state.GetNonce(from)
|
||||||
|
|
|
||||||
|
|
@ -217,6 +217,10 @@ func (c *ChainConfig) IsTIP2019(num *big.Int) bool {
|
||||||
return isForked(common.TIP2019Block, num)
|
return isForked(common.TIP2019Block, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ChainConfig) IsTIPSigning(num *big.Int) bool {
|
||||||
|
return isForked(common.TIPSigning, num)
|
||||||
|
}
|
||||||
|
|
||||||
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
||||||
//
|
//
|
||||||
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ func returnHasherToPool(h *hasher) {
|
||||||
|
|
||||||
// hash collapses a node down into a hash node, also returning a copy of the
|
// hash collapses a node down into a hash node, also returning a copy of the
|
||||||
// original node initialized with the computed hash to replace the original one.
|
// original node initialized with the computed hash to replace the original one.
|
||||||
func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
|
func (h *hasher) hash(n Node, db *Database, force bool) (Node, Node, error) {
|
||||||
// If we're not storing the node, just hashing, use available cached data
|
// If we're not storing the node, just hashing, use available cached data
|
||||||
if hash, dirty := n.cache(); hash != nil {
|
if hash, dirty := n.Cache(); hash != nil {
|
||||||
if db == nil {
|
if db == nil {
|
||||||
return hash, n, nil
|
return hash, n, nil
|
||||||
}
|
}
|
||||||
|
|
@ -72,23 +72,23 @@ func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
|
||||||
// Trie not processed yet or needs storage, walk the children
|
// Trie not processed yet or needs storage, walk the children
|
||||||
collapsed, cached, err := h.hashChildren(n, db)
|
collapsed, cached, err := h.hashChildren(n, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hashNode{}, n, err
|
return HashNode{}, n, err
|
||||||
}
|
}
|
||||||
hashed, err := h.store(collapsed, db, force)
|
hashed, err := h.store(collapsed, db, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hashNode{}, n, err
|
return HashNode{}, n, err
|
||||||
}
|
}
|
||||||
// Cache the hash of the node for later reuse and remove
|
// Cache the hash of the node for later reuse and remove
|
||||||
// the dirty flag in commit mode. It's fine to assign these values directly
|
// the dirty flag in commit mode. It's fine to assign these values directly
|
||||||
// without copying the node first because hashChildren copies it.
|
// without copying the node first because hashChildren copies it.
|
||||||
cachedHash, _ := hashed.(hashNode)
|
cachedHash, _ := hashed.(HashNode)
|
||||||
switch cn := cached.(type) {
|
switch cn := cached.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
cn.flags.hash = cachedHash
|
cn.flags.hash = cachedHash
|
||||||
if db != nil {
|
if db != nil {
|
||||||
cn.flags.dirty = false
|
cn.flags.dirty = false
|
||||||
}
|
}
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
cn.flags.hash = cachedHash
|
cn.flags.hash = cachedHash
|
||||||
if db != nil {
|
if db != nil {
|
||||||
cn.flags.dirty = false
|
cn.flags.dirty = false
|
||||||
|
|
@ -100,28 +100,28 @@ func (h *hasher) hash(n node, db *Database, force bool) (node, node, error) {
|
||||||
// hashChildren replaces the children of a node with their hashes if the encoded
|
// hashChildren replaces the children of a node with their hashes if the encoded
|
||||||
// size of the child is larger than a hash, returning the collapsed node as well
|
// size of the child is larger than a hash, returning the collapsed node as well
|
||||||
// as a replacement for the original node with the child hashes cached in.
|
// as a replacement for the original node with the child hashes cached in.
|
||||||
func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
|
func (h *hasher) hashChildren(original Node, db *Database) (Node, Node, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch n := original.(type) {
|
switch n := original.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
// Hash the short node's child, caching the newly hashed subtree
|
// Hash the short Node's child, caching the newly hashed subtree
|
||||||
collapsed, cached := n.copy(), n.copy()
|
collapsed, cached := n.copy(), n.copy()
|
||||||
collapsed.Key = hexToCompact(n.Key)
|
collapsed.Key = hexToCompact(n.Key)
|
||||||
cached.Key = common.CopyBytes(n.Key)
|
cached.Key = common.CopyBytes(n.Key)
|
||||||
|
|
||||||
if _, ok := n.Val.(valueNode); !ok {
|
if _, ok := n.Val.(ValueNode); !ok {
|
||||||
collapsed.Val, cached.Val, err = h.hash(n.Val, db, false)
|
collapsed.Val, cached.Val, err = h.hash(n.Val, db, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return original, original, err
|
return original, original, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if collapsed.Val == nil {
|
if collapsed.Val == nil {
|
||||||
collapsed.Val = valueNode(nil) // Ensure that nil children are encoded as empty strings.
|
collapsed.Val = ValueNode(nil) // Ensure that nil children are encoded as empty strings.
|
||||||
}
|
}
|
||||||
return collapsed, cached, nil
|
return collapsed, cached, nil
|
||||||
|
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
// Hash the full node's children, caching the newly hashed subtrees
|
// Hash the full node's children, caching the newly hashed subtrees
|
||||||
collapsed, cached := n.copy(), n.copy()
|
collapsed, cached := n.copy(), n.copy()
|
||||||
|
|
||||||
|
|
@ -132,12 +132,12 @@ func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
|
||||||
return original, original, err
|
return original, original, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
collapsed.Children[i] = valueNode(nil) // Ensure that nil children are encoded as empty strings.
|
collapsed.Children[i] = ValueNode(nil) // Ensure that nil children are encoded as empty strings.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cached.Children[16] = n.Children[16]
|
cached.Children[16] = n.Children[16]
|
||||||
if collapsed.Children[16] == nil {
|
if collapsed.Children[16] == nil {
|
||||||
collapsed.Children[16] = valueNode(nil)
|
collapsed.Children[16] = ValueNode(nil)
|
||||||
}
|
}
|
||||||
return collapsed, cached, nil
|
return collapsed, cached, nil
|
||||||
|
|
||||||
|
|
@ -150,9 +150,9 @@ func (h *hasher) hashChildren(original node, db *Database) (node, node, error) {
|
||||||
// store hashes the node n and if we have a storage layer specified, it writes
|
// store hashes the node n and if we have a storage layer specified, it writes
|
||||||
// the key/value pair to it and tracks any node->child references as well as any
|
// the key/value pair to it and tracks any node->child references as well as any
|
||||||
// node->external trie references.
|
// node->external trie references.
|
||||||
func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
func (h *hasher) store(n Node, db *Database, force bool) (Node, error) {
|
||||||
// Don't store hashes or empty nodes.
|
// Don't store hashes or empty nodes.
|
||||||
if _, isHash := n.(hashNode); n == nil || isHash {
|
if _, isHash := n.(HashNode); n == nil || isHash {
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
// Generate the RLP encoding of the node
|
// Generate the RLP encoding of the node
|
||||||
|
|
@ -164,11 +164,11 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
||||||
return n, nil // Nodes smaller than 32 bytes are stored inside their parent
|
return n, nil // Nodes smaller than 32 bytes are stored inside their parent
|
||||||
}
|
}
|
||||||
// Larger nodes are replaced by their hash and stored in the database.
|
// Larger nodes are replaced by their hash and stored in the database.
|
||||||
hash, _ := n.cache()
|
hash, _ := n.Cache()
|
||||||
if hash == nil {
|
if hash == nil {
|
||||||
h.sha.Reset()
|
h.sha.Reset()
|
||||||
h.sha.Write(h.tmp.Bytes())
|
h.sha.Write(h.tmp.Bytes())
|
||||||
hash = hashNode(h.sha.Sum(nil))
|
hash = HashNode(h.sha.Sum(nil))
|
||||||
}
|
}
|
||||||
if db != nil {
|
if db != nil {
|
||||||
// We are pooling the trie nodes into an intermediate memory cache
|
// We are pooling the trie nodes into an intermediate memory cache
|
||||||
|
|
@ -179,13 +179,13 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
||||||
|
|
||||||
// Track all direct parent->child node references
|
// Track all direct parent->child node references
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
if child, ok := n.Val.(hashNode); ok {
|
if child, ok := n.Val.(HashNode); ok {
|
||||||
db.reference(common.BytesToHash(child), hash)
|
db.reference(common.BytesToHash(child), hash)
|
||||||
}
|
}
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
if child, ok := n.Children[i].(hashNode); ok {
|
if child, ok := n.Children[i].(HashNode); ok {
|
||||||
db.reference(common.BytesToHash(child), hash)
|
db.reference(common.BytesToHash(child), hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -195,13 +195,13 @@ func (h *hasher) store(n node, db *Database, force bool) (node, error) {
|
||||||
// Track external references from account->storage trie
|
// Track external references from account->storage trie
|
||||||
if h.onleaf != nil {
|
if h.onleaf != nil {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
if child, ok := n.Val.(valueNode); ok {
|
if child, ok := n.Val.(ValueNode); ok {
|
||||||
h.onleaf(child, hash)
|
h.onleaf(child, hash)
|
||||||
}
|
}
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
if child, ok := n.Children[i].(valueNode); ok {
|
if child, ok := n.Children[i].(ValueNode); ok {
|
||||||
h.onleaf(child, hash)
|
h.onleaf(child, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -60,6 +59,7 @@ type NodeIterator interface {
|
||||||
// Next moves the iterator to the next node. If the parameter is false, any child
|
// Next moves the iterator to the next node. If the parameter is false, any child
|
||||||
// nodes will be skipped.
|
// nodes will be skipped.
|
||||||
Next(bool) bool
|
Next(bool) bool
|
||||||
|
|
||||||
// Error returns the error status of the iterator.
|
// Error returns the error status of the iterator.
|
||||||
Error() error
|
Error() error
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ type NodeIterator interface {
|
||||||
// trie, which can be resumed at a later invocation.
|
// trie, which can be resumed at a later invocation.
|
||||||
type nodeIteratorState struct {
|
type nodeIteratorState struct {
|
||||||
hash common.Hash // Hash of the node being iterated (nil if not standalone)
|
hash common.Hash // Hash of the node being iterated (nil if not standalone)
|
||||||
node node // Trie node being iterated
|
node Node // Trie node being iterated
|
||||||
parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
|
parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
|
||||||
index int // Child to be processed next
|
index int // Child to be processed next
|
||||||
pathlen int // Length of the path to this node
|
pathlen int // Length of the path to this node
|
||||||
|
|
@ -112,7 +112,7 @@ func (e seekError) Error() string {
|
||||||
return "seek error: " + e.err.Error()
|
return "seek error: " + e.err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNodeIterator(trie *Trie, start []byte) NodeIterator {
|
func NewNodeIterator(trie *Trie, start []byte) NodeIterator {
|
||||||
if trie.Hash() == emptyState {
|
if trie.Hash() == emptyState {
|
||||||
return new(nodeIterator)
|
return new(nodeIterator)
|
||||||
}
|
}
|
||||||
|
|
@ -141,7 +141,7 @@ func (it *nodeIterator) Leaf() bool {
|
||||||
|
|
||||||
func (it *nodeIterator) LeafBlob() []byte {
|
func (it *nodeIterator) LeafBlob() []byte {
|
||||||
if len(it.stack) > 0 {
|
if len(it.stack) > 0 {
|
||||||
if node, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
if node, ok := it.stack[len(it.stack)-1].node.(ValueNode); ok {
|
||||||
return []byte(node)
|
return []byte(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +150,7 @@ func (it *nodeIterator) LeafBlob() []byte {
|
||||||
|
|
||||||
func (it *nodeIterator) LeafKey() []byte {
|
func (it *nodeIterator) LeafKey() []byte {
|
||||||
if len(it.stack) > 0 {
|
if len(it.stack) > 0 {
|
||||||
if _, ok := it.stack[len(it.stack)-1].node.(valueNode); ok {
|
if _, ok := it.stack[len(it.stack)-1].node.(ValueNode); ok {
|
||||||
return hexToKeybytes(it.path)
|
return hexToKeybytes(it.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +250,7 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
|
func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
|
||||||
if hash, ok := st.node.(hashNode); ok {
|
if hash, ok := st.node.(HashNode); ok {
|
||||||
resolved, err := tr.resolveHash(hash, path)
|
resolved, err := tr.resolveHash(hash, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -263,12 +263,12 @@ func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
|
||||||
|
|
||||||
func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Hash) (*nodeIteratorState, []byte, bool) {
|
func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Hash) (*nodeIteratorState, []byte, bool) {
|
||||||
switch node := parent.node.(type) {
|
switch node := parent.node.(type) {
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
// Full node, move to the first non-nil child.
|
// Full Node, move to the first non-nil child.
|
||||||
for i := parent.index + 1; i < len(node.Children); i++ {
|
for i := parent.index + 1; i < len(node.Children); i++ {
|
||||||
child := node.Children[i]
|
child := node.Children[i]
|
||||||
if child != nil {
|
if child != nil {
|
||||||
hash, _ := child.cache()
|
hash, _ := child.Cache()
|
||||||
state := &nodeIteratorState{
|
state := &nodeIteratorState{
|
||||||
hash: common.BytesToHash(hash),
|
hash: common.BytesToHash(hash),
|
||||||
node: child,
|
node: child,
|
||||||
|
|
@ -281,10 +281,10 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has
|
||||||
return state, path, true
|
return state, path, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
// Short node, return the pointer singleton child
|
// Short node, return the pointer singleton child
|
||||||
if parent.index < 0 {
|
if parent.index < 0 {
|
||||||
hash, _ := node.Val.cache()
|
hash, _ := node.Val.Cache()
|
||||||
state := &nodeIteratorState{
|
state := &nodeIteratorState{
|
||||||
hash: common.BytesToHash(hash),
|
hash: common.BytesToHash(hash),
|
||||||
node: node.Val,
|
node: node.Val,
|
||||||
|
|
|
||||||
78
trie/node.go
78
trie/node.go
|
|
@ -27,63 +27,63 @@ import (
|
||||||
|
|
||||||
var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "[17]"}
|
var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "[17]"}
|
||||||
|
|
||||||
type node interface {
|
type Node interface {
|
||||||
fstring(string) string
|
fstring(string) string
|
||||||
cache() (hashNode, bool)
|
Cache() (HashNode, bool)
|
||||||
canUnload(cachegen, cachelimit uint16) bool
|
canUnload(cachegen, cachelimit uint16) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
fullNode struct {
|
FullNode struct {
|
||||||
Children [17]node // Actual trie node data to encode/decode (needs custom encoder)
|
Children [17]Node // Actual trie node data to encode/decode (needs custom encoder)
|
||||||
flags nodeFlag
|
flags nodeFlag
|
||||||
}
|
}
|
||||||
shortNode struct {
|
ShortNode struct {
|
||||||
Key []byte
|
Key []byte
|
||||||
Val node
|
Val Node
|
||||||
flags nodeFlag
|
flags nodeFlag
|
||||||
}
|
}
|
||||||
hashNode []byte
|
HashNode []byte
|
||||||
valueNode []byte
|
ValueNode []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncodeRLP encodes a full node into the consensus RLP format.
|
// EncodeRLP encodes a full node into the consensus RLP format.
|
||||||
func (n *fullNode) EncodeRLP(w io.Writer) error {
|
func (n *FullNode) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, n.Children)
|
return rlp.Encode(w, n.Children)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fullNode) copy() *fullNode { copy := *n; return © }
|
func (n *FullNode) copy() *FullNode { copy := *n; return © }
|
||||||
func (n *shortNode) copy() *shortNode { copy := *n; return © }
|
func (n *ShortNode) copy() *ShortNode { copy := *n; return © }
|
||||||
|
|
||||||
// nodeFlag contains caching-related metadata about a node.
|
// nodeFlag contains caching-related metadata about a node.
|
||||||
type nodeFlag struct {
|
type nodeFlag struct {
|
||||||
hash hashNode // cached hash of the node (may be nil)
|
hash HashNode // cached hash of the node (may be nil)
|
||||||
gen uint16 // cache generation counter
|
gen uint16 // cache generation counter
|
||||||
dirty bool // whether the node has changes that must be written to the database
|
dirty bool // whether the node has changes that must be written to the database
|
||||||
}
|
}
|
||||||
|
|
||||||
// canUnload tells whether a node can be unloaded.
|
// canUnload tells whether a Node can be unloaded.
|
||||||
func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool {
|
func (n *nodeFlag) canUnload(cachegen, cachelimit uint16) bool {
|
||||||
return !n.dirty && cachegen-n.gen >= cachelimit
|
return !n.dirty && cachegen-n.gen >= cachelimit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *fullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
|
func (n *FullNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
|
||||||
func (n *shortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
|
func (n *ShortNode) canUnload(gen, limit uint16) bool { return n.flags.canUnload(gen, limit) }
|
||||||
func (n hashNode) canUnload(uint16, uint16) bool { return false }
|
func (n HashNode) canUnload(uint16, uint16) bool { return false }
|
||||||
func (n valueNode) canUnload(uint16, uint16) bool { return false }
|
func (n ValueNode) canUnload(uint16, uint16) bool { return false }
|
||||||
|
|
||||||
func (n *fullNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
|
func (n *FullNode) Cache() (HashNode, bool) { return n.flags.hash, n.flags.dirty }
|
||||||
func (n *shortNode) cache() (hashNode, bool) { return n.flags.hash, n.flags.dirty }
|
func (n *ShortNode) Cache() (HashNode, bool) { return n.flags.hash, n.flags.dirty }
|
||||||
func (n hashNode) cache() (hashNode, bool) { return nil, true }
|
func (n HashNode) Cache() (HashNode, bool) { return nil, true }
|
||||||
func (n valueNode) cache() (hashNode, bool) { return nil, true }
|
func (n ValueNode) Cache() (HashNode, bool) { return nil, true }
|
||||||
|
|
||||||
// Pretty printing.
|
// Pretty printing.
|
||||||
func (n *fullNode) String() string { return n.fstring("") }
|
func (n *FullNode) String() string { return n.fstring("") }
|
||||||
func (n *shortNode) String() string { return n.fstring("") }
|
func (n *ShortNode) String() string { return n.fstring("") }
|
||||||
func (n hashNode) String() string { return n.fstring("") }
|
func (n HashNode) String() string { return n.fstring("") }
|
||||||
func (n valueNode) String() string { return n.fstring("") }
|
func (n ValueNode) String() string { return n.fstring("") }
|
||||||
|
|
||||||
func (n *fullNode) fstring(ind string) string {
|
func (n *FullNode) fstring(ind string) string {
|
||||||
resp := fmt.Sprintf("[\n%s ", ind)
|
resp := fmt.Sprintf("[\n%s ", ind)
|
||||||
for i, node := range n.Children {
|
for i, node := range n.Children {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
|
|
@ -94,17 +94,17 @@ func (n *fullNode) fstring(ind string) string {
|
||||||
}
|
}
|
||||||
return resp + fmt.Sprintf("\n%s] ", ind)
|
return resp + fmt.Sprintf("\n%s] ", ind)
|
||||||
}
|
}
|
||||||
func (n *shortNode) fstring(ind string) string {
|
func (n *ShortNode) fstring(ind string) string {
|
||||||
return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" "))
|
return fmt.Sprintf("{%x: %v} ", n.Key, n.Val.fstring(ind+" "))
|
||||||
}
|
}
|
||||||
func (n hashNode) fstring(ind string) string {
|
func (n HashNode) fstring(ind string) string {
|
||||||
return fmt.Sprintf("<%x> ", []byte(n))
|
return fmt.Sprintf("<%x> ", []byte(n))
|
||||||
}
|
}
|
||||||
func (n valueNode) fstring(ind string) string {
|
func (n ValueNode) fstring(ind string) string {
|
||||||
return fmt.Sprintf("%x ", []byte(n))
|
return fmt.Sprintf("%x ", []byte(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustDecodeNode(hash, buf []byte, cachegen uint16) node {
|
func MustDecodeNode(hash, buf []byte, cachegen uint16) Node {
|
||||||
n, err := decodeNode(hash, buf, cachegen)
|
n, err := decodeNode(hash, buf, cachegen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("node %x: %v", hash, err))
|
panic(fmt.Sprintf("node %x: %v", hash, err))
|
||||||
|
|
@ -113,7 +113,7 @@ func mustDecodeNode(hash, buf []byte, cachegen uint16) node {
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeNode parses the RLP encoding of a trie node.
|
// decodeNode parses the RLP encoding of a trie node.
|
||||||
func decodeNode(hash, buf []byte, cachegen uint16) (node, error) {
|
func decodeNode(hash, buf []byte, cachegen uint16) (Node, error) {
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ func decodeNode(hash, buf []byte, cachegen uint16) (node, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
|
func decodeShort(hash, buf, elems []byte, cachegen uint16) (Node, error) {
|
||||||
kbuf, rest, err := rlp.SplitString(elems)
|
kbuf, rest, err := rlp.SplitString(elems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -146,17 +146,17 @@ func decodeShort(hash, buf, elems []byte, cachegen uint16) (node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid value node: %v", err)
|
return nil, fmt.Errorf("invalid value node: %v", err)
|
||||||
}
|
}
|
||||||
return &shortNode{key, append(valueNode{}, val...), flag}, nil
|
return &ShortNode{key, append(ValueNode{}, val...), flag}, nil
|
||||||
}
|
}
|
||||||
r, _, err := decodeRef(rest, cachegen)
|
r, _, err := decodeRef(rest, cachegen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, wrapError(err, "val")
|
return nil, wrapError(err, "val")
|
||||||
}
|
}
|
||||||
return &shortNode{key, r, flag}, nil
|
return &ShortNode{key, r, flag}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) {
|
func decodeFull(hash, buf, elems []byte, cachegen uint16) (*FullNode, error) {
|
||||||
n := &fullNode{flags: nodeFlag{hash: hash, gen: cachegen}}
|
n := &FullNode{flags: nodeFlag{hash: hash, gen: cachegen}}
|
||||||
for i := 0; i < 16; i++ {
|
for i := 0; i < 16; i++ {
|
||||||
cld, rest, err := decodeRef(elems, cachegen)
|
cld, rest, err := decodeRef(elems, cachegen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -169,14 +169,14 @@ func decodeFull(hash, buf, elems []byte, cachegen uint16) (*fullNode, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if len(val) > 0 {
|
if len(val) > 0 {
|
||||||
n.Children[16] = append(valueNode{}, val...)
|
n.Children[16] = append(ValueNode{}, val...)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashLen = len(common.Hash{})
|
const hashLen = len(common.Hash{})
|
||||||
|
|
||||||
func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) {
|
func decodeRef(buf []byte, cachegen uint16) (Node, []byte, error) {
|
||||||
kind, val, rest, err := rlp.Split(buf)
|
kind, val, rest, err := rlp.Split(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, buf, err
|
return nil, buf, err
|
||||||
|
|
@ -195,7 +195,7 @@ func decodeRef(buf []byte, cachegen uint16) (node, []byte, error) {
|
||||||
// empty node
|
// empty node
|
||||||
return nil, rest, nil
|
return nil, rest, nil
|
||||||
case kind == rlp.String && len(val) == 32:
|
case kind == rlp.String && len(val) == 32:
|
||||||
return append(hashNode{}, val...), rest, nil
|
return append(HashNode{}, val...), rest, nil
|
||||||
default:
|
default:
|
||||||
return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val))
|
return nil, nil, fmt.Errorf("invalid RLP string size %d (want 0 or 32)", len(val))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@ import (
|
||||||
func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
||||||
// Collect all nodes on the path to key.
|
// Collect all nodes on the path to key.
|
||||||
key = keybytesToHex(key)
|
key = keybytesToHex(key)
|
||||||
nodes := []node{}
|
nodes := []Node{}
|
||||||
tn := t.root
|
tn := t.root
|
||||||
for len(key) > 0 && tn != nil {
|
for len(key) > 0 && tn != nil {
|
||||||
switch n := tn.(type) {
|
switch n := tn.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
||||||
// The trie doesn't contain the key.
|
// The trie doesn't contain the key.
|
||||||
tn = nil
|
tn = nil
|
||||||
|
|
@ -50,11 +50,11 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
||||||
key = key[len(n.Key):]
|
key = key[len(n.Key):]
|
||||||
}
|
}
|
||||||
nodes = append(nodes, n)
|
nodes = append(nodes, n)
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
tn = n.Children[key[0]]
|
tn = n.Children[key[0]]
|
||||||
key = key[1:]
|
key = key[1:]
|
||||||
nodes = append(nodes, n)
|
nodes = append(nodes, n)
|
||||||
case hashNode:
|
case HashNode:
|
||||||
var err error
|
var err error
|
||||||
tn, err = t.resolveHash(n, nil)
|
tn, err = t.resolveHash(n, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -71,7 +71,7 @@ func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
|
||||||
// if encoding doesn't work and we're not writing to any database.
|
// if encoding doesn't work and we're not writing to any database.
|
||||||
n, _, _ = hasher.hashChildren(n, nil)
|
n, _, _ = hasher.hashChildren(n, nil)
|
||||||
hn, _ := hasher.store(n, nil, false)
|
hn, _ := hasher.store(n, nil, false)
|
||||||
if hash, ok := hn.(hashNode); ok || i == 0 {
|
if hash, ok := hn.(HashNode); ok || i == 0 {
|
||||||
// If the node's database encoding is a hash (or is the
|
// If the node's database encoding is a hash (or is the
|
||||||
// root node), it becomes a proof element.
|
// root node), it becomes a proof element.
|
||||||
if fromLevel > 0 {
|
if fromLevel > 0 {
|
||||||
|
|
@ -119,32 +119,32 @@ func VerifyProof(rootHash common.Hash, key []byte, proofDb DatabaseReader) (valu
|
||||||
case nil:
|
case nil:
|
||||||
// The trie doesn't contain the key.
|
// The trie doesn't contain the key.
|
||||||
return nil, nil, i
|
return nil, nil, i
|
||||||
case hashNode:
|
case HashNode:
|
||||||
key = keyrest
|
key = keyrest
|
||||||
copy(wantHash[:], cld)
|
copy(wantHash[:], cld)
|
||||||
case valueNode:
|
case ValueNode:
|
||||||
return cld, nil, i + 1
|
return cld, nil, i + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(tn node, key []byte) ([]byte, node) {
|
func get(tn Node, key []byte) ([]byte, Node) {
|
||||||
for {
|
for {
|
||||||
switch n := tn.(type) {
|
switch n := tn.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
if len(key) < len(n.Key) || !bytes.Equal(n.Key, key[:len(n.Key)]) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
tn = n.Val
|
tn = n.Val
|
||||||
key = key[len(n.Key):]
|
key = key[len(n.Key):]
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
tn = n.Children[key[0]]
|
tn = n.Children[key[0]]
|
||||||
key = key[1:]
|
key = key[1:]
|
||||||
case hashNode:
|
case HashNode:
|
||||||
return key, n
|
return key, n
|
||||||
case nil:
|
case nil:
|
||||||
return key, nil
|
return key, nil
|
||||||
case valueNode:
|
case ValueNode:
|
||||||
return nil, n
|
return nil, n
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
|
||||||
|
|
|
||||||
12
trie/sync.go
12
trie/sync.go
|
|
@ -248,21 +248,21 @@ func (s *TrieSync) schedule(req *request) {
|
||||||
|
|
||||||
// children retrieves all the missing children of a state trie entry for future
|
// children retrieves all the missing children of a state trie entry for future
|
||||||
// retrieval scheduling.
|
// retrieval scheduling.
|
||||||
func (s *TrieSync) children(req *request, object node) ([]*request, error) {
|
func (s *TrieSync) children(req *request, object Node) ([]*request, error) {
|
||||||
// Gather all the children of the node, irrelevant whether known or not
|
// Gather all the children of the node, irrelevant whether known or not
|
||||||
type child struct {
|
type child struct {
|
||||||
node node
|
node Node
|
||||||
depth int
|
depth int
|
||||||
}
|
}
|
||||||
children := []child{}
|
children := []child{}
|
||||||
|
|
||||||
switch node := (object).(type) {
|
switch node := (object).(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
children = []child{{
|
children = []child{{
|
||||||
node: node.Val,
|
node: node.Val,
|
||||||
depth: req.depth + len(node.Key),
|
depth: req.depth + len(node.Key),
|
||||||
}}
|
}}
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
for i := 0; i < 17; i++ {
|
for i := 0; i < 17; i++ {
|
||||||
if node.Children[i] != nil {
|
if node.Children[i] != nil {
|
||||||
children = append(children, child{
|
children = append(children, child{
|
||||||
|
|
@ -279,14 +279,14 @@ func (s *TrieSync) children(req *request, object node) ([]*request, error) {
|
||||||
for _, child := range children {
|
for _, child := range children {
|
||||||
// Notify any external watcher of a new key/value node
|
// Notify any external watcher of a new key/value node
|
||||||
if req.callback != nil {
|
if req.callback != nil {
|
||||||
if node, ok := (child.node).(valueNode); ok {
|
if node, ok := (child.node).(ValueNode); ok {
|
||||||
if err := req.callback(node, req.hash); err != nil {
|
if err := req.callback(node, req.hash); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the child references another node, resolve or schedule
|
// If the child references another node, resolve or schedule
|
||||||
if node, ok := (child.node).(hashNode); ok {
|
if node, ok := (child.node).(HashNode); ok {
|
||||||
// Try to resolve the node from the local database
|
// Try to resolve the node from the local database
|
||||||
hash := common.BytesToHash(node)
|
hash := common.BytesToHash(node)
|
||||||
if _, ok := s.membatch.batch[hash]; ok {
|
if _, ok := s.membatch.batch[hash]; ok {
|
||||||
|
|
|
||||||
76
trie/trie.go
76
trie/trie.go
|
|
@ -66,7 +66,7 @@ type LeafCallback func(leaf []byte, parent common.Hash) error
|
||||||
// Trie is not safe for concurrent use.
|
// Trie is not safe for concurrent use.
|
||||||
type Trie struct {
|
type Trie struct {
|
||||||
db *Database
|
db *Database
|
||||||
root node
|
root Node
|
||||||
originalRoot common.Hash
|
originalRoot common.Hash
|
||||||
|
|
||||||
// Cache generation values.
|
// Cache generation values.
|
||||||
|
|
@ -114,7 +114,7 @@ func New(root common.Hash, db *Database) (*Trie, error) {
|
||||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at
|
// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at
|
||||||
// the key after the given start key.
|
// the key after the given start key.
|
||||||
func (t *Trie) NodeIterator(start []byte) NodeIterator {
|
func (t *Trie) NodeIterator(start []byte) NodeIterator {
|
||||||
return newNodeIterator(t, start)
|
return NewNodeIterator(t, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value for key stored in the trie.
|
// Get returns the value for key stored in the trie.
|
||||||
|
|
@ -139,13 +139,13 @@ func (t *Trie) TryGet(key []byte) ([]byte, error) {
|
||||||
return value, err
|
return value, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode node, didResolve bool, err error) {
|
func (t *Trie) tryGet(origNode Node, key []byte, pos int) (value []byte, newnode Node, didResolve bool, err error) {
|
||||||
switch n := (origNode).(type) {
|
switch n := (origNode).(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return nil, nil, false, nil
|
return nil, nil, false, nil
|
||||||
case valueNode:
|
case ValueNode:
|
||||||
return n, n, false, nil
|
return n, n, false, nil
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
|
if len(key)-pos < len(n.Key) || !bytes.Equal(n.Key, key[pos:pos+len(n.Key)]) {
|
||||||
// key not found in trie
|
// key not found in trie
|
||||||
return nil, n, false, nil
|
return nil, n, false, nil
|
||||||
|
|
@ -157,7 +157,7 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode
|
||||||
n.flags.gen = t.cachegen
|
n.flags.gen = t.cachegen
|
||||||
}
|
}
|
||||||
return value, n, didResolve, err
|
return value, n, didResolve, err
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1)
|
value, newnode, didResolve, err = t.tryGet(n.Children[key[pos]], key, pos+1)
|
||||||
if err == nil && didResolve {
|
if err == nil && didResolve {
|
||||||
n = n.copy()
|
n = n.copy()
|
||||||
|
|
@ -165,7 +165,7 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode
|
||||||
n.Children[key[pos]] = newnode
|
n.Children[key[pos]] = newnode
|
||||||
}
|
}
|
||||||
return value, n, didResolve, err
|
return value, n, didResolve, err
|
||||||
case hashNode:
|
case HashNode:
|
||||||
child, err := t.resolveHash(n, key[:pos])
|
child, err := t.resolveHash(n, key[:pos])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, n, true, err
|
return nil, n, true, err
|
||||||
|
|
@ -200,7 +200,7 @@ func (t *Trie) Update(key, value []byte) {
|
||||||
func (t *Trie) TryUpdate(key, value []byte) error {
|
func (t *Trie) TryUpdate(key, value []byte) error {
|
||||||
k := keybytesToHex(key)
|
k := keybytesToHex(key)
|
||||||
if len(value) != 0 {
|
if len(value) != 0 {
|
||||||
_, n, err := t.insert(t.root, nil, k, valueNode(value))
|
_, n, err := t.insert(t.root, nil, k, ValueNode(value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -215,15 +215,15 @@ func (t *Trie) TryUpdate(key, value []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error) {
|
func (t *Trie) insert(n Node, prefix, key []byte, value Node) (bool, Node, error) {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
if v, ok := n.(valueNode); ok {
|
if v, ok := n.(ValueNode); ok {
|
||||||
return !bytes.Equal(v, value.(valueNode)), value, nil
|
return !bytes.Equal(v, value.(ValueNode)), value, nil
|
||||||
}
|
}
|
||||||
return true, value, nil
|
return true, value, nil
|
||||||
}
|
}
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
matchlen := prefixLen(key, n.Key)
|
matchlen := prefixLen(key, n.Key)
|
||||||
// If the whole key matches, keep this short node as is
|
// If the whole key matches, keep this short node as is
|
||||||
// and only update the value.
|
// and only update the value.
|
||||||
|
|
@ -232,10 +232,10 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
|
||||||
if !dirty || err != nil {
|
if !dirty || err != nil {
|
||||||
return false, n, err
|
return false, n, err
|
||||||
}
|
}
|
||||||
return true, &shortNode{n.Key, nn, t.newFlag()}, nil
|
return true, &ShortNode{n.Key, nn, t.newFlag()}, nil
|
||||||
}
|
}
|
||||||
// Otherwise branch out at the index where they differ.
|
// Otherwise branch out at the index where they differ.
|
||||||
branch := &fullNode{flags: t.newFlag()}
|
branch := &FullNode{flags: t.newFlag()}
|
||||||
var err error
|
var err error
|
||||||
_, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val)
|
_, branch.Children[n.Key[matchlen]], err = t.insert(nil, append(prefix, n.Key[:matchlen+1]...), n.Key[matchlen+1:], n.Val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -250,9 +250,9 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
|
||||||
return true, branch, nil
|
return true, branch, nil
|
||||||
}
|
}
|
||||||
// Otherwise, replace it with a short node leading up to the branch.
|
// Otherwise, replace it with a short node leading up to the branch.
|
||||||
return true, &shortNode{key[:matchlen], branch, t.newFlag()}, nil
|
return true, &ShortNode{key[:matchlen], branch, t.newFlag()}, nil
|
||||||
|
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value)
|
dirty, nn, err := t.insert(n.Children[key[0]], append(prefix, key[0]), key[1:], value)
|
||||||
if !dirty || err != nil {
|
if !dirty || err != nil {
|
||||||
return false, n, err
|
return false, n, err
|
||||||
|
|
@ -263,9 +263,9 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
|
||||||
return true, n, nil
|
return true, n, nil
|
||||||
|
|
||||||
case nil:
|
case nil:
|
||||||
return true, &shortNode{key, value, t.newFlag()}, nil
|
return true, &ShortNode{key, value, t.newFlag()}, nil
|
||||||
|
|
||||||
case hashNode:
|
case HashNode:
|
||||||
// We've hit a part of the trie that isn't loaded yet. Load
|
// We've hit a part of the trie that isn't loaded yet. Load
|
||||||
// the node and insert into it. This leaves all child nodes on
|
// the node and insert into it. This leaves all child nodes on
|
||||||
// the path to the value in the trie.
|
// the path to the value in the trie.
|
||||||
|
|
@ -306,9 +306,9 @@ func (t *Trie) TryDelete(key []byte) error {
|
||||||
// delete returns the new root of the trie with key deleted.
|
// delete returns the new root of the trie with key deleted.
|
||||||
// It reduces the trie to minimal form by simplifying
|
// It reduces the trie to minimal form by simplifying
|
||||||
// nodes on the way up after deleting recursively.
|
// nodes on the way up after deleting recursively.
|
||||||
func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
|
func (t *Trie) delete(n Node, prefix, key []byte) (bool, Node, error) {
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
matchlen := prefixLen(key, n.Key)
|
matchlen := prefixLen(key, n.Key)
|
||||||
if matchlen < len(n.Key) {
|
if matchlen < len(n.Key) {
|
||||||
return false, n, nil // don't replace n on mismatch
|
return false, n, nil // don't replace n on mismatch
|
||||||
|
|
@ -325,19 +325,19 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
|
||||||
return false, n, err
|
return false, n, err
|
||||||
}
|
}
|
||||||
switch child := child.(type) {
|
switch child := child.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
// Deleting from the subtrie reduced it to another
|
// Deleting from the subtrie reduced it to another
|
||||||
// short node. Merge the nodes to avoid creating a
|
// short node. Merge the nodes to avoid creating a
|
||||||
// shortNode{..., shortNode{...}}. Use concat (which
|
// shortNode{..., shortNode{...}}. Use concat (which
|
||||||
// always creates a new slice) instead of append to
|
// always creates a new slice) instead of append to
|
||||||
// avoid modifying n.Key since it might be shared with
|
// avoid modifying n.Key since it might be shared with
|
||||||
// other nodes.
|
// other nodes.
|
||||||
return true, &shortNode{concat(n.Key, child.Key...), child.Val, t.newFlag()}, nil
|
return true, &ShortNode{concat(n.Key, child.Key...), child.Val, t.newFlag()}, nil
|
||||||
default:
|
default:
|
||||||
return true, &shortNode{n.Key, child, t.newFlag()}, nil
|
return true, &ShortNode{n.Key, child, t.newFlag()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
dirty, nn, err := t.delete(n.Children[key[0]], append(prefix, key[0]), key[1:])
|
dirty, nn, err := t.delete(n.Children[key[0]], append(prefix, key[0]), key[1:])
|
||||||
if !dirty || err != nil {
|
if !dirty || err != nil {
|
||||||
return false, n, err
|
return false, n, err
|
||||||
|
|
@ -368,7 +368,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
|
||||||
}
|
}
|
||||||
if pos >= 0 {
|
if pos >= 0 {
|
||||||
if pos != 16 {
|
if pos != 16 {
|
||||||
// If the remaining entry is a short node, it replaces
|
// If the remaining entry is a short node, it replaces
|
||||||
// n and its key gets the missing nibble tacked to the
|
// n and its key gets the missing nibble tacked to the
|
||||||
// front. This avoids creating an invalid
|
// front. This avoids creating an invalid
|
||||||
// shortNode{..., shortNode{...}}. Since the entry
|
// shortNode{..., shortNode{...}}. Since the entry
|
||||||
|
|
@ -378,25 +378,25 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, err
|
return false, nil, err
|
||||||
}
|
}
|
||||||
if cnode, ok := cnode.(*shortNode); ok {
|
if cnode, ok := cnode.(*ShortNode); ok {
|
||||||
k := append([]byte{byte(pos)}, cnode.Key...)
|
k := append([]byte{byte(pos)}, cnode.Key...)
|
||||||
return true, &shortNode{k, cnode.Val, t.newFlag()}, nil
|
return true, &ShortNode{k, cnode.Val, t.newFlag()}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, n is replaced by a one-nibble short node
|
// Otherwise, n is replaced by a one-nibble short node
|
||||||
// containing the child.
|
// containing the child.
|
||||||
return true, &shortNode{[]byte{byte(pos)}, n.Children[pos], t.newFlag()}, nil
|
return true, &ShortNode{[]byte{byte(pos)}, n.Children[pos], t.newFlag()}, nil
|
||||||
}
|
}
|
||||||
// n still contains at least two values and cannot be reduced.
|
// n still contains at least two values and cannot be reduced.
|
||||||
return true, n, nil
|
return true, n, nil
|
||||||
|
|
||||||
case valueNode:
|
case ValueNode:
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
|
|
||||||
case nil:
|
case nil:
|
||||||
return false, nil, nil
|
return false, nil, nil
|
||||||
|
|
||||||
case hashNode:
|
case HashNode:
|
||||||
// We've hit a part of the trie that isn't loaded yet. Load
|
// We've hit a part of the trie that isn't loaded yet. Load
|
||||||
// the node and delete from it. This leaves all child nodes on
|
// the node and delete from it. This leaves all child nodes on
|
||||||
// the path to the value in the trie.
|
// the path to the value in the trie.
|
||||||
|
|
@ -422,14 +422,14 @@ func concat(s1 []byte, s2 ...byte) []byte {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) resolve(n node, prefix []byte) (node, error) {
|
func (t *Trie) resolve(n Node, prefix []byte) (Node, error) {
|
||||||
if n, ok := n.(hashNode); ok {
|
if n, ok := n.(HashNode); ok {
|
||||||
return t.resolveHash(n, prefix)
|
return t.resolveHash(n, prefix)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
|
func (t *Trie) resolveHash(n HashNode, prefix []byte) (Node, error) {
|
||||||
cacheMissCounter.Inc(1)
|
cacheMissCounter.Inc(1)
|
||||||
|
|
||||||
hash := common.BytesToHash(n)
|
hash := common.BytesToHash(n)
|
||||||
|
|
@ -438,7 +438,7 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
|
||||||
if err != nil || enc == nil {
|
if err != nil || enc == nil {
|
||||||
return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
|
return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
|
||||||
}
|
}
|
||||||
return mustDecodeNode(n, enc, t.cachegen), nil
|
return MustDecodeNode(n, enc, t.cachegen), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root returns the root hash of the trie.
|
// Root returns the root hash of the trie.
|
||||||
|
|
@ -450,7 +450,7 @@ func (t *Trie) Root() []byte { return t.Hash().Bytes() }
|
||||||
func (t *Trie) Hash() common.Hash {
|
func (t *Trie) Hash() common.Hash {
|
||||||
hash, cached, _ := t.hashRoot(nil, nil)
|
hash, cached, _ := t.hashRoot(nil, nil)
|
||||||
t.root = cached
|
t.root = cached
|
||||||
return common.BytesToHash(hash.(hashNode))
|
return common.BytesToHash(hash.(HashNode))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit writes all nodes to the trie's memory database, tracking the internal
|
// Commit writes all nodes to the trie's memory database, tracking the internal
|
||||||
|
|
@ -465,12 +465,12 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
|
||||||
}
|
}
|
||||||
t.root = cached
|
t.root = cached
|
||||||
t.cachegen++
|
t.cachegen++
|
||||||
return common.BytesToHash(hash.(hashNode)), nil
|
return common.BytesToHash(hash.(HashNode)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Trie) hashRoot(db *Database, onleaf LeafCallback) (node, node, error) {
|
func (t *Trie) hashRoot(db *Database, onleaf LeafCallback) (Node, Node, error) {
|
||||||
if t.root == nil {
|
if t.root == nil {
|
||||||
return hashNode(emptyRoot.Bytes()), nil, nil
|
return HashNode(emptyRoot.Bytes()), nil, nil
|
||||||
}
|
}
|
||||||
h := newHasher(t.cachegen, t.cachelimit, onleaf)
|
h := newHasher(t.cachegen, t.cachelimit, onleaf)
|
||||||
defer returnHasherToPool(h)
|
defer returnHasherToPool(h)
|
||||||
|
|
|
||||||
|
|
@ -469,14 +469,14 @@ func runRandTest(rt randTest) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCacheInvariant(n, parent node, parentCachegen uint16, parentDirty bool, depth int) error {
|
func checkCacheInvariant(n, parent Node, parentCachegen uint16, parentDirty bool, depth int) error {
|
||||||
var children []node
|
var children []Node
|
||||||
var flag nodeFlag
|
var flag nodeFlag
|
||||||
switch n := n.(type) {
|
switch n := n.(type) {
|
||||||
case *shortNode:
|
case *ShortNode:
|
||||||
flag = n.flags
|
flag = n.flags
|
||||||
children = []node{n.Val}
|
children = []Node{n.Val}
|
||||||
case *fullNode:
|
case *FullNode:
|
||||||
flag = n.flags
|
flag = n.flags
|
||||||
children = n.Children[:]
|
children = n.Children[:]
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue