Skip to content

Commit

Permalink
fast node verification
Browse files Browse the repository at this point in the history
Signed-off-by: kyrie-yl <yl.on.the.way@gmail.com>
  • Loading branch information
kyrie-yl committed Jan 20, 2022
1 parent 80fd4dd commit 2e0530d
Show file tree
Hide file tree
Showing 14 changed files with 463 additions and 25 deletions.
4 changes: 3 additions & 1 deletion cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ var (
utils.NoUSBFlag,
utils.DirectBroadcastFlag,
utils.DisableSnapProtocolFlag,
utils.DisableDiffProtocolFlag,
utils.EnableTrustProtocolFlag,
utils.DiffSyncFlag,
utils.RangeLimitFlag,
utils.USBFlag,
Expand All @@ -97,6 +99,7 @@ var (
utils.TxPoolLifetimeFlag,
utils.TxPoolReannounceTimeFlag,
utils.SyncModeFlag,
utils.TriesVerifyModeFlag,
utils.ExitWhenSyncedFlag,
utils.GCModeFlag,
utils.SnapshotFlag,
Expand All @@ -114,7 +117,6 @@ var (
utils.WhitelistFlag,
utils.BloomFilterSizeFlag,
utils.TriesInMemoryFlag,
utils.AllowInsecureNoTriesFlag,
utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheTrieFlag,
Expand Down
3 changes: 3 additions & 0 deletions cmd/geth/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.NoUSBFlag,
utils.DirectBroadcastFlag,
utils.DisableSnapProtocolFlag,
utils.DisableDiffProtocolFlag,
utils.EnableTrustProtocolFlag,
utils.RangeLimitFlag,
utils.SmartCardDaemonPathFlag,
utils.NetworkIdFlag,
Expand All @@ -50,6 +52,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.YoloV3Flag,
utils.RopstenFlag,
utils.SyncModeFlag,
utils.TriesVerifyModeFlag,
utils.ExitWhenSyncedFlag,
utils.GCModeFlag,
utils.TxLookupLimitFlag,
Expand Down
32 changes: 27 additions & 5 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,14 @@ var (
Name: "disablesnapprotocol",
Usage: "Disable snap protocol",
}
DisableDiffProtocolFlag = cli.BoolFlag{
Name: "disablediffprotocol",
Usage: "Disable diff protocol",
}
EnableTrustProtocolFlag = cli.BoolFlag{
Name: "enabletrustprotocol",
Usage: "Enable trust protocol",
}
DiffSyncFlag = cli.BoolFlag{
Name: "diffsync",
Usage: "Enable diffy sync, Please note that enable diffsync will improve the syncing speed, " +
Expand Down Expand Up @@ -259,9 +267,11 @@ var (
Usage: "The layer of tries trees that keep in memory",
Value: 128,
}
AllowInsecureNoTriesFlag = cli.BoolTFlag{
Name: "allow-insecure-no-tries",
Usage: `Disable the tries state root verification, the state consistency is no longer 100% guaranteed, diffsync is not allowed if enabled. Do not enable it unless you know exactly what the consequence it will cause.`,
defaultVerifyMode = ethconfig.Defaults.TriesVerifyMode
TriesVerifyModeFlag = TextMarshalerFlag{
Name: "tries-verify-mode",
Usage: `tries verify mode: "local", "full", "light", "insecure"`,
Value: &defaultVerifyMode,
}
OverrideBerlinFlag = cli.Uint64Flag{
Name: "override.berlin",
Expand Down Expand Up @@ -1622,6 +1632,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.GlobalIsSet(DisableSnapProtocolFlag.Name) {
cfg.DisableSnapProtocol = ctx.GlobalBool(DisableSnapProtocolFlag.Name)
}
if ctx.GlobalIsSet(DisableDiffProtocolFlag.Name) {
cfg.DisableDiffProtocol = ctx.GlobalIsSet(DisableDiffProtocolFlag.Name)
}
if ctx.GlobalIsSet(EnableTrustProtocolFlag.Name) {
cfg.EnableTrustProtocol = ctx.GlobalIsSet(EnableTrustProtocolFlag.Name)
}
if ctx.GlobalIsSet(DiffSyncFlag.Name) {
cfg.DiffSync = ctx.GlobalBool(DiffSyncFlag.Name)
}
Expand Down Expand Up @@ -1652,8 +1668,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.GlobalIsSet(TriesInMemoryFlag.Name) {
cfg.TriesInMemory = ctx.GlobalUint64(TriesInMemoryFlag.Name)
}
if ctx.GlobalIsSet(AllowInsecureNoTriesFlag.Name) {
cfg.NoTries = ctx.GlobalBool(AllowInsecureNoTriesFlag.Name)
if ctx.GlobalIsSet(TriesVerifyModeFlag.Name) {
cfg.TriesVerifyMode = *GlobalTextMarshaler(ctx, TriesVerifyModeFlag.Name).(*core.VerifyMode)
if cfg.TriesVerifyMode == core.FullVerify || cfg.TriesVerifyMode == core.LightVerify {
cfg.EnableTrustProtocol = true
}
if cfg.TriesVerifyMode != core.LocalVerify {
cfg.DisableDiffProtocol = true
}
}
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) {
cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100
Expand Down
24 changes: 24 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ type BlockChain struct {
engine consensus.Engine
validator Validator // Block and state validator interface
processor Processor // Block transaction processor interface
verifyManager *VerifyManager
vmConfig vm.Config

shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
Expand Down Expand Up @@ -462,6 +463,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
return bc, nil
}

func (bc *BlockChain) StartVerify(peers VerifyPeers, allowUntrustedVerify bool) {
bc.verifyManager.peers = peers
bc.verifyManager.allowUntrustedVerify = allowUntrustedVerify
bc.verifyManager.Start()
}

// GetVMConfig returns the block chain VM config.
func (bc *BlockChain) GetVMConfig() *vm.Config {
return &bc.vmConfig
Expand Down Expand Up @@ -1191,6 +1198,7 @@ func (bc *BlockChain) Stop() {
close(bc.quit)
bc.StopInsert()
bc.wg.Wait()
bc.verifyManager.Stop()

// Ensure that the entirety of the state snapshot is journalled to disk.
var snapBase common.Hash
Expand Down Expand Up @@ -2009,6 +2017,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
log.Debug("Abort during block processing")
break
}
if bc.verifyManager != nil {
for ; !bc.verifyManager.CheckAncestorVerified(block.Header()); {
log.Debug("Block ancestor has not been verified", "hash", block.Hash(), "number", block.Number())
}
}
// If the header is a banned one, straight out abort
if BadHashes[block.Hash()] {
bc.reportBlock(block, nil, ErrBlacklistedHash)
Expand Down Expand Up @@ -2052,6 +2065,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
lastCanon = block
continue
}

if bc.verifyManager != nil {
bc.verifyManager.NewBlockVerifyTask(block.Header())
}
// Retrieve the parent block and it's state to execute on top
start := time.Now()

Expand Down Expand Up @@ -3020,6 +3037,13 @@ func EnablePersistDiff(limit uint64) BlockChainOption {
}
}

func EnableVerifyManager() BlockChainOption {
return func(chain *BlockChain) *BlockChain {
chain.verifyManager = NewVerifyManager(chain)
return chain
}
}

func (bc *BlockChain) GetRootByDiffHash(blockNumber uint64, blockHash common.Hash, diffHash common.Hash) (*types.VerifyResult, error) {
var res types.VerifyResult
res.BlockNumber = blockNumber
Expand Down
20 changes: 20 additions & 0 deletions core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ func DeleteCanonicalHash(db ethdb.KeyValueWriter, number uint64) {
}
}

func ReadTrustBlockHash(db ethdb.Reader, hash common.Hash) bool {
data, _ := db.Get(trustBlockHashKey(hash))
if len(data) == 0 {
return false
}
return bytes.Equal(data,[]byte{0x01})
}

func WriteTrustBlockHash(db ethdb.KeyValueWriter, hashkey common.Hash) {
if err := db.Put(trustBlockHashKey(hashkey),[]byte{0x01}); err != nil {
log.Crit("Failed to store trust block hash")
}
}

func DeleteTrustBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(trustBlockHashKey(hash)); err != nil {
log.Crit("Failed to delete trust block hash")
}
}

// ReadAllHashes retrieves all the hashes assigned to blocks at a certain heights,
// both canonical and reorged forks included.
func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
Expand Down
7 changes: 7 additions & 0 deletions core/rawdb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ var (
// difflayer database
diffLayerPrefix = []byte("d") // diffLayerPrefix + hash -> diffLayer

// trust block database
trustBlockPrefix = []byte("tb") // trustBlockPrefix + hash -> verify result

preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
configPrefix = []byte("ethereum-config-") // config prefix for the db

Expand Down Expand Up @@ -164,6 +167,10 @@ func headerTDKey(number uint64, hash common.Hash) []byte {
func headerHashKey(number uint64) []byte {
return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
}
// trustBlockHashKey = trustBlockPrefix + hash
func trustBlockHashKey(hash common.Hash) []byte {
return append(append(trustBlockPrefix, hash.Bytes()...))
}

// headerNumberKey = headerNumberPrefix + hash
func headerNumberKey(hash common.Hash) []byte {
Expand Down
133 changes: 133 additions & 0 deletions core/verify_manager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package core

import (
"fmt"
"time"

lru "github.com/hashicorp/golang-lru"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)

const verifiedCacheSize = 256

type VerifyManager struct {
bc *BlockChain
tasks map[common.Hash]*VerifyTask
peers VerifyPeers
verifiedCache *lru.Cache
allowUntrustedVerify bool
verifyCh chan common.Hash
exitCh chan struct{}
}

func NewVerifyManager(blockchain *BlockChain) *VerifyManager {
verifiedCache, _ := lru.New(verifiedCacheSize)
vm := &VerifyManager{
bc: blockchain,
tasks: make(map[common.Hash]*VerifyTask),
verifiedCache: verifiedCache,
verifyCh: make(chan common.Hash),
exitCh: make(chan struct{}),
}
return vm
}

func (vm *VerifyManager) Start() {
//read disk store to initial verified cache
//load unverified blocks in a normalized chain and start a batch of verify task
header := vm.bc.CurrentHeader()
vm.NewBlockVerifyTask(header)
go vm.mainLoop()
}

func (vm *VerifyManager) Stop() {
defer close(vm.exitCh)
vm.exitCh <- struct{}{}
}

func (vm *VerifyManager) mainLoop() {
pruneTicker := time.NewTicker(time.Second)
for {
select {
case hash := <-vm.verifyCh:
vm.cacheBlockVerified(hash)
rawdb.WriteTrustBlockHash(vm.bc.db, hash)
delete(vm.tasks, hash)
case <-pruneTicker.C:
for hash, task := range vm.tasks {
if vm.bc.CurrentHeader().Number.Uint64()-task.blockHeader.Number.Uint64() > 15 {
task.terminalCh <- struct{}{}
delete(vm.tasks, hash)
}
}
case <-vm.exitCh:
return
}
}
}

func (vm *VerifyManager) NewBlockVerifyTask(header *types.Header) {
for i := 0; i < 11; i++ {
hash := header.Hash()
diffLayer := vm.bc.GetTrustedDiffLayer(hash)
//if this block has no diff, there is no need to verify it.
if diffLayer == nil {
continue
}
//if verified cache record that this block has been verified, skip.
if _, ok := vm.verifiedCache.Get(hash); ok {
continue
}
//if verified storage record that this block has been verified, skip.
if rawdb.ReadTrustBlockHash(vm.bc.db, hash) {
vm.cacheBlockVerified(hash)
continue
}
//if there already has a verify task for this block, skip.
if _, ok := vm.tasks[hash]; ok {
continue
}
diffHash, err := GetTrustedDiffHash(diffLayer)
if err != nil {
log.Error("failed to get diff hash", "block", hash, "number", header.Number, "error:", err)
}
verifyTask := NewVerifyTask(diffHash, header, vm.peers, vm.bc.db, vm.verifyCh, vm.allowUntrustedVerify)
vm.tasks[hash] = verifyTask
header = vm.bc.GetHeaderByHash(header.ParentHash)
}
}

func (vm *VerifyManager) cacheBlockVerified(hash common.Hash) {
if vm.verifiedCache.Len() >= verifiedCacheSize {
vm.verifiedCache.RemoveOldest()
}
vm.verifiedCache.Add(hash, true)
}

//CheckAncestorVerified function check whether head 11 of this block has been verified.
//If not, the blockchain should stop to insert new block.
func (vm *VerifyManager) CheckAncestorVerified(header *types.Header) bool {
pHeader := header
for i := 0; i < 11; i++ {
pHeader = vm.bc.GetHeaderByHash(pHeader.ParentHash)
}
hash := pHeader.Hash()
if _, ok := vm.verifiedCache.Get(hash); ok {
return true
} else {
return rawdb.ReadTrustBlockHash(vm.bc.db, hash)
}
return false
}

func (vm *VerifyManager) HandleRootResponse(vr *types.VerifyResult, pid string) error {
if vt, ok := vm.tasks[vr.BlockHash]; ok {
vt.messageCh <- VerifyMessage{verifyResult: vr, peerId: pid}
return nil
}
return fmt.Errorf("")
}
Loading

0 comments on commit 2e0530d

Please sign in to comment.