diff --git a/cmd/geth/main.go b/cmd/geth/main.go index db00cf19e1..97b3b5b268 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -71,6 +71,8 @@ var ( utils.NoUSBFlag, utils.DirectBroadcastFlag, utils.DisableSnapProtocolFlag, + utils.DisableDiffProtocolFlag, + utils.EnableTrustProtocolFlag, utils.DiffSyncFlag, utils.PipeCommitFlag, utils.RangeLimitFlag, @@ -98,6 +100,7 @@ var ( utils.TxPoolLifetimeFlag, utils.TxPoolReannounceTimeFlag, utils.SyncModeFlag, + utils.TriesVerifyModeFlag, utils.ExitWhenSyncedFlag, utils.GCModeFlag, utils.SnapshotFlag, @@ -115,7 +118,6 @@ var ( utils.WhitelistFlag, utils.BloomFilterSizeFlag, utils.TriesInMemoryFlag, - utils.AllowInsecureNoTriesFlag, utils.CacheFlag, utils.CacheDatabaseFlag, utils.CacheTrieFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index a1a0a7d0b4..6dd878f9e9 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -41,6 +41,8 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.NoUSBFlag, utils.DirectBroadcastFlag, utils.DisableSnapProtocolFlag, + utils.DisableDiffProtocolFlag, + utils.EnableTrustProtocolFlag, utils.RangeLimitFlag, utils.SmartCardDaemonPathFlag, utils.NetworkIdFlag, @@ -50,6 +52,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{ utils.YoloV3Flag, utils.RopstenFlag, utils.SyncModeFlag, + utils.TriesVerifyModeFlag, utils.ExitWhenSyncedFlag, utils.GCModeFlag, utils.TxLookupLimitFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a407098e61..8d24d23d84 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -122,6 +122,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, " + @@ -264,9 +272,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", "insecure", "none"`, + Value: &defaultVerifyMode, } OverrideBerlinFlag = cli.Uint64Flag{ Name: "override.berlin", @@ -1637,6 +1647,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) } @@ -1670,8 +1686,13 @@ 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 a node sets verify mode to full or light, it's a fast node and need + // to verify blocks from verify nodes, then it should enable trust protocol. + if cfg.TriesVerifyMode.NeedRemoteVerify() { + cfg.EnableTrustProtocol = true + } } if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) { cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100 diff --git a/core/block_validator.go b/core/block_validator.go index 3ea6615b61..4cc5c67c16 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -34,18 +34,23 @@ const badBlockCacheExpire = 30 * time.Second // // BlockValidator implements Validator. type BlockValidator struct { - config *params.ChainConfig // Chain configuration options - bc *BlockChain // Canonical block chain - engine consensus.Engine // Consensus engine used for validating + config *params.ChainConfig // Chain configuration options + bc *BlockChain // Canonical block chain + engine consensus.Engine // Consensus engine used for validating + remoteValidator *remoteVerifyManager } // NewBlockValidator returns a new block validator which is safe for re-use -func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator { +func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine, mode VerifyMode, peers verifyPeers) *BlockValidator { validator := &BlockValidator{ config: config, engine: engine, bc: blockchain, } + if mode.NeedRemoteVerify() { + validator.remoteValidator = NewVerifyManager(blockchain, peers, mode == InsecureVerify) + go validator.remoteValidator.mainLoop() + } return validator } @@ -85,6 +90,13 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { } return nil }, + // for fast node which verify trie from remote verify peers, a block's H-11 ancestor should have been verify. + func() error { + if v.remoteValidator != nil && !v.remoteValidator.AncestorVerified(v.bc.GetHeaderByNumber(header.Number.Uint64())) { + return fmt.Errorf("block's ancessor %x has not been verified", block.Hash()) + } + return nil + }, } validateRes := make(chan error, len(validateFuns)) for _, f := range validateFuns { @@ -164,6 +176,10 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return err } +func (v *BlockValidator) RemoteVerifyManager() *remoteVerifyManager { + return v.remoteValidator +} + // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas above the provided floor, and increase it towards the // ceil if the blocks are full. If the ceil is exceeded, it will always decrease diff --git a/core/blockchain.go b/core/blockchain.go index 6e1a174dbb..ffcfe444b6 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -311,8 +311,8 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par diffNumToBlockHashes: make(map[uint64]map[common.Hash]struct{}), diffPeersToDiffHashes: make(map[string]map[common.Hash]struct{}), } + bc.prefetcher = NewStatePrefetcher(chainConfig, bc, engine) - bc.validator = NewBlockValidator(chainConfig, bc, engine) bc.processor = NewStateProcessor(chainConfig, bc, engine) var err error @@ -3135,8 +3135,15 @@ func EnablePersistDiff(limit uint64) BlockChainOption { } } -func (bc *BlockChain) GetRootByDiffHash(blockNumber uint64, blockHash common.Hash, diffHash common.Hash) *types.VerifyResult { - var res types.VerifyResult +func EnableBlockValidator(chainConfig *params.ChainConfig, engine consensus.Engine, mode VerifyMode, peers verifyPeers) BlockChainOption { + return func(bc *BlockChain) *BlockChain { + bc.validator = NewBlockValidator(chainConfig, bc, engine, mode, peers) + return bc + } +} + +func (bc *BlockChain) GetRootByDiffHash(blockNumber uint64, blockHash common.Hash, diffHash common.Hash) *VerifyResult { + var res VerifyResult res.BlockNumber = blockNumber res.BlockHash = blockHash diff --git a/core/blockchain_diff_test.go b/core/blockchain_diff_test.go index ab5af2815c..c4eeccda45 100644 --- a/core/blockchain_diff_test.go +++ b/core/blockchain_diff_test.go @@ -584,7 +584,7 @@ func testGetRootByDiffHash(t *testing.T, chain1, chain2 *BlockChain, blockNumber if block2 == nil { t.Fatalf("failed to find block, number: %v", blockNumber) } - expect := types.VerifyResult{ + expect := VerifyResult{ Status: status, BlockNumber: blockNumber, BlockHash: block2.Hash(), diff --git a/core/remote_state_verifier.go b/core/remote_state_verifier.go new file mode 100644 index 0000000000..224d9b009b --- /dev/null +++ b/core/remote_state_verifier.go @@ -0,0 +1,392 @@ +package core + +import ( + "fmt" + "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/metrics" + "math/rand" + "time" + + lru "github.com/hashicorp/golang-lru" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" +) + +const ( + verifiedCacheSize = 256 + maxForkHeight = 11 + + // defaultPeerNumber is default number of verify peers + defaultPeerNumber = 3 + // pruneHeightDiff indicates that if the height difference between current block and task's + // corresponding block is larger than it, the task should be pruned. + pruneHeightDiff = 15 + pruneInterval = 5 * time.Second + resendInterval = 2 * time.Second + // tryAllPeersTime is the time that a block has not been verified and then try all the valid verify peers. + tryAllPeersTime = 15 * time.Second +) + +var ( + remoteVerifyTaskCounter = metrics.NewRegisteredCounter("remote/state/verify/task/total", nil) + + statusFullVerifiedMeter = metrics.NewRegisteredMeter("status/full/verified/messages/total", nil) + statusUntrustedVerifiedMeter = metrics.NewRegisteredMeter("status/untrusted/verified/messages/total", nil) + + statusDiffHashMismatchMeter = metrics.NewRegisteredMeter("status/diffhash/mismatch/messages/total", nil) + statusImpossibleForkMeter = metrics.NewRegisteredMeter("status/impossible/fork/messages/total", nil) + + statusBlockTooNewMeter = metrics.NewRegisteredMeter("status/block/too/new/messages/total", nil) + statusBlockNewerMeter = metrics.NewRegisteredMeter("status/block/newer/messages/total", nil) + statusPossibleForkMeter = metrics.NewRegisteredMeter("status/possible/fork/messages/total", nil) + statusUnexpectedErrorMeter = metrics.NewRegisteredMeter("status/unexpected/error/total", nil) + + codeMap = map[uint16]metrics.Meter{ + 0x101: statusFullVerifiedMeter, + 0x102: statusUntrustedVerifiedMeter, + 0x201: statusDiffHashMismatchMeter, + 0x202: statusImpossibleForkMeter, + 0x301: statusBlockTooNewMeter, + 0x302: statusBlockNewerMeter, + 0x303: statusPossibleForkMeter, + 0x400: statusUnexpectedErrorMeter, + } +) + +type remoteVerifyManager struct { + bc *BlockChain + tasks map[common.Hash]*verifyTask + peers verifyPeers + verifiedCache *lru.Cache + allowInsecure bool + + // Subscription + chainHeadCh chan ChainHeadEvent + chainHeadSub event.Subscription + + // Channels + verifyCh chan common.Hash + messageCh chan verifyMessage +} + +func NewVerifyManager(blockchain *BlockChain, peers verifyPeers, allowUntrusted bool) *remoteVerifyManager { + verifiedCache, _ := lru.New(verifiedCacheSize) + vm := &remoteVerifyManager{ + bc: blockchain, + tasks: make(map[common.Hash]*verifyTask), + peers: peers, + verifiedCache: verifiedCache, + allowInsecure: allowUntrusted, + + chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), + verifyCh: make(chan common.Hash, maxForkHeight), + messageCh: make(chan verifyMessage), + } + vm.chainHeadSub = blockchain.SubscribeChainHeadEvent(vm.chainHeadCh) + return vm +} + +func (vm *remoteVerifyManager) mainLoop() { + defer vm.chainHeadSub.Unsubscribe() + + // load unverified blocks in a normalized chain and start a batch of verify task + header := vm.bc.CurrentHeader() + // Start verify task from H to H-11 if need. + vm.NewBlockVerifyTask(header) + pruneTicker := time.NewTicker(pruneInterval) + defer pruneTicker.Stop() + for { + select { + case h := <-vm.chainHeadCh: + vm.NewBlockVerifyTask(h.Block.Header()) + case hash := <-vm.verifyCh: + vm.cacheBlockVerified(hash) + if task, ok := vm.tasks[hash]; ok { + delete(vm.tasks, hash) + remoteVerifyTaskCounter.Dec(1) + close(task.terminalCh) + } + case <-pruneTicker.C: + for hash, task := range vm.tasks { + if vm.bc.CurrentHeader().Number.Cmp(task.blockHeader.Number) == 1 && + vm.bc.CurrentHeader().Number.Uint64()-task.blockHeader.Number.Uint64() > pruneHeightDiff { + delete(vm.tasks, hash) + remoteVerifyTaskCounter.Dec(1) + close(task.terminalCh) + } + } + case message := <-vm.messageCh: + if vt, ok := vm.tasks[message.verifyResult.BlockHash]; ok { + vt.messageCh <- message + } + + // System stopped + case <-vm.bc.quit: + for _, task := range vm.tasks { + close(task.terminalCh) + } + return + case <-vm.chainHeadSub.Err(): + return + } + } +} + +func (vm *remoteVerifyManager) NewBlockVerifyTask(header *types.Header) { + for i := 0; header != nil && i <= maxForkHeight; i++ { + func(hash common.Hash) { + // if verified cache record that this block has been verified, skip. + if _, ok := vm.verifiedCache.Get(hash); ok { + return + } + // if there already has a verify task for this block, skip. + if _, ok := vm.tasks[hash]; ok { + return + } + diffLayer := vm.bc.GetTrustedDiffLayer(hash) + // if this block has no diff, there is no need to verify it. + var err error + if diffLayer == nil { + if diffLayer, err = vm.bc.GenerateDiffLayer(hash); err != nil { + log.Error("failed to get diff layer", "block", hash, "number", header.Number, "error", err) + return + } else { + log.Info("this is an empty block:", "block", hash, "number", header.Number) + return + } + } + diffHash, err := CalculateDiffHash(diffLayer) + if err != nil { + log.Error("failed to get diff hash", "block", hash, "number", header.Number, "error", err) + return + } + verifyTask := NewVerifyTask(diffHash, header, vm.peers, vm.verifyCh, vm.allowInsecure) + vm.tasks[hash] = verifyTask + remoteVerifyTaskCounter.Inc(1) + }(header.Hash()) + header = vm.bc.GetHeaderByHash(header.ParentHash) + } +} + +func (vm *remoteVerifyManager) cacheBlockVerified(hash common.Hash) { + if vm.verifiedCache.Len() >= verifiedCacheSize { + vm.verifiedCache.RemoveOldest() + } + vm.verifiedCache.Add(hash, true) +} + +// AncestorVerified function check block has been verified or it's a empty block. +func (vm *remoteVerifyManager) AncestorVerified(header *types.Header) bool { + // find header of H-11 block. + header = vm.bc.GetHeaderByNumber(header.Number.Uint64() - maxForkHeight) + // If start from genesis block, there has not a H-11 block. + if header == nil { + return true + } + // check whether H-11 block is a empty block. + if header.TxHash == types.EmptyRootHash { + parent := vm.bc.GetHeaderByHash(header.ParentHash) + return header.Root == parent.Root + } + hash := header.Hash() + _, exist := vm.verifiedCache.Get(hash) + return exist +} + +func (vm *remoteVerifyManager) HandleRootResponse(vr *VerifyResult, pid string) error { + vm.messageCh <- verifyMessage{verifyResult: vr, peerId: pid} + return nil +} + +type VerifyResult struct { + Status types.VerifyStatus + BlockNumber uint64 + BlockHash common.Hash + Root common.Hash +} + +type verifyMessage struct { + verifyResult *VerifyResult + peerId string +} + +type verifyTask struct { + diffhash common.Hash + blockHeader *types.Header + candidatePeers verifyPeers + BadPeers map[string]struct{} + startAt time.Time + allowUntrusted bool + + messageCh chan verifyMessage + terminalCh chan struct{} +} + +func NewVerifyTask(diffhash common.Hash, header *types.Header, peers verifyPeers, verifyCh chan common.Hash, allowUntrusted bool) *verifyTask { + vt := &verifyTask{ + diffhash: diffhash, + blockHeader: header, + candidatePeers: peers, + BadPeers: make(map[string]struct{}), + allowUntrusted: allowUntrusted, + messageCh: make(chan verifyMessage), + terminalCh: make(chan struct{}), + } + go vt.Start(verifyCh) + return vt +} + +func (vt *verifyTask) Start(verifyCh chan common.Hash) { + vt.startAt = time.Now() + + vt.sendVerifyRequest(defaultPeerNumber) + resend := time.NewTicker(resendInterval) + defer resend.Stop() + for { + select { + case msg := <-vt.messageCh: + if metric, exist := codeMap[msg.verifyResult.Status.Code]; exist { + metric.Mark(1) + } + switch msg.verifyResult.Status { + case types.StatusFullVerified: + statusFullVerifiedMeter.Mark(1) + vt.compareRootHashAndWrite(msg, verifyCh) + case types.StatusPartiallyVerified: + statusUntrustedVerifiedMeter.Mark(1) + log.Warn("block %s , num= %s is untrusted verified", msg.verifyResult.BlockHash, msg.verifyResult.BlockNumber) + if vt.allowUntrusted { + vt.compareRootHashAndWrite(msg, verifyCh) + } + case types.StatusUnexpectedError, types.StatusImpossibleFork, types.StatusDiffHashMismatch: + vt.BadPeers[msg.peerId] = struct{}{} + log.Info("peer %s is not available: code %d, msg %s,", msg.peerId, msg.verifyResult.Status.Code, msg.verifyResult.Status.Msg) + case types.StatusBlockTooNew, types.StatusBlockNewer, types.StatusPossibleFork: + log.Info("return msg from peer %s for block %s is %s", msg.peerId, msg.verifyResult.BlockHash, msg.verifyResult.Status.Msg) + } + case <-resend.C: + // if a task has run over 15s, try all the vaild peers to verify. + if time.Since(vt.startAt) < tryAllPeersTime { + vt.sendVerifyRequest(1) + } else { + vt.sendVerifyRequest(-1) + } + case <-vt.terminalCh: + return + } + } +} + +// sendVerifyRequest func select at most n peers from (candidatePeers-badPeers) randomly and send verify request. +// when n<0, send to all the peers exclude badPeers. +func (vt *verifyTask) sendVerifyRequest(n int) { + var validPeers []VerifyPeer + candidatePeers := vt.candidatePeers.GetVerifyPeers() + for _, p := range candidatePeers { + if _, ok := vt.BadPeers[p.ID()]; !ok { + validPeers = append(validPeers, p) + } + } + // if has not valid peer, log warning. + if len(validPeers) == 0 { + log.Warn("there is no valid peer for block", vt.blockHeader.Number) + } + if n < 0 || n >= len(validPeers) { + for _, p := range validPeers { + p.RequestRoot(vt.blockHeader.Number.Uint64(), vt.blockHeader.Hash(), vt.diffhash) + } + return + } + + // if n < len(validPeers), select n peers from validPeers randomly. + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(validPeers), func(i, j int) { validPeers[i], validPeers[j] = validPeers[j], validPeers[i] }) + for i := 0; i < n; i++ { + p := validPeers[i] + p.RequestRoot(vt.blockHeader.Number.Uint64(), vt.blockHeader.Hash(), vt.diffhash) + } +} + +func (vt *verifyTask) compareRootHashAndWrite(msg verifyMessage, verifyCh chan common.Hash) { + if msg.verifyResult.Root == vt.blockHeader.Root { + blockhash := msg.verifyResult.BlockHash + // write back to manager so that manager can cache the result and delete this task. + verifyCh <- blockhash + } else { + vt.BadPeers[msg.peerId] = struct{}{} + } +} + +type VerifyPeer interface { + RequestRoot(blockNumber uint64, blockHash common.Hash, diffHash common.Hash) error + ID() string +} + +type verifyPeers interface { + GetVerifyPeers() []VerifyPeer +} + +type VerifyMode uint32 + +const ( + LocalVerify VerifyMode = iota + FullVerify + InsecureVerify + NoneVerify +) + +func (mode VerifyMode) IsValid() bool { + return mode >= LocalVerify && mode <= NoneVerify +} + +func (mode VerifyMode) String() string { + switch mode { + case LocalVerify: + return "local" + case FullVerify: + return "full" + case InsecureVerify: + return "insecure" + case NoneVerify: + return "none" + default: + return "unknown" + } +} + +func (mode VerifyMode) MarshalText() ([]byte, error) { + switch mode { + case LocalVerify: + return []byte("local"), nil + case FullVerify: + return []byte("full"), nil + case InsecureVerify: + return []byte("insecure"), nil + case NoneVerify: + return []byte("none"), nil + default: + return nil, fmt.Errorf("unknown verify mode %d", mode) + } +} + +func (mode *VerifyMode) UnmarshalText(text []byte) error { + switch string(text) { + case "local": + *mode = LocalVerify + case "full": + *mode = FullVerify + case "insecure": + *mode = InsecureVerify + case "none": + *mode = NoneVerify + default: + return fmt.Errorf(`unknown sync mode %q, want "full", "light" or "insecure"`, text) + } + return nil +} + +func (mode VerifyMode) NeedRemoteVerify() bool { + return mode == FullVerify || mode == InsecureVerify +} diff --git a/core/types.go b/core/types.go index 5ed4817e68..0a0633103e 100644 --- a/core/types.go +++ b/core/types.go @@ -32,6 +32,8 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, skipHeavyVerify bool) error + // RemoteVerifyManager return remoteVerifyManager of validator. + RemoteVerifyManager() *remoteVerifyManager } // Prefetcher is an interface for pre-caching transaction signatures and state. diff --git a/core/types/block.go b/core/types/block.go index 72cf3408b5..da3bf163bf 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -66,13 +66,6 @@ var ( StatusUnexpectedError = VerifyStatus{Code: 0x400, Msg: "can’t verify because of unexpected internal error"} ) -type VerifyResult struct { - Status VerifyStatus - BlockNumber uint64 - BlockHash common.Hash - Root common.Hash -} - // A BlockNonce is a 64-bit hash which proves (combined with the // mix-hash) that a sufficient amount of computation has been carried // out on a block. diff --git a/eth/backend.go b/eth/backend.go index 0e937c49a0..a062dd834d 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -26,8 +26,6 @@ import ( "sync/atomic" "time" - "github.com/ethereum/go-ethereum/eth/protocols/trust" - "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -47,6 +45,7 @@ import ( "github.com/ethereum/go-ethereum/eth/protocols/diff" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" + "github.com/ethereum/go-ethereum/eth/protocols/trust" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -112,6 +111,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if !config.SyncMode.IsValid() { return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode) } + if !config.TriesVerifyMode.IsValid() { + return nil, fmt.Errorf("invalid tries verify mode %d", config.TriesVerifyMode) + } if config.Miner.GasPrice == nil || config.Miner.GasPrice.Cmp(common.Big0) <= 0 { log.Warn("Sanitizing invalid miner gas price", "provided", config.Miner.GasPrice, "updated", ethconfig.Defaults.Miner.GasPrice) config.Miner.GasPrice = new(big.Int).Set(ethconfig.Defaults.Miner.GasPrice) @@ -197,14 +199,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { TrieDirtyLimit: config.TrieDirtyCache, TrieDirtyDisabled: config.NoPruning, TrieTimeLimit: config.TrieTimeout, - NoTries: config.NoTries, + NoTries: config.TriesVerifyMode != core.LocalVerify, SnapshotLimit: config.SnapshotCache, TriesInMemory: config.TriesInMemory, Preimages: config.Preimages, } ) bcOps := make([]core.BlockChainOption, 0) - if config.DiffSync && !config.NoTries { + if config.DiffSync && config.TriesVerifyMode == core.LocalVerify { bcOps = append(bcOps, core.EnableLightProcessor) } if config.PipeCommit { @@ -213,6 +215,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { if config.PersistDiff { bcOps = append(bcOps, core.EnablePersistDiff(config.DiffBlock)) } + + peers := newPeerSet() + bcOps = append(bcOps, core.EnableBlockValidator(chainConfig, eth.engine, config.TriesVerifyMode, peers)) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, bcOps...) if err != nil { return nil, err @@ -250,6 +255,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { DirectBroadcast: config.DirectBroadcast, DiffSync: config.DiffSync, DisablePeerTxBroadcast: config.DisablePeerTxBroadcast, + PeerSet: peers, }); err != nil { return nil, err } @@ -560,8 +566,12 @@ func (s *Ethereum) Protocols() []p2p.Protocol { protos = append(protos, snap.MakeProtocols((*snapHandler)(s.handler), s.snapDialCandidates)...) } // diff protocol can still open without snap protocol - protos = append(protos, diff.MakeProtocols((*diffHandler)(s.handler), s.snapDialCandidates)...) - protos = append(protos, trust.MakeProtocols((*trustHandler)(s.handler), s.trustDialCandidates)...) + if !s.config.DisableDiffProtocol { + protos = append(protos, diff.MakeProtocols((*diffHandler)(s.handler), s.snapDialCandidates)...) + } + if s.config.EnableTrustProtocol { + protos = append(protos, trust.MakeProtocols((*trustHandler)(s.handler), s.snapDialCandidates)...) + } return protos } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index ee35d123a3..8dbef35442 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -79,7 +79,7 @@ var Defaults = Config{ TrieDirtyCache: 256, TrieTimeout: 60 * time.Minute, TriesInMemory: 128, - NoTries: false, + TriesVerifyMode: core.LocalVerify, SnapshotCache: 102, DiffBlock: uint64(86400), Miner: miner.Config{ @@ -138,6 +138,8 @@ type Config struct { NoPruning bool // Whether to disable pruning and flush everything to disk DirectBroadcast bool DisableSnapProtocol bool //Whether disable snap protocol + DisableDiffProtocol bool //Whether disable diff protocol + EnableTrustProtocol bool //Whether enable trust protocol DiffSync bool // Whether support diff sync PipeCommit bool RangeLimit bool @@ -177,7 +179,7 @@ type Config struct { TrieTimeout time.Duration SnapshotCache int TriesInMemory uint64 - NoTries bool + TriesVerifyMode core.VerifyMode Preimages bool // Mining options diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index c5e46ced41..df60b498bd 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -29,6 +29,8 @@ func (c Config) MarshalTOML() (interface{}, error) { NoPrefetch bool DirectBroadcast bool DisableSnapProtocol bool + DisableDiffProtocol bool + EnableTrustProtocol bool DiffSync bool RangeLimit bool TxLookupLimit uint64 `toml:",omitempty"` @@ -57,7 +59,7 @@ func (c Config) MarshalTOML() (interface{}, error) { TrieTimeout time.Duration SnapshotCache int TriesInMemory uint64 - NoTries bool + TriesVerifyMode core.VerifyMode Preimages bool Miner miner.Config Ethash ethash.Config `toml:",omitempty"` @@ -84,6 +86,8 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.NoPruning = c.NoPruning enc.DirectBroadcast = c.DirectBroadcast enc.DisableSnapProtocol = c.DisableSnapProtocol + enc.DisableDiffProtocol = c.DisableDiffProtocol + enc.EnableTrustProtocol = c.EnableTrustProtocol enc.DiffSync = c.DiffSync enc.RangeLimit = c.RangeLimit enc.TxLookupLimit = c.TxLookupLimit @@ -112,7 +116,7 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.TrieTimeout = c.TrieTimeout enc.SnapshotCache = c.SnapshotCache enc.TriesInMemory = c.TriesInMemory - enc.NoTries = c.NoTries + enc.TriesVerifyMode = c.TriesVerifyMode enc.Preimages = c.Preimages enc.Miner = c.Miner enc.Ethash = c.Ethash @@ -144,6 +148,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { NoPrefetch *bool DirectBroadcast *bool DisableSnapProtocol *bool + DisableDiffProtocol *bool + EnableTrustProtocol *bool DiffSync *bool RangeLimit *bool TxLookupLimit *uint64 `toml:",omitempty"` @@ -172,7 +178,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { TrieTimeout *time.Duration SnapshotCache *int TriesInMemory *uint64 - NoTries *bool + TriesVerifyMode *core.VerifyMode Preimages *bool Miner *miner.Config Ethash *ethash.Config `toml:",omitempty"` @@ -222,6 +228,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.DisableSnapProtocol != nil { c.DisableSnapProtocol = *dec.DisableSnapProtocol } + if dec.DisableDiffProtocol != nil { + c.DisableDiffProtocol = *dec.DisableDiffProtocol + } + if dec.EnableTrustProtocol != nil { + c.EnableTrustProtocol = *dec.EnableTrustProtocol + } if dec.DiffSync != nil { c.DiffSync = *dec.DiffSync } @@ -306,8 +318,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.TriesInMemory != nil { c.TriesInMemory = *dec.TriesInMemory } - if dec.NoTries != nil { - c.NoTries = *dec.NoTries + if dec.TriesVerifyMode != nil { + c.TriesVerifyMode = *dec.TriesVerifyMode } if dec.Preimages != nil { c.Preimages = *dec.Preimages diff --git a/eth/handler.go b/eth/handler.go index e47d3eee8d..f9854766c4 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -96,6 +96,7 @@ type handlerConfig struct { Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged DirectBroadcast bool DisablePeerTxBroadcast bool + PeerSet *peerSet } type handler struct { @@ -155,7 +156,7 @@ func newHandler(config *handlerConfig) (*handler, error) { database: config.Database, txpool: config.TxPool, chain: config.Chain, - peers: newPeerSet(), + peers: config.PeerSet, whitelist: config.Whitelist, directBroadcast: config.DirectBroadcast, diffSync: config.DiffSync, diff --git a/eth/handler_trust.go b/eth/handler_trust.go index 6df630a2e8..52100144e2 100644 --- a/eth/handler_trust.go +++ b/eth/handler_trust.go @@ -2,7 +2,6 @@ package eth import ( "fmt" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/protocols/trust" "github.com/ethereum/go-ethereum/p2p/enode" @@ -34,8 +33,17 @@ func (h *trustHandler) PeerInfo(id enode.ID) interface{} { func (h *trustHandler) Handle(peer *trust.Peer, packet trust.Packet) error { switch packet := packet.(type) { case *trust.RootResponsePacket: - // TODO: h.bc.VerifyManager().HandleRootResponse(peer.ID(), *packet) - return nil + verifyResult := &core.VerifyResult{ + Status: packet.Status, + BlockNumber: packet.BlockNumber, + BlockHash: packet.BlockHash, + Root: packet.Root, + } + if vm := h.Chain().Validator().RemoteVerifyManager(); vm != nil { + vm.HandleRootResponse(verifyResult, peer.ID()) + return nil + } + return fmt.Errorf("verify manager is nil which is unexpected") default: return fmt.Errorf("unexpected trust packet type: %T", packet) diff --git a/eth/peerset.go b/eth/peerset.go index 5bbaa2dd2b..b68d0e7783 100644 --- a/eth/peerset.go +++ b/eth/peerset.go @@ -22,13 +22,13 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/eth/protocols/trust" - "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/protocols/diff" "github.com/ethereum/go-ethereum/eth/protocols/eth" "github.com/ethereum/go-ethereum/eth/protocols/snap" + "github.com/ethereum/go-ethereum/eth/protocols/trust" "github.com/ethereum/go-ethereum/p2p" ) @@ -334,14 +334,14 @@ func (ps *peerSet) GetDiffPeer(pid string) downloader.IDiffPeer { } // GetVerifyPeers returns an array of verify nodes. -func (ps *peerSet) GetVerifyPeers() []*trustPeer { +func (ps *peerSet) GetVerifyPeers() []core.VerifyPeer { ps.lock.RLock() defer ps.lock.RUnlock() - res := make([]*trustPeer, 0) + res := make([]core.VerifyPeer, 0) for _, p := range ps.peers { - if p.trustExt != nil { - res = append(res, p.trustExt) + if p.trustExt != nil && p.trustExt.Peer != nil { + res = append(res, p.trustExt.Peer) } } return res diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 395e87fe1d..78f5c6bb14 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/ethereum/go-ethereum/core" "math/big" "github.com/ethereum/go-ethereum" @@ -200,8 +201,8 @@ func (ec *Client) GetDiffAccountsWithScope(ctx context.Context, number *big.Int, return &result, err } -func (ec *Client) GetRootByDiffHash(ctx context.Context, blockNr *big.Int, blockHash common.Hash, diffHash common.Hash) (*types.VerifyResult, error) { - var result types.VerifyResult +func (ec *Client) GetRootByDiffHash(ctx context.Context, blockNr *big.Int, blockHash common.Hash, diffHash common.Hash) (*core.VerifyResult, error) { + var result core.VerifyResult err := ec.c.CallContext(ctx, &result, "eth_getRootByDiffHash", toBlockNumArg(blockNr), blockHash, diffHash) return &result, err } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f3bcc3b98d..3f3122a37e 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1287,7 +1287,7 @@ func (s *PublicBlockChainAPI) GetDiffAccountsWithScope(ctx context.Context, bloc return result, err } -func (s *PublicBlockChainAPI) GetRootByDiffHash(ctx context.Context, blockNr rpc.BlockNumber, blockHash common.Hash, diffHash common.Hash) *types.VerifyResult { +func (s *PublicBlockChainAPI) GetRootByDiffHash(ctx context.Context, blockNr rpc.BlockNumber, blockHash common.Hash, diffHash common.Hash) *core.VerifyResult { return s.b.Chain().GetRootByDiffHash(uint64(blockNr), blockHash, diffHash) }