diff --git a/light/lightchain.go b/light/lightchain.go
deleted file mode 100644
index 2c52cf7989..0000000000
--- a/light/lightchain.go
+++ /dev/null
@@ -1,520 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "math/big"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/lru"
- "github.com/XinFinOrg/XDPoSChain/consensus"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/event"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/params"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-var (
- bodyCacheLimit = 256
- blockCacheLimit = 256
-)
-
-// LightChain represents a canonical chain that by default only handles block
-// headers, downloading block bodies and receipts on demand through an ODR
-// interface. It only does header validation during chain insertion.
-type LightChain struct {
- hc *core.HeaderChain
- chainDb ethdb.Database
- odr OdrBackend
- chainFeed event.Feed
- chainSideFeed event.Feed
- chainHeadFeed event.Feed
- scope event.SubscriptionScope
- genesisBlock *types.Block
-
- chainmu sync.RWMutex
-
- bodyCache *lru.Cache[common.Hash, *types.Body]
- bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
- blockCache *lru.Cache[common.Hash, *types.Block]
-
- quit chan struct{}
- running int32 // running must be called automically
- // procInterrupt must be atomically called
- procInterrupt int32 // interrupt signaler for block processing
- wg sync.WaitGroup
-
- engine consensus.Engine
-}
-
-// NewLightChain returns a fully initialised light chain using information
-// available in the database. It initialises the default Ethereum header
-// validator.
-func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) {
- bc := &LightChain{
- chainDb: odr.Database(),
- odr: odr,
- quit: make(chan struct{}),
- bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
- bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
- blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
- engine: engine,
- }
- var err error
- bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
- if err != nil {
- return nil, err
- }
- bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0)
- if bc.genesisBlock == nil {
- return nil, core.ErrNoGenesis
- }
- if cp, ok := trustedCheckpoints[bc.genesisBlock.Hash()]; ok {
- bc.addTrustedCheckpoint(cp)
- }
- if err := bc.loadLastState(); err != nil {
- return nil, err
- }
- // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
- for hash := range core.BadHashes {
- if header := bc.GetHeaderByHash(hash); header != nil {
- log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
- bc.SetHead(header.Number.Uint64() - 1)
- log.Error("Chain rewind was successful, resuming normal operation")
- }
- }
- return bc, nil
-}
-
-// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
-func (lc *LightChain) addTrustedCheckpoint(cp TrustedCheckpoint) {
- if lc.odr.ChtIndexer() != nil {
- StoreChtRoot(lc.chainDb, cp.SectionIdx, cp.SectionHead, cp.CHTRoot)
- lc.odr.ChtIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead)
- }
- if lc.odr.BloomTrieIndexer() != nil {
- StoreBloomTrieRoot(lc.chainDb, cp.SectionIdx, cp.SectionHead, cp.BloomRoot)
- lc.odr.BloomTrieIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead)
- }
- if lc.odr.BloomIndexer() != nil {
- lc.odr.BloomIndexer().AddKnownSectionHead(cp.SectionIdx, cp.SectionHead)
- }
- log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.SectionIdx+1)*CHTFrequencyClient-1, "hash", cp.SectionHead)
-}
-
-func (lc *LightChain) getProcInterrupt() bool {
- return atomic.LoadInt32(&lc.procInterrupt) == 1
-}
-
-// Odr returns the ODR backend of the chain
-func (lc *LightChain) Odr() OdrBackend {
- return lc.odr
-}
-
-// loadLastState loads the last known chain state from the database. This method
-// assumes that the chain manager mutex is held.
-func (lc *LightChain) loadLastState() error {
- if head := rawdb.ReadHeadHeaderHash(lc.chainDb); head == (common.Hash{}) {
- // Corrupt or empty database, init from scratch
- lc.Reset()
- } else {
- if header := lc.GetHeaderByHash(head); header != nil {
- lc.hc.SetCurrentHeader(header)
- }
- }
- // Issue a status log and return
- header := lc.hc.CurrentHeader()
- headerTd := lc.GetTd(header.Hash(), header.Number.Uint64())
- log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd)
-
- return nil
-}
-
-// SetHead rewinds the local chain to a new head. Everything above the new
-// head will be deleted and the new one set.
-func (lc *LightChain) SetHead(head uint64) {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.hc.SetHead(head, nil, nil)
- lc.loadLastState()
-}
-
-// GasLimit returns the gas limit of the current HEAD block.
-func (lc *LightChain) GasLimit() uint64 {
- return lc.hc.CurrentHeader().GasLimit
-}
-
-// Reset purges the entire blockchain, restoring it to its genesis state.
-func (lc *LightChain) Reset() {
- lc.ResetWithGenesisBlock(lc.genesisBlock)
-}
-
-// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
-// specified genesis state.
-func (lc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
- // Dump the entire block chain and purge the caches
- lc.SetHead(0)
-
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- // Prepare the genesis block and reinitialise the chain
- batch := lc.chainDb.NewBatch()
- rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
- rawdb.WriteBlock(batch, genesis)
- rawdb.WriteHeadHeaderHash(batch, genesis.Hash())
- if err := batch.Write(); err != nil {
- log.Crit("Failed to reset genesis block", "err", err)
- }
- lc.genesisBlock = genesis
- lc.hc.SetGenesis(lc.genesisBlock.Header())
- lc.hc.SetCurrentHeader(lc.genesisBlock.Header())
-}
-
-// Accessors
-
-// Engine retrieves the light chain's consensus engine.
-func (lc *LightChain) Engine() consensus.Engine { return lc.engine }
-
-// Genesis returns the genesis block
-func (lc *LightChain) Genesis() *types.Block {
- return lc.genesisBlock
-}
-
-// State returns a new mutable state based on the current HEAD block.
-func (lc *LightChain) State() (*state.StateDB, error) {
- return nil, errors.New("not implemented, needs client/server interface split")
-}
-
-// GetBody retrieves a block body (transactions and uncles) from the database
-// or ODR service by hash, caching it if found.
-func (lc *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := lc.bodyCache.Get(hash); ok && cached != nil {
- return cached, nil
- }
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- body, err := GetBody(ctx, lc.odr, hash, *number)
- if err != nil {
- return nil, err
- }
- // Cache the found body for next time and return
- lc.bodyCache.Add(hash, body)
- return body, nil
-}
-
-// GetBodyRLP retrieves a block body in RLP encoding from the database or
-// ODR service by hash, caching it if found.
-func (lc *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := lc.bodyRLPCache.Get(hash); ok {
- return cached, nil
- }
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- body, err := GetBodyRLP(ctx, lc.odr, hash, *number)
- if err != nil {
- return nil, err
- }
- // Cache the found body for next time and return
- lc.bodyRLPCache.Add(hash, body)
- return body, nil
-}
-
-// HasBlock checks if a block is fully present in the database or not, caching
-// it if present.
-func (lc *LightChain) HasBlock(hash common.Hash, number uint64) bool {
- blk, _ := lc.GetBlock(NoOdr, hash, number)
- return blk != nil
-}
-
-// GetBlock retrieves a block from the database or ODR service by hash and number,
-// caching it if found.
-func (lc *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) {
- // Short circuit if the block's already in the cache, retrieve otherwise
- if block, ok := lc.blockCache.Get(hash); ok && block != nil {
- return block, nil
- }
- block, err := GetBlock(ctx, lc.odr, hash, number)
- if err != nil {
- return nil, err
- }
- // Cache the found block for next time and return
- lc.blockCache.Add(block.Hash(), block)
- return block, nil
-}
-
-// GetBlockByHash retrieves a block from the database or ODR service by hash,
-// caching it if found.
-func (lc *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- number := lc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil, errors.New("unknown block")
- }
- return lc.GetBlock(ctx, hash, *number)
-}
-
-// GetBlockByNumber retrieves a block from the database or ODR service by
-// number, caching it (associated with its hash) if found.
-func (lc *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
- hash, err := GetCanonicalHash(ctx, lc.odr, number)
- if hash == (common.Hash{}) || err != nil {
- return nil, err
- }
- return lc.GetBlock(ctx, hash, number)
-}
-
-// Stop stops the blockchain service. If any imports are currently in progress
-// it will abort them using the procInterrupt.
-func (lc *LightChain) Stop() {
- if !atomic.CompareAndSwapInt32(&lc.running, 0, 1) {
- return
- }
- close(lc.quit)
- atomic.StoreInt32(&lc.procInterrupt, 1)
-
- lc.wg.Wait()
- log.Info("Blockchain manager stopped")
-}
-
-// Rollback is designed to remove a chain of links from the database that aren't
-// certain enough to be valid.
-func (lc *LightChain) Rollback(chain []common.Hash) {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- batch := lc.chainDb.NewBatch()
- for i := len(chain) - 1; i >= 0; i-- {
- hash := chain[i]
-
- // Degrade the chain markers if they are explicitly reverted.
- // In theory we should update all in-memory markers in the
- // last step, however the direction of rollback is from high
- // to low, so it's safe the update in-memory markers directly.
- if head := lc.hc.CurrentHeader(); head.Hash() == hash {
- rawdb.WriteHeadHeaderHash(batch, head.ParentHash)
- lc.hc.SetCurrentHeader(lc.GetHeader(head.ParentHash, head.Number.Uint64()-1))
- }
- }
- if err := batch.Write(); err != nil {
- log.Crit("Failed to rollback light chain", "error", err)
- }
-}
-
-// postChainEvents iterates over the events generated by a chain insertion and
-// posts them into the event feed.
-func (lc *LightChain) postChainEvents(events []interface{}) {
- for _, event := range events {
- switch ev := event.(type) {
- case core.ChainEvent:
- if lc.CurrentHeader().Hash() == ev.Hash {
- lc.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block})
- }
- lc.chainFeed.Send(ev)
- case core.ChainSideEvent:
- lc.chainSideFeed.Send(ev)
- }
- }
-}
-
-// InsertHeaderChain attempts to insert the given header chain in to the local
-// chain, possibly creating a reorg. If an error is returned, it will return the
-// index number of the failing header as well an error describing what went wrong.
-//
-// The verify parameter can be used to fine tune whether nonce verification
-// should be done or not. The reason behind the optional check is because some
-// of the header retrieval mechanisms already need to verfy nonces, as well as
-// because nonces can be verified sparsely, not needing to check each.
-//
-// In the case of a light chain, InsertHeaderChain also creates and posts light
-// chain events when necessary.
-func (lc *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
- start := time.Now()
- if i, err := lc.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
- return i, err
- }
-
- // Make sure only one thread manipulates the chain at once
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- lc.wg.Add(1)
- defer lc.wg.Done()
-
- var events []interface{}
- whFunc := func(header *types.Header) error {
- status, err := lc.hc.WriteHeader(header)
-
- switch status {
- case core.CanonStatTy:
- log.Debug("Inserted new header", "number", header.Number, "hash", header.Hash())
- events = append(events, core.ChainEvent{Block: types.NewBlockWithHeader(header), Hash: header.Hash()})
-
- case core.SideStatTy:
- log.Debug("Inserted forked header", "number", header.Number, "hash", header.Hash())
- events = append(events, core.ChainSideEvent{Block: types.NewBlockWithHeader(header)})
- }
- return err
- }
- i, err := lc.hc.InsertHeaderChain(chain, whFunc, start)
- lc.postChainEvents(events)
- return i, err
-}
-
-// CurrentHeader retrieves the current head header of the canonical chain. The
-// header is retrieved from the HeaderChain's internal cache.
-func (lc *LightChain) CurrentHeader() *types.Header {
- return lc.hc.CurrentHeader()
-}
-
-// GetTd retrieves a block's total difficulty in the canonical chain from the
-// database by hash and number, caching it if found.
-func (lc *LightChain) GetTd(hash common.Hash, number uint64) *big.Int {
- return lc.hc.GetTd(hash, number)
-}
-
-// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
-// database by hash, caching it if found.
-func (lc *LightChain) GetTdByHash(hash common.Hash) *big.Int {
- return lc.hc.GetTdByHash(hash)
-}
-
-// GetHeader retrieves a block header from the database by hash and number,
-// caching it if found.
-func (lc *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header {
- return lc.hc.GetHeader(hash, number)
-}
-
-// GetHeaderByHash retrieves a block header from the database by hash, caching it if
-// found.
-func (lc *LightChain) GetHeaderByHash(hash common.Hash) *types.Header {
- return lc.hc.GetHeaderByHash(hash)
-}
-
-// HasHeader checks if a block header is present in the database or not, caching
-// it if present.
-func (lc *LightChain) HasHeader(hash common.Hash, number uint64) bool {
- return lc.hc.HasHeader(hash, number)
-}
-
-// GetCanonicalHash returns the canonical hash for a given block number
-func (lc *LightChain) GetCanonicalHash(number uint64) common.Hash {
- return lc.hc.GetCanonicalHash(number)
-}
-
-// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
-// hash, fetching towards the genesis block.
-func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
- return lc.hc.GetBlockHashesFromHash(hash, max)
-}
-
-// GetHeaderByNumber retrieves a block header from the database by number,
-// caching it (associated with its hash) if found.
-func (lc *LightChain) GetHeaderByNumber(number uint64) *types.Header {
- return lc.hc.GetHeaderByNumber(number)
-}
-
-// GetHeaderByNumberOdr retrieves a block header from the database or network
-// by number, caching it (associated with its hash) if found.
-func (lc *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) {
- if header := lc.hc.GetHeaderByNumber(number); header != nil {
- return header, nil
- }
- return GetHeaderByNumber(ctx, lc.odr, number)
-}
-
-// Config retrieves the header chain's chain configuration.
-func (lc *LightChain) Config() *params.ChainConfig { return lc.hc.Config() }
-
-func (lc *LightChain) SyncCht(ctx context.Context) bool {
- if lc.odr.ChtIndexer() == nil {
- return false
- }
- headNum := lc.CurrentHeader().Number.Uint64()
- chtCount, _, _ := lc.odr.ChtIndexer().Sections()
- if headNum+1 < chtCount*CHTFrequencyClient {
- num := chtCount*CHTFrequencyClient - 1
- // Retrieve the latest useful header and update to it
- if header, err := GetHeaderByNumber(ctx, lc.odr, num); header != nil && err == nil {
- lc.chainmu.Lock()
- defer lc.chainmu.Unlock()
-
- // Ensure the chain didn't move past the latest block while retrieving it
- if lc.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
- log.Info("Updated latest header based on CHT", "number", header.Number, "hash", header.Hash())
- rawdb.WriteHeadHeaderHash(lc.chainDb, header.Hash())
- lc.hc.SetCurrentHeader(header)
- }
- return true
- }
- }
- return false
-}
-
-// LockChain locks the chain mutex for reading so that multiple canonical hashes can be
-// retrieved while it is guaranteed that they belong to the same version of the chain
-func (lc *LightChain) LockChain() {
- lc.chainmu.RLock()
-}
-
-// UnlockChain unlocks the chain mutex
-func (lc *LightChain) UnlockChain() {
- lc.chainmu.RUnlock()
-}
-
-// SubscribeChainEvent registers a subscription of ChainEvent.
-func (lc *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
- return lc.scope.Track(lc.chainFeed.Subscribe(ch))
-}
-
-// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
-func (lc *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
- return lc.scope.Track(lc.chainHeadFeed.Subscribe(ch))
-}
-
-// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
-func (lc *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription {
- return lc.scope.Track(lc.chainSideFeed.Subscribe(ch))
-}
-
-// SubscribeLogsEvent implements the interface of filters.Backend
-// LightChain does not send logs events, so return an empty subscription.
-func (lc *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return lc.scope.Track(new(event.Feed).Subscribe(ch))
-}
-
-// SubscribeRemovedLogsEvent implements the interface of filters.Backend
-// LightChain does not send core.RemovedLogsEvent, so return an empty subscription.
-func (lc *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
- return lc.scope.Track(new(event.Feed).Subscribe(ch))
-}
diff --git a/light/lightchain_test.go b/light/lightchain_test.go
deleted file mode 100644
index 2c710c07d2..0000000000
--- a/light/lightchain_test.go
+++ /dev/null
@@ -1,351 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "math/big"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/params"
-)
-
-// So we can deterministically seed different blockchains
-var (
- canonicalSeed = 1
- forkSeed = 2
-)
-
-// makeHeaderChain creates a deterministic chain of headers rooted at parent.
-func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header {
- blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) {
- b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
- })
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
- }
- return headers
-}
-
-// newCanonical creates a chain database, and injects a deterministic canonical
-// chain. Depending on the full flag, if creates either a full block chain or a
-// header only chain.
-func newCanonical(n int) (ethdb.Database, *LightChain, error) {
- db := rawdb.NewMemoryDatabase()
- gspec := core.Genesis{Config: params.TestChainConfig}
- genesis := gspec.MustCommit(db)
- blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFaker())
-
- // Create and inject the requested chain
- if n == 0 {
- return db, blockchain, nil
- }
- // Header-only chain requested
- headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
- _, err := blockchain.InsertHeaderChain(headers, 1)
- return db, blockchain, err
-}
-
-// newTestLightChain creates a LightChain that doesn't validate anything.
-func newTestLightChain() *LightChain {
- db := rawdb.NewMemoryDatabase()
- gspec := &core.Genesis{
- Difficulty: big.NewInt(1),
- Config: params.TestChainConfig,
- }
- gspec.MustCommit(db)
- lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker())
- if err != nil {
- panic(err)
- }
- return lc
-}
-
-// Test fork of length N starting from block i
-func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) {
- // Copy old chain up to #i into a new db
- db, LightChain2, err := newCanonical(i)
- if err != nil {
- t.Fatal("could not make new canonical in testFork", err)
- }
- // Assert the chains have the same header/block at #i
- var hash1, hash2 common.Hash
- hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash()
- hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash()
- if hash1 != hash2 {
- t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
- }
- // Extend the newly created chain
- headerChainB := makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed)
- if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil {
- t.Fatalf("failed to insert forking chain: %v", err)
- }
- // Sanity check that the forked chain can be imported into the original
- var tdPre, tdPost *big.Int
-
- tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash())
- if err := testHeaderChainImport(headerChainB, LightChain); err != nil {
- t.Fatalf("failed to import forked header chain: %v", err)
- }
- tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash())
- // Compare the total difficulties of the chains
- comparator(tdPre, tdPost)
-}
-
-// testHeaderChainImport tries to process a chain of header, writing them into
-// the database if successful.
-func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error {
- for _, header := range chain {
- // Try and validate the header
- if err := lightchain.engine.VerifyHeader(lightchain.hc, header, true); err != nil {
- return err
- }
- // Manually insert the header into the database, but don't reorganize (allows subsequent testing)
- lightchain.chainmu.Lock()
- rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash)))
- rawdb.WriteHeader(lightchain.chainDb, header)
- lightchain.chainmu.Unlock()
- }
- return nil
-}
-
-// Tests that given a starting canonical chain of a given size, it can be extended
-// with various length chains.
-func TestExtendCanonicalHeaders(t *testing.T) {
- length := 5
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Start fork from current height
- testFork(t, processor, length, 1, better)
- testFork(t, processor, length, 2, better)
- testFork(t, processor, length, 5, better)
- testFork(t, processor, length, 10, better)
-}
-
-// Tests that given a starting canonical chain of a given size, creating shorter
-// forks do not take canonical ownership.
-func TestShorterForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- worse := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) >= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1)
- }
- }
- // Sum of numbers must be less than `length` for this to be a shorter fork
- testFork(t, processor, 0, 3, worse)
- testFork(t, processor, 0, 7, worse)
- testFork(t, processor, 1, 1, worse)
- testFork(t, processor, 1, 7, worse)
- testFork(t, processor, 5, 3, worse)
- testFork(t, processor, 5, 4, worse)
-}
-
-// Tests that given a starting canonical chain of a given size, creating longer
-// forks do take canonical ownership.
-func TestLongerForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- better := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) <= 0 {
- t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
- }
- }
- // Sum of numbers must be greater than `length` for this to be a longer fork
- testFork(t, processor, 0, 11, better)
- testFork(t, processor, 0, 15, better)
- testFork(t, processor, 1, 10, better)
- testFork(t, processor, 1, 12, better)
- testFork(t, processor, 5, 6, better)
- testFork(t, processor, 5, 8, better)
-}
-
-// Tests that given a starting canonical chain of a given size, creating equal
-// forks do take canonical ownership.
-func TestEqualForkHeaders(t *testing.T) {
- length := 10
-
- // Make first chain starting from genesis
- _, processor, err := newCanonical(length)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Define the difficulty comparator
- equal := func(td1, td2 *big.Int) {
- if td2.Cmp(td1) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1)
- }
- }
- // Sum of numbers must be equal to `length` for this to be an equal fork
- testFork(t, processor, 0, 10, equal)
- testFork(t, processor, 1, 9, equal)
- testFork(t, processor, 2, 8, equal)
- testFork(t, processor, 5, 5, equal)
- testFork(t, processor, 6, 4, equal)
- testFork(t, processor, 9, 1, equal)
-}
-
-// Tests that chains missing links do not get accepted by the processor.
-func TestBrokenHeaderChain(t *testing.T) {
- // Make chain starting from genesis
- db, LightChain, err := newCanonical(10)
- if err != nil {
- t.Fatalf("failed to make new canonical chain: %v", err)
- }
- // Create a forked chain, and try to insert with a missing link
- chain := makeHeaderChain(LightChain.CurrentHeader(), 5, db, forkSeed)[1:]
- if err := testHeaderChainImport(chain, LightChain); err == nil {
- t.Errorf("broken header chain not reported")
- }
-}
-
-func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
- var chain []*types.Header
- for i, difficulty := range d {
- header := &types.Header{
- Coinbase: common.Address{seed},
- Number: big.NewInt(int64(i + 1)),
- Difficulty: big.NewInt(int64(difficulty)),
- UncleHash: types.EmptyUncleHash,
- TxHash: types.EmptyTxsHash,
- ReceiptHash: types.EmptyReceiptsHash,
- }
- if i == 0 {
- header.ParentHash = genesis.Hash()
- } else {
- header.ParentHash = chain[i-1].Hash()
- }
- chain = append(chain, types.CopyHeader(header))
- }
- return chain
-}
-
-type dummyOdr struct {
- OdrBackend
- db ethdb.Database
-}
-
-func (odr *dummyOdr) Database() ethdb.Database {
- return odr.db
-}
-
-func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error {
- return nil
-}
-
-// Tests that reorganizing a long difficult chain after a short easy one
-// overwrites the canonical numbers and links in the database.
-func TestReorgLongHeaders(t *testing.T) {
- testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10)
-}
-
-// Tests that reorganizing a short difficult chain after a long easy one
-// overwrites the canonical numbers and links in the database.
-func TestReorgShortHeaders(t *testing.T) {
- testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11)
-}
-
-func testReorg(t *testing.T, first, second []int, td int64) {
- bc := newTestLightChain()
-
- // Insert an easy and a difficult chain afterwards
- bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
- bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
- // Check that the chain is valid number and link wise
- prev := bc.CurrentHeader()
- for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) {
- if prev.ParentHash != header.Hash() {
- t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
- }
- }
- // Make sure the chain total difficulty is the correct one
- want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
- if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 {
- t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
- }
-}
-
-// Tests that the insertion functions detect banned hashes.
-func TestBadHeaderHashes(t *testing.T) {
- bc := newTestLightChain()
-
- // Create a chain, ban a hash and try to import
- var err error
- headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
- core.BadHashes[headers[2].Hash()] = true
- if _, err = bc.InsertHeaderChain(headers, 1); !errors.Is(err, core.ErrBlacklistedHash) {
- t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash)
- }
-}
-
-// Tests that bad hashes are detected on boot, and the chan rolled back to a
-// good state prior to the bad hash.
-func TestReorgBadHeaderHashes(t *testing.T) {
- bc := newTestLightChain()
-
- // Create a chain, import and ban aferwards
- headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
-
- if _, err := bc.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to import headers: %v", err)
- }
- if bc.CurrentHeader().Hash() != headers[3].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash())
- }
- core.BadHashes[headers[3].Hash()] = true
- defer func() { delete(core.BadHashes, headers[3].Hash()) }()
-
- // Create a new LightChain and check that it rolled back the state.
- ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker())
- if err != nil {
- t.Fatalf("failed to create new chain manager: %v", err)
- }
- if ncm.CurrentHeader().Hash() != headers[2].Hash() {
- t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash())
- }
-}
diff --git a/light/nodeset.go b/light/nodeset.go
deleted file mode 100644
index 53217b8161..0000000000
--- a/light/nodeset.go
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "errors"
- "sync"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-// NodeSet stores a set of trie nodes. It implements trie.Database and can also
-// act as a cache for another trie.Database.
-type NodeSet struct {
- nodes map[string][]byte
- order []string
-
- dataSize int
- lock sync.RWMutex
-}
-
-// NewNodeSet creates an empty node set
-func NewNodeSet() *NodeSet {
- return &NodeSet{
- nodes: make(map[string][]byte),
- }
-}
-
-// Put stores a new node in the set
-func (db *NodeSet) Put(key []byte, value []byte) error {
- db.lock.Lock()
- defer db.lock.Unlock()
-
- if _, ok := db.nodes[string(key)]; ok {
- return nil
- }
- keystr := string(key)
-
- db.nodes[keystr] = common.CopyBytes(value)
- db.order = append(db.order, keystr)
- db.dataSize += len(value)
-
- return nil
-}
-
-func (db *NodeSet) Delete(key []byte) error {
- db.lock.Lock()
- defer db.lock.Unlock()
-
- value, ok := db.nodes[string(key)]
- if !ok {
- return nil
- }
- keystr := string(key)
- delete(db.nodes, keystr)
- index := -1
- for i, key := range db.order {
- if key == keystr {
- index = i
- break
- }
- }
- length := len(db.order)
- if index == 0 {
- db.order = db.order[index+1 : length]
- } else if index == length-1 {
- db.order = db.order[0 : length-1]
- } else {
- db.order = append(db.order[0:index], db.order[index+1:length]...)
- }
- db.dataSize -= len(value)
- return nil
-}
-
-// Get returns a stored node
-func (db *NodeSet) Get(key []byte) ([]byte, error) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- if entry, ok := db.nodes[string(key)]; ok {
- return entry, nil
- }
- return nil, errors.New("not found")
-}
-
-// Has returns true if the node set contains the given key
-func (db *NodeSet) Has(key []byte) (bool, error) {
- _, err := db.Get(key)
- return err == nil, nil
-}
-
-// KeyCount returns the number of nodes in the set
-func (db *NodeSet) KeyCount() int {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- return len(db.nodes)
-}
-
-// DataSize returns the aggregated data size of nodes in the set
-func (db *NodeSet) DataSize() int {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- return db.dataSize
-}
-
-// NodeList converts the node set to a NodeList
-func (db *NodeSet) NodeList() NodeList {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- var values NodeList
- for _, key := range db.order {
- values = append(values, db.nodes[key])
- }
- return values
-}
-
-// Store writes the contents of the set to the given database
-func (db *NodeSet) Store(target ethdb.KeyValueWriter) {
- db.lock.RLock()
- defer db.lock.RUnlock()
-
- for key, value := range db.nodes {
- target.Put([]byte(key), value)
- }
-}
-
-// NodeList stores an ordered list of trie nodes. It implements ethdb.Writer.
-type NodeList []rlp.RawValue
-
-func (n NodeList) Delete(key []byte) error {
- return nil
-}
-
-// Store writes the contents of the list to the given database
-func (n NodeList) Store(db ethdb.KeyValueWriter) {
- for _, node := range n {
- db.Put(crypto.Keccak256(node), node)
- }
-}
-
-// NodeSet converts the node list to a NodeSet
-func (n NodeList) NodeSet() *NodeSet {
- db := NewNodeSet()
- n.Store(db)
- return db
-}
-
-// Put stores a new node at the end of the list
-func (n *NodeList) Put(key []byte, value []byte) error {
- *n = append(*n, value)
- return nil
-}
-
-// DataSize returns the aggregated data size of nodes in the list
-func (n NodeList) DataSize() int {
- var size int
- for _, node := range n {
- size += len(node)
- }
- return size
-}
diff --git a/light/odr.go b/light/odr.go
deleted file mode 100644
index 51affeb969..0000000000
--- a/light/odr.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// Package light implements on-demand retrieval capable state and chain objects
-// for the Ethereum Light Client.
-package light
-
-import (
- "context"
- "errors"
- "math/big"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
-)
-
-// NoOdr is the default context passed to an ODR capable function when the ODR
-// service is not required.
-var NoOdr = context.Background()
-
-// ErrNoPeers is returned if no peers capable of serving a queued request are available
-var ErrNoPeers = errors.New("no suitable peers available")
-
-// OdrBackend is an interface to a backend service that handles ODR retrievals type
-type OdrBackend interface {
- Database() ethdb.Database
- ChtIndexer() *core.ChainIndexer
- BloomTrieIndexer() *core.ChainIndexer
- BloomIndexer() *core.ChainIndexer
- Retrieve(ctx context.Context, req OdrRequest) error
-}
-
-// OdrRequest is an interface for retrieval requests
-type OdrRequest interface {
- StoreResult(db ethdb.Database)
-}
-
-// TrieID identifies a state or account storage trie
-type TrieID struct {
- BlockHash, Root common.Hash
- BlockNumber uint64
- AccKey []byte
-}
-
-// StateTrieID returns a TrieID for a state trie belonging to a certain block
-// header.
-func StateTrieID(header *types.Header) *TrieID {
- return &TrieID{
- BlockHash: header.Hash(),
- BlockNumber: header.Number.Uint64(),
- AccKey: nil,
- Root: header.Root,
- }
-}
-
-// StorageTrieID returns a TrieID for a contract storage trie at a given account
-// of a given state trie. It also requires the root hash of the trie for
-// checking Merkle proofs.
-func StorageTrieID(state *TrieID, addrHash, root common.Hash) *TrieID {
- return &TrieID{
- BlockHash: state.BlockHash,
- BlockNumber: state.BlockNumber,
- AccKey: addrHash[:],
- Root: root,
- }
-}
-
-// TrieRequest is the ODR request type for state/storage trie entries
-type TrieRequest struct {
- OdrRequest
- Id *TrieID
- Key []byte
- Proof *NodeSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *TrieRequest) StoreResult(db ethdb.Database) {
- req.Proof.Store(db)
-}
-
-// CodeRequest is the ODR request type for retrieving contract code
-type CodeRequest struct {
- OdrRequest
- Id *TrieID // references storage trie of the account
- Hash common.Hash
- Data []byte
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *CodeRequest) StoreResult(db ethdb.Database) {
- db.Put(req.Hash[:], req.Data)
-}
-
-// BlockRequest is the ODR request type for retrieving block bodies
-type BlockRequest struct {
- OdrRequest
- Hash common.Hash
- Number uint64
- Rlp []byte
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *BlockRequest) StoreResult(db ethdb.Database) {
- rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp)
-}
-
-// ReceiptsRequest is the ODR request type for retrieving block bodies
-type ReceiptsRequest struct {
- OdrRequest
- Hash common.Hash
- Number uint64
- Receipts types.Receipts
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
- rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts)
-}
-
-// ChtRequest is the ODR request type for state/storage trie entries
-type ChtRequest struct {
- OdrRequest
- ChtNum, BlockNum uint64
- ChtRoot common.Hash
- Header *types.Header
- Td *big.Int
- Proof *NodeSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *ChtRequest) StoreResult(db ethdb.Database) {
- // if there is a canonical hash, there is a header too
- rawdb.WriteHeader(db, req.Header)
- hash, num := req.Header.Hash(), req.Header.Number.Uint64()
- rawdb.WriteTd(db, hash, num, req.Td)
- rawdb.WriteCanonicalHash(db, hash, num)
-}
-
-// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
-type BloomRequest struct {
- OdrRequest
- BloomTrieNum uint64
- BitIdx uint
- SectionIdxList []uint64
- BloomTrieRoot common.Hash
- BloomBits [][]byte
- Proofs *NodeSet
-}
-
-// StoreResult stores the retrieved data in local database
-func (req *BloomRequest) StoreResult(db ethdb.Database) {
- for i, sectionIdx := range req.SectionIdxList {
- sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1)
- // if we don't have the canonical hash stored for this section head number, we'll still store it under
- // a key with a zero sectionHead. GetBloomBits will look there too if we still don't have the canonical
- // hash. In the unlikely case we've retrieved the section head hash since then, we'll just retrieve the
- // bit vector again from the network.
- rawdb.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i])
- }
-}
diff --git a/light/odr_test.go b/light/odr_test.go
deleted file mode 100644
index e561f62ab4..0000000000
--- a/light/odr_test.go
+++ /dev/null
@@ -1,327 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "errors"
- gomath "math"
- "math/big"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/math"
- "github.com/XinFinOrg/XDPoSChain/consensus"
- "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/core/vm"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/params"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- "github.com/XinFinOrg/XDPoSChain/trie"
-)
-
-var (
- testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
- testBankFunds = big.NewInt(gomath.MaxInt64)
-
- acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
- acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey)
- acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
-
- testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
- testContractAddr common.Address
-)
-
-type testOdr struct {
- OdrBackend
- sdb, ldb ethdb.Database
- disable bool
-}
-
-func (odr *testOdr) Database() ethdb.Database {
- return odr.ldb
-}
-
-var ErrOdrDisabled = errors.New("ODR disabled")
-
-func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
- if odr.disable {
- return ErrOdrDisabled
- }
- switch req := req.(type) {
- case *BlockRequest:
- number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
- if number != nil {
- req.Rlp = rawdb.ReadBodyRLP(odr.sdb, req.Hash, *number)
- }
- case *ReceiptsRequest:
- number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
- if number != nil {
- req.Receipts = rawdb.ReadRawReceipts(odr.sdb, req.Hash, *number)
- }
- case *TrieRequest:
- t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb))
- nodes := NewNodeSet()
- t.Prove(req.Key, 0, nodes)
- req.Proof = nodes
- case *CodeRequest:
- req.Data, _ = odr.sdb.Get(req.Hash[:])
- }
- req.StoreResult(odr.ldb)
- return nil
-}
-
-type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error)
-
-func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, odrGetBlock) }
-
-func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- var block *types.Block
- if bc != nil {
- block = bc.GetBlockByHash(bhash)
- } else {
- block, _ = lc.GetBlockByHash(ctx, bhash)
- }
- if block == nil {
- return nil, nil
- }
- rlp, _ := rlp.EncodeToBytes(block)
- return rlp, nil
-}
-
-func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) }
-
-func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- var receipts types.Receipts
- if bc != nil {
- number := rawdb.ReadHeaderNumber(db, bhash)
- if number != nil {
- receipts = rawdb.ReadReceipts(db, bhash, *number, bc.Config())
- }
- } else {
- number := rawdb.ReadHeaderNumber(db, bhash)
- if number != nil {
- receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, *number)
- }
- }
- if receipts == nil {
- return nil, nil
- }
- rlp, _ := rlp.EncodeToBytes(receipts)
- return rlp, nil
-}
-
-func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, odrAccounts) }
-
-func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678")
- acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr}
-
- var st *state.StateDB
- if bc == nil {
- header := lc.GetHeaderByHash(bhash)
- st = NewState(ctx, header, lc.Odr())
- } else {
- header := bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, state.NewDatabase(db))
- }
-
- var res []byte
- for _, addr := range acc {
- bal := st.GetBalance(addr)
- rlp, _ := rlp.EncodeToBytes(bal)
- res = append(res, rlp...)
- }
- return res, st.Error()
-}
-
-func TestOdrContractCallLes1(t *testing.T) { testChainOdr(t, 1, odrContractCall) }
-
-type callmsg struct {
- types.Message
-}
-
-func (callmsg) CheckNonce() bool { return false }
-
-func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
- data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000")
- config := params.TestChainConfig
- config.Eip1559Block = big.NewInt(0)
-
- var res []byte
- for i := 0; i < 3; i++ {
- data[35] = byte(i)
-
- var (
- st *state.StateDB
- header *types.Header
- chain consensus.ChainContext
- )
- if bc == nil {
- chain = lc
- header = lc.GetHeaderByHash(bhash)
- st = NewState(ctx, header, lc.Odr())
- } else {
- chain = bc
- header = bc.GetHeaderByHash(bhash)
- st, _ = state.New(header.Root, state.NewDatabase(db))
- }
-
- // Perform read-only call.
- st.SetBalance(testBankAddress, math.MaxBig256)
- feeCapacity := state.GetTRC21FeeCapacityFromState(st)
- var balanceTokenFee *big.Int
- if value, ok := feeCapacity[testContractAddr]; ok {
- balanceTokenFee = value
- }
- msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, big.NewInt(params.InitialBaseFee), big.NewInt(params.InitialBaseFee), new(big.Int), data, nil, true, balanceTokenFee, header.Number)}
- txContext := core.NewEVMTxContext(msg)
- context := core.NewEVMBlockContext(header, chain, nil)
- vmenv := vm.NewEVM(context, txContext, st, nil, config, vm.Config{NoBaseFee: true})
- gp := new(core.GasPool).AddGas(gomath.MaxUint64)
- owner := common.Address{}
- result, _ := core.ApplyMessage(vmenv, msg, gp, owner)
- res = append(res, result.Return()...)
- if st.Error() != nil {
- return res, st.Error()
- }
- }
- return res, nil
-}
-
-func testChainGen(i int, block *core.BlockGen) {
- signer := types.HomesteadSigner{}
- switch i {
- case 0:
- // In block 1, the test bank sends account #1 some ether.
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(90_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
- block.AddTx(tx)
- case 1:
- // In block 2, the test bank sends some more ether to account #1.
- // acc1Addr passes it on to account #2.
- // acc1Addr creates a test contract.
- tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, testBankKey)
- nonce := block.TxNonce(acc1Addr)
- tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1_000_000_000_000_000), params.TxGas, block.BaseFee(), nil), signer, acc1Key)
- nonce++
- tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, block.BaseFee(), testContractCode), signer, acc1Key)
- testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
- block.AddTx(tx1)
- block.AddTx(tx2)
- block.AddTx(tx3)
- case 2:
- // Block 3 is empty but was mined by account #2.
- block.SetCoinbase(acc2Addr)
- block.SetExtra([]byte("yeehaw"))
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
- block.AddTx(tx)
- case 3:
- // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
- b2 := block.PrevBlock(1).Header()
- b2.Extra = []byte("foo")
- block.AddUncle(b2)
- b3 := block.PrevBlock(2).Header()
- b3.Extra = []byte("foo")
- block.AddUncle(b3)
- data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002")
- tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, block.BaseFee(), data), signer, testBankKey)
- block.AddTx(tx)
- }
-}
-
-func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
- var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
- Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- genesis = gspec.MustCommit(sdb)
- )
- gspec.MustCommit(ldb)
- // Assemble the test environment
- config := *params.TestChainConfig
- config.Eip1559Block = big.NewInt(0)
- blockchain, _ := core.NewBlockChain(sdb, nil, &config, ethash.NewFullFaker(), vm.Config{})
- gchain, _ := core.GenerateChain(&config, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- t.Fatal(err)
- }
-
- odr := &testOdr{sdb: sdb, ldb: ldb}
- lightchain, err := NewLightChain(odr, &config, ethash.NewFullFaker())
- if err != nil {
- t.Fatal(err)
- }
- headers := make([]*types.Header, len(gchain))
- for i, block := range gchain {
- headers[i] = block.Header()
- }
- if _, err := lightchain.InsertHeaderChain(headers, 1); err != nil {
- t.Fatal(err)
- }
-
- test := func(expFail int) {
- for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ {
- bhash := rawdb.ReadCanonicalHash(sdb, i)
- b1, err := fn(NoOdr, sdb, blockchain, nil, bhash)
- if err != nil {
- t.Fatalf("error in full-node test for block %d: %v", i, err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
- defer cancel()
-
- exp := i < uint64(expFail)
- b2, err := fn(ctx, ldb, nil, lightchain, bhash)
- if err != nil && exp {
- t.Errorf("error in ODR test for block %d: %v", i, err)
- }
-
- eq := bytes.Equal(b1, b2)
- if exp && !eq {
- t.Errorf("ODR test output for block %d doesn't match full node", i)
- }
- }
- }
-
- // expect retrievals to fail (except genesis block) without a les peer
- t.Log("checking without ODR")
- odr.disable = true
- test(1)
-
- // expect all retrievals to pass with ODR enabled
- t.Log("checking with ODR")
- odr.disable = false
- test(len(gchain))
-
- // still expect all retrievals to pass, now data should be cached locally
- t.Log("checking without ODR, should be cached")
- odr.disable = true
- test(len(gchain))
-}
diff --git a/light/odr_util.go b/light/odr_util.go
deleted file mode 100644
index 4dd44f8433..0000000000
--- a/light/odr_util.go
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "errors"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/rlp"
-)
-
-var sha3_nil = crypto.Keccak256Hash(nil)
-
-// errNonCanonicalHash is returned if the requested chain data doesn't belong
-// to the canonical chain. ODR can only retrieve the canonical chain data covered
-// by the CHT or Bloom trie for verification.
-var errNonCanonicalHash = errors.New("hash is not currently canonical")
-
-// GetHeaderByNumber retrieves the canonical block header corresponding to the
-// given number. The returned header is proven by local CHT.
-func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
- db := odr.Database()
- hash := rawdb.ReadCanonicalHash(db, number)
- if (hash != common.Hash{}) {
- // if there is a canonical hash, there is a header too
- header := rawdb.ReadHeader(db, hash, number)
- if header == nil {
- panic("Canonical hash present but header not found")
- }
- return header, nil
- }
-
- var (
- chtCount, sectionHeadNum uint64
- sectionHead common.Hash
- )
- if odr.ChtIndexer() != nil {
- chtCount, sectionHeadNum, sectionHead = odr.ChtIndexer().Sections()
- canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum)
- // if the CHT was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too
- for chtCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) {
- chtCount--
- if chtCount > 0 {
- sectionHeadNum = chtCount*CHTFrequencyClient - 1
- sectionHead = odr.ChtIndexer().SectionHead(chtCount - 1)
- canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum)
- }
- }
- }
- if number >= chtCount*CHTFrequencyClient {
- return nil, ErrNoTrustedCht
- }
- r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- return r.Header, nil
-}
-
-func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
- hash := rawdb.ReadCanonicalHash(odr.Database(), number)
- if (hash != common.Hash{}) {
- return hash, nil
- }
- header, err := GetHeaderByNumber(ctx, odr, number)
- if header != nil {
- return header.Hash(), nil
- }
- return common.Hash{}, err
-}
-
-// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
-func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) {
- if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil {
- return data, nil
- }
- r := &BlockRequest{Hash: hash, Number: number}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- } else {
- return r.Rlp, nil
- }
-}
-
-// GetBody retrieves the block body (transactons, uncles) corresponding to the
-// hash.
-func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) {
- data, err := GetBodyRLP(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- body := new(types.Body)
- if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
- return nil, err
- }
- return body, nil
-}
-
-// GetBlock retrieves an entire block corresponding to the hash, assembling it
-// back from the stored header and body.
-func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) {
- // Retrieve the block header and body contents
- header := rawdb.ReadHeader(odr.Database(), hash, number)
- if header == nil {
- return nil, errNoHeader
- }
- body, err := GetBody(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- // Reassemble the block and return
- return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil
-}
-
-// GetBlockReceipts retrieves the receipts generated by the transactions included
-// in a block given by its hash. Receipts will be filled in with context data.
-func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
- // Assume receipts are already stored locally and attempt to retrieve.
- receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number)
- if receipts == nil {
- header, err := GetHeaderByNumber(ctx, odr, number)
- if err != nil {
- return nil, errNoHeader
- }
- if header.Hash() != hash {
- return nil, errNonCanonicalHash
- }
- r := &ReceiptsRequest{Hash: hash, Number: number}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- }
- receipts = r.Receipts
- }
- // If the receipts are incomplete, fill the derived fields
- if len(receipts) > 0 && receipts[0].TxHash == (common.Hash{}) {
- block, err := GetBlock(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
- config, _ := rawdb.ReadChainConfig(odr.Database(), genesis)
-
- if err := receipts.DeriveFields(config, hash, number, block.BaseFee(), block.Transactions()); err != nil {
- return nil, err
- }
- rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
- }
- return receipts, nil
-}
-
-// GetBlockLogs retrieves the logs generated by the transactions included in a
-// block given by its hash. Logs will be filled in with context data.
-func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) {
- // Retrieve the potentially incomplete receipts from disk or network
- receipts, err := GetBlockReceipts(ctx, odr, hash, number)
- if err != nil {
- return nil, err
- }
- // Return the logs without deriving any computed fields on the receipts
- logs := make([][]*types.Log, len(receipts))
- for i, receipt := range receipts {
- logs[i] = receipt.Logs
- }
- return logs, nil
-}
-
-// GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to the given bit index and section indexes
-func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxList []uint64) ([][]byte, error) {
- db := odr.Database()
- result := make([][]byte, len(sectionIdxList))
- var (
- reqList []uint64
- reqIdx []int
- )
-
- var (
- bloomTrieCount, sectionHeadNum uint64
- sectionHead common.Hash
- )
- if odr.BloomTrieIndexer() != nil {
- bloomTrieCount, sectionHeadNum, sectionHead = odr.BloomTrieIndexer().Sections()
- canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum)
- // if the BloomTrie was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too
- for bloomTrieCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) {
- bloomTrieCount--
- if bloomTrieCount > 0 {
- sectionHeadNum = bloomTrieCount*BloomTrieFrequency - 1
- sectionHead = odr.BloomTrieIndexer().SectionHead(bloomTrieCount - 1)
- canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum)
- }
- }
- }
-
- for i, sectionIdx := range sectionIdxList {
- sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1)
- // if we don't have the canonical hash stored for this section head number, we'll still look for
- // an entry with a zero sectionHead (we store it with zero section head too if we don't know it
- // at the time of the retrieval)
- bloomBits, err := rawdb.ReadBloomBits(db, bitIdx, sectionIdx, sectionHead)
- if err == nil {
- result[i] = bloomBits
- } else {
- if sectionIdx >= bloomTrieCount {
- return nil, ErrNoTrustedBloomTrie
- }
- reqList = append(reqList, sectionIdx)
- reqIdx = append(reqIdx, i)
- }
- }
- if reqList == nil {
- return result, nil
- }
-
- r := &BloomRequest{BloomTrieRoot: GetBloomTrieRoot(db, bloomTrieCount-1, sectionHead), BloomTrieNum: bloomTrieCount - 1, BitIdx: bitIdx, SectionIdxList: reqList}
- if err := odr.Retrieve(ctx, r); err != nil {
- return nil, err
- } else {
- for i, idx := range reqIdx {
- result[idx] = r.BloomBits[i]
- }
- return result, nil
- }
-}
diff --git a/light/postprocess.go b/light/postprocess.go
deleted file mode 100644
index af553c657d..0000000000
--- a/light/postprocess.go
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "encoding/binary"
- "errors"
- "fmt"
- "math/big"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/common/bitutil"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/rlp"
- "github.com/XinFinOrg/XDPoSChain/trie"
-)
-
-const (
- // CHTFrequencyClient is the block frequency for creating CHTs on the client side.
- CHTFrequencyClient = 32768
-
- // CHTFrequencyServer is the block frequency for creating CHTs on the server side.
- // Eventually this can be merged back with the client version, but that requires a
- // full database upgrade, so that should be left for a suitable moment.
- CHTFrequencyServer = 4096
-
- HelperTrieConfirmations = 2048 // number of confirmations before a server is expected to have the given HelperTrie available
- HelperTrieProcessConfirmations = 256 // number of confirmations before a HelperTrie is generated
-)
-
-// TrustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with
-// the appropriate section index and head hash. It is used to start light syncing from this checkpoint
-// and avoid downloading the entire header chain while still being able to securely access old headers/logs.
-type TrustedCheckpoint struct {
- name string
- SectionIdx uint64
- SectionHead, CHTRoot, BloomRoot common.Hash
-}
-
-// trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to
-var trustedCheckpoints = map[common.Hash]TrustedCheckpoint{}
-
-var (
- ErrNoTrustedCht = errors.New("no trusted canonical hash trie")
- ErrNoTrustedBloomTrie = errors.New("no trusted bloom trie")
- errNoHeader = errors.New("header not found")
- chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
- ChtTablePrefix = "cht-"
-)
-
-// ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format
-type ChtNode struct {
- Hash common.Hash
- Td *big.Int
-}
-
-// GetChtRoot reads the CHT root assoctiated to the given section from the database
-// Note that sectionIdx is specified according to LES/1 CHT section size
-func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...))
- return common.BytesToHash(data)
-}
-
-// GetChtV2Root reads the CHT root assoctiated to the given section from the database
-// Note that sectionIdx is specified according to LES/2 CHT section size
-func GetChtV2Root(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
- return GetChtRoot(db, (sectionIdx+1)*(CHTFrequencyClient/CHTFrequencyServer)-1, sectionHead)
-}
-
-// StoreChtRoot writes the CHT root assoctiated to the given section into the database
-// Note that sectionIdx is specified according to LES/1 CHT section size
-func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
-}
-
-// ChtIndexerBackend implements core.ChainIndexerBackend
-type ChtIndexerBackend struct {
- diskdb, trieTable ethdb.Database
- odr OdrBackend
- triedb *trie.Database
- section, sectionSize uint64
- lastHash common.Hash
- trie *trie.Trie
-}
-
-// NewChtIndexer creates a BloomTrie chain indexer
-func NewChtIndexer(db ethdb.Database, clientMode bool, odr OdrBackend) *core.ChainIndexer {
- var sectionSize, confirmReq uint64
- if clientMode {
- sectionSize = CHTFrequencyClient
- confirmReq = HelperTrieConfirmations
- } else {
- sectionSize = CHTFrequencyServer
- confirmReq = HelperTrieProcessConfirmations
- }
- idb := rawdb.NewTable(db, "chtIndex-")
- trieTable := rawdb.NewTable(db, ChtTablePrefix)
- backend := &ChtIndexerBackend{
- diskdb: db,
- odr: odr,
- trieTable: trieTable,
- triedb: trie.NewDatabase(trieTable),
- sectionSize: sectionSize,
- }
- return core.NewChainIndexer(db, idb, backend, sectionSize, confirmReq, time.Millisecond*100, "cht")
-}
-
-// fetchMissingNodes tries to retrieve the last entry of the latest trusted CHT from the
-// ODR backend in order to be able to add new entries and calculate subsequent root hashes
-func (c *ChtIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error {
- batch := c.trieTable.NewBatch()
- r := &ChtRequest{ChtRoot: root, ChtNum: section - 1, BlockNum: section*c.sectionSize - 1}
- for {
- err := c.odr.Retrieve(ctx, r)
- switch err {
- case nil:
- r.Proof.Store(batch)
- return batch.Write()
- case ErrNoPeers:
- // if there are no peers to serve, retry later
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-time.After(time.Second * 10):
- // stay in the loop and try again
- }
- default:
- return err
- }
- }
-}
-
-// Reset implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
- root := types.EmptyRootHash
- if section > 0 {
- root = GetChtRoot(c.diskdb, section-1, lastSectionHead)
- }
- var err error
- c.trie, err = trie.New(root, c.triedb)
-
- if err != nil && c.odr != nil {
- err = c.fetchMissingNodes(ctx, section, root)
- if err == nil {
- c.trie, err = trie.New(root, c.triedb)
- }
- }
-
- c.section = section
- return err
-}
-
-// Process implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error {
- hash, num := header.Hash(), header.Number.Uint64()
- c.lastHash = hash
-
- td := rawdb.ReadTd(c.diskdb, hash, num)
- if td == nil {
- panic("ChtIndexerBackend Process: td == nil")
- }
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], num)
- data, _ := rlp.EncodeToBytes(ChtNode{hash, td})
- c.trie.Update(encNumber[:], data)
- return nil
-}
-
-// Commit implements core.ChainIndexerBackend
-func (c *ChtIndexerBackend) Commit() error {
- root, err := c.trie.Commit(nil)
- if err != nil {
- return err
- }
- c.triedb.Commit(root, false)
-
- if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 {
- log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root))
- }
- StoreChtRoot(c.diskdb, c.section, c.lastHash, root)
- return nil
-}
-
-const (
- BloomTrieFrequency = 32768
- ethBloomBitsSection = 4096
-)
-
-var (
- bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash
- BloomTrieTablePrefix = "blt-"
-)
-
-// GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database
-func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...))
- return common.BytesToHash(data)
-}
-
-// StoreBloomTrieRoot writes the BloomTrie root assoctiated to the given section into the database
-func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], sectionIdx)
- db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes())
-}
-
-// BloomTrieIndexerBackend implements core.ChainIndexerBackend
-type BloomTrieIndexerBackend struct {
- diskdb, trieTable ethdb.Database
- odr OdrBackend
- triedb *trie.Database
- section, parentSectionSize, bloomTrieRatio uint64
- trie *trie.Trie
- sectionHeads []common.Hash
-}
-
-// NewBloomTrieIndexer creates a BloomTrie chain indexer
-func NewBloomTrieIndexer(db ethdb.Database, clientMode bool, odr OdrBackend) *core.ChainIndexer {
- trieTable := rawdb.NewTable(db, BloomTrieTablePrefix)
- backend := &BloomTrieIndexerBackend{
- diskdb: db,
- odr: odr,
- trieTable: trieTable,
- triedb: trie.NewDatabase(trieTable),
- }
- idb := rawdb.NewTable(db, "bltIndex-")
-
- if clientMode {
- backend.parentSectionSize = BloomTrieFrequency
- } else {
- backend.parentSectionSize = ethBloomBitsSection
- }
- backend.bloomTrieRatio = BloomTrieFrequency / backend.parentSectionSize
- backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio)
- return core.NewChainIndexer(db, idb, backend, BloomTrieFrequency, 0, time.Millisecond*100, "bloomtrie")
-}
-
-// fetchMissingNodes tries to retrieve the last entries of the latest trusted bloom trie from the
-// ODR backend in order to be able to add new entries and calculate subsequent root hashes
-func (b *BloomTrieIndexerBackend) fetchMissingNodes(ctx context.Context, section uint64, root common.Hash) error {
- indexCh := make(chan uint, types.BloomBitLength)
- type res struct {
- nodes *NodeSet
- err error
- }
- resCh := make(chan res, types.BloomBitLength)
- for i := 0; i < 20; i++ {
- go func() {
- for bitIndex := range indexCh {
- r := &BloomRequest{BloomTrieRoot: root, BloomTrieNum: section - 1, BitIdx: bitIndex, SectionIdxList: []uint64{section - 1}}
- for {
- if err := b.odr.Retrieve(ctx, r); err == ErrNoPeers {
- // if there are no peers to serve, retry later
- select {
- case <-ctx.Done():
- resCh <- res{nil, ctx.Err()}
- return
- case <-time.After(time.Second * 10):
- // stay in the loop and try again
- }
- } else {
- resCh <- res{r.Proofs, err}
- break
- }
- }
- }
- }()
- }
-
- for i := uint(0); i < types.BloomBitLength; i++ {
- indexCh <- i
- }
- close(indexCh)
- batch := b.trieTable.NewBatch()
- for i := uint(0); i < types.BloomBitLength; i++ {
- res := <-resCh
- if res.err != nil {
- return res.err
- }
- res.nodes.Store(batch)
- }
- return batch.Write()
-}
-
-// Reset implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
- root := types.EmptyRootHash
- if section > 0 {
- root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead)
- }
- var err error
- b.trie, err = trie.New(root, b.triedb)
- if err != nil && b.odr != nil {
- err = b.fetchMissingNodes(ctx, section, root)
- if err == nil {
- b.trie, err = trie.New(root, b.triedb)
- }
- }
- b.section = section
- return err
-}
-
-// Process implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Process(ctx context.Context, header *types.Header) error {
- num := header.Number.Uint64() - b.section*BloomTrieFrequency
- if (num+1)%b.parentSectionSize == 0 {
- b.sectionHeads[num/b.parentSectionSize] = header.Hash()
- }
- return nil
-}
-
-// Commit implements core.ChainIndexerBackend
-func (b *BloomTrieIndexerBackend) Commit() error {
- var compSize, decompSize uint64
-
- for i := uint(0); i < types.BloomBitLength; i++ {
- var encKey [10]byte
- binary.BigEndian.PutUint16(encKey[0:2], uint16(i))
- binary.BigEndian.PutUint64(encKey[2:10], b.section)
- var decomp []byte
- for j := uint64(0); j < b.bloomTrieRatio; j++ {
- data, err := rawdb.ReadBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j])
- if err != nil {
- return err
- }
- decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSectionSize/8))
- if err2 != nil {
- return err2
- }
- decomp = append(decomp, decompData...)
- }
- comp := bitutil.CompressBytes(decomp)
-
- decompSize += uint64(len(decomp))
- compSize += uint64(len(comp))
- if len(comp) > 0 {
- b.trie.Update(encKey[:], comp)
- } else {
- b.trie.Delete(encKey[:])
- }
- }
- root, err := b.trie.Commit(nil)
- if err != nil {
- return err
- }
- b.triedb.Commit(root, false)
-
- sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
- log.Info("Storing bloom trie", "section", b.section, "head", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression", float64(compSize)/float64(decompSize))
- StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root)
-
- return nil
-}
diff --git a/light/trie.go b/light/trie.go
deleted file mode 100644
index 7ae696f472..0000000000
--- a/light/trie.go
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/crypto"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/trie"
-)
-
-func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB {
- state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr))
- return state
-}
-
-func NewStateDatabase(ctx context.Context, head *types.Header, odr OdrBackend) state.Database {
- return &odrDatabase{ctx, StateTrieID(head), odr}
-}
-
-type odrDatabase struct {
- ctx context.Context
- id *TrieID
- backend OdrBackend
-}
-
-func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
- return &odrTrie{db: db, id: db.id}, nil
-}
-
-func (db *odrDatabase) OpenStorageTrie(addrHash, root common.Hash) (state.Trie, error) {
- return &odrTrie{db: db, id: StorageTrieID(db.id, addrHash, root)}, nil
-}
-
-func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie {
- switch t := t.(type) {
- case *odrTrie:
- cpy := &odrTrie{db: t.db, id: t.id}
- if t.trie != nil {
- cpytrie := *t.trie
- cpy.trie = &cpytrie
- }
- return cpy
- default:
- panic(fmt.Errorf("unknown trie type %T", t))
- }
-}
-
-func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
- if codeHash == sha3_nil {
- return nil, nil
- }
- if code, err := db.backend.Database().Get(codeHash[:]); err == nil {
- return code, nil
- }
- id := *db.id
- id.AccKey = addrHash[:]
- req := &CodeRequest{Id: &id, Hash: codeHash}
- err := db.backend.Retrieve(db.ctx, req)
- return req.Data, err
-}
-
-func (db *odrDatabase) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
- code, err := db.ContractCode(addrHash, codeHash)
- return len(code), err
-}
-
-func (db *odrDatabase) TrieDB() *trie.Database {
- return nil
-}
-
-type odrTrie struct {
- db *odrDatabase
- id *TrieID
- trie *trie.Trie
-}
-
-func (t *odrTrie) TryGet(key []byte) ([]byte, error) {
- key = crypto.Keccak256(key)
- var res []byte
- err := t.do(key, func() (err error) {
- res, err = t.trie.TryGet(key)
- return err
- })
- return res, err
-}
-
-func (t *odrTrie) TryUpdate(key, value []byte) error {
- key = crypto.Keccak256(key)
- return t.do(key, func() error {
- return t.trie.TryDelete(key)
- })
-}
-
-func (t *odrTrie) TryDelete(key []byte) error {
- key = crypto.Keccak256(key)
- return t.do(key, func() error {
- return t.trie.TryDelete(key)
- })
-}
-
-func (t *odrTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
- if t.trie == nil {
- return t.id.Root, nil
- }
- return t.trie.Commit(onleaf)
-}
-
-func (t *odrTrie) Hash() common.Hash {
- if t.trie == nil {
- return t.id.Root
- }
- return t.trie.Hash()
-}
-
-func (t *odrTrie) NodeIterator(startkey []byte) trie.NodeIterator {
- return newNodeIterator(t, startkey)
-}
-
-func (t *odrTrie) GetKey(sha []byte) []byte {
- return nil
-}
-
-func (t *odrTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error {
- return errors.New("not implemented, needs client/server interface split")
-}
-
-// do tries and retries to execute a function until it returns with no error or
-// an error type other than MissingNodeError
-func (t *odrTrie) do(key []byte, fn func() error) error {
- for {
- var err error
- if t.trie == nil {
- t.trie, err = trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database()))
- }
- if err == nil {
- err = fn()
- }
- if _, ok := err.(*trie.MissingNodeError); !ok {
- return err
- }
- r := &TrieRequest{Id: t.id, Key: key}
- if err := t.db.backend.Retrieve(t.db.ctx, r); err != nil {
- return err
- }
- }
-}
-
-type nodeIterator struct {
- trie.NodeIterator
- t *odrTrie
- err error
-}
-
-func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator {
- it := &nodeIterator{t: t}
- // Open the actual non-ODR trie if that hasn't happened yet.
- if t.trie == nil {
- it.do(func() error {
- t, err := trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database()))
- if err == nil {
- it.t.trie = t
- }
- return err
- })
- }
- it.do(func() error {
- it.NodeIterator = it.t.trie.NodeIterator(startkey)
- return it.NodeIterator.Error()
- })
- return it
-}
-
-func (it *nodeIterator) Next(descend bool) bool {
- var ok bool
- it.do(func() error {
- ok = it.NodeIterator.Next(descend)
- return it.NodeIterator.Error()
- })
- return ok
-}
-
-// do runs fn and attempts to fill in missing nodes by retrieving.
-func (it *nodeIterator) do(fn func() error) {
- var lasthash common.Hash
- for {
- it.err = fn()
- missing, ok := it.err.(*trie.MissingNodeError)
- if !ok {
- return
- }
- if missing.NodeHash == lasthash {
- it.err = fmt.Errorf("retrieve loop for trie node %x", missing.NodeHash)
- return
- }
- lasthash = missing.NodeHash
- r := &TrieRequest{Id: it.t.id, Key: nibblesToKey(missing.Path)}
- if it.err = it.t.db.backend.Retrieve(it.t.db.ctx, r); it.err != nil {
- return
- }
- }
-}
-
-func (it *nodeIterator) Error() error {
- if it.err != nil {
- return it.err
- }
- return it.NodeIterator.Error()
-}
-
-func nibblesToKey(nib []byte) []byte {
- if len(nib) > 0 && nib[len(nib)-1] == 0x10 {
- nib = nib[:len(nib)-1] // drop terminator
- }
- if len(nib)&1 == 1 {
- nib = append(nib, 0) // make even
- }
- key := make([]byte, len(nib)/2)
- for bi, ni := 0, 0; ni < len(nib); bi, ni = bi+1, ni+2 {
- key[bi] = nib[ni]<<4 | nib[ni+1]
- }
- return key
-}
diff --git a/light/trie_test.go b/light/trie_test.go
deleted file mode 100644
index bf4853e536..0000000000
--- a/light/trie_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "math/big"
- "testing"
-
- "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/core/vm"
- "github.com/XinFinOrg/XDPoSChain/params"
- "github.com/XinFinOrg/XDPoSChain/trie"
- "github.com/davecgh/go-spew/spew"
-)
-
-func TestNodeIterator(t *testing.T) {
- var (
- fulldb = rawdb.NewMemoryDatabase()
- lightdb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
- Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- genesis = gspec.MustCommit(fulldb)
- )
- gspec.MustCommit(lightdb)
- blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- panic(err)
- }
-
- ctx := context.Background()
- odr := &testOdr{sdb: fulldb, ldb: lightdb}
- head := blockchain.CurrentHeader()
- lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root)
- fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root)
- if err := diffTries(fullTrie, lightTrie); err != nil {
- t.Fatal(err)
- }
-}
-
-func diffTries(t1, t2 state.Trie) error {
- i1 := trie.NewIterator(t1.NodeIterator(nil))
- i2 := trie.NewIterator(t2.NodeIterator(nil))
- for i1.Next() && i2.Next() {
- if !bytes.Equal(i1.Key, i2.Key) {
- spew.Dump(i2)
- return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key)
- }
- if !bytes.Equal(i2.Value, i2.Value) {
- return fmt.Errorf("tries differ at key %x", i1.Key)
- }
- }
- switch {
- case i1.Err != nil:
- return fmt.Errorf("full trie iterator error: %v", i1.Err)
- case i2.Err != nil:
- return fmt.Errorf("light trie iterator error: %v", i1.Err)
- case i1.Next():
- return errors.New("full trie iterator has more k/v pairs")
- case i2.Next():
- return errors.New("light trie iterator has more k/v pairs")
- }
- return nil
-}
diff --git a/light/txpool.go b/light/txpool.go
deleted file mode 100644
index 80a2224585..0000000000
--- a/light/txpool.go
+++ /dev/null
@@ -1,582 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "fmt"
- "math/big"
- "sync"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
- "github.com/XinFinOrg/XDPoSChain/core/state"
- "github.com/XinFinOrg/XDPoSChain/core/txpool"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/ethdb"
- "github.com/XinFinOrg/XDPoSChain/event"
- "github.com/XinFinOrg/XDPoSChain/log"
- "github.com/XinFinOrg/XDPoSChain/params"
-)
-
-const (
- // chainHeadChanSize is the size of channel listening to ChainHeadEvent.
- chainHeadChanSize = 10
-)
-
-// txPermanent is the number of mined blocks after a mined transaction is
-// considered permanent and no rollback is expected
-var txPermanent = uint64(500)
-
-// TxPool implements the transaction pool for light clients, which keeps track
-// of the status of locally created transactions, detecting if they are included
-// in a block (mined) or rolled back. There are no queued transactions since we
-// always receive all locally signed transactions in the same order as they are
-// created.
-type TxPool struct {
- config *params.ChainConfig
- signer types.Signer
- quit chan bool
- txFeed event.Feed
- scope event.SubscriptionScope
- chainHeadCh chan core.ChainHeadEvent
- chainHeadSub event.Subscription
- mu sync.RWMutex
- chain *LightChain
- odr OdrBackend
- chainDb ethdb.Database
- relay TxRelayBackend
- head common.Hash
- nonce map[common.Address]uint64 // "pending" nonce
- pending map[common.Hash]*types.Transaction // pending transactions by tx hash
- mined map[common.Hash][]*types.Transaction // mined transactions by block hash
- clearIdx uint64 // earliest block nr that can contain mined tx info
-
- homestead bool
- eip2718 bool // Fork indicator whether we are in the eip2718 stage.
- eip1559 bool // Fork indicator whether we are in the eip1559 stage.
-}
-
-// TxRelayBackend provides an interface to the mechanism that forwards transacions
-// to the ETH network. The implementations of the functions should be non-blocking.
-//
-// Send instructs backend to forward new transactions
-// NewHead notifies backend about a new head after processed by the tx pool,
-//
-// including mined and rolled back transactions since the last event
-//
-// Discard notifies backend about transactions that should be discarded either
-//
-// because they have been replaced by a re-send or because they have been mined
-// long ago and no rollback is expected
-type TxRelayBackend interface {
- Send(txs types.Transactions)
- NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash)
- Discard(hashes []common.Hash)
-}
-
-// NewTxPool creates a new light transaction pool
-func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool {
- pool := &TxPool{
- config: config,
- signer: types.LatestSigner(config),
- nonce: make(map[common.Address]uint64),
- pending: make(map[common.Hash]*types.Transaction),
- mined: make(map[common.Hash][]*types.Transaction),
- quit: make(chan bool),
- chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
- chain: chain,
- relay: relay,
- odr: chain.Odr(),
- chainDb: chain.Odr().Database(),
- head: chain.CurrentHeader().Hash(),
- clearIdx: chain.CurrentHeader().Number.Uint64(),
- }
- // Subscribe events from blockchain
- pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh)
- go pool.eventLoop()
-
- return pool
-}
-
-// currentState returns the light state of the current head header
-func (p *TxPool) currentState(ctx context.Context) *state.StateDB {
- return NewState(ctx, p.chain.CurrentHeader(), p.odr)
-}
-
-// GetNonce returns the "pending" nonce of a given address. It always queries
-// the nonce belonging to the latest header too in order to detect if another
-// client using the same key sent a transaction.
-func (p *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
- state := p.currentState(ctx)
- nonce := state.GetNonce(addr)
- if state.Error() != nil {
- return 0, state.Error()
- }
- sn, ok := p.nonce[addr]
- if ok && sn > nonce {
- nonce = sn
- }
- if !ok || sn < nonce {
- p.nonce[addr] = nonce
- }
- return nonce, nil
-}
-
-// txStateChanges stores the recent changes between pending/mined states of
-// transactions. True means mined, false means rolled back, no entry means no change
-type txStateChanges map[common.Hash]bool
-
-// setState sets the status of a tx to either recently mined or recently rolled back
-func (txc txStateChanges) setState(txHash common.Hash, mined bool) {
- val, ent := txc[txHash]
- if ent && (val != mined) {
- delete(txc, txHash)
- } else {
- txc[txHash] = mined
- }
-}
-
-// getLists creates lists of mined and rolled back tx hashes
-func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Hash) {
- for hash, val := range txc {
- if val {
- mined = append(mined, hash)
- } else {
- rollback = append(rollback, hash)
- }
- }
- return
-}
-
-// checkMinedTxs checks newly added blocks for the currently pending transactions
-// and marks them as mined if necessary. It also stores block position in the db
-// and adds them to the received txStateChanges map.
-func (p *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error {
- // If no transactions are pending, we don't care about anything
- if len(p.pending) == 0 {
- return nil
- }
- block, err := GetBlock(ctx, p.odr, hash, number)
- if err != nil {
- return err
- }
- // Gather all the local transaction mined in this block
- list := p.mined[hash]
- for _, tx := range block.Transactions() {
- if _, ok := p.pending[tx.Hash()]; ok {
- list = append(list, tx)
- }
- }
- // If some transactions have been mined, write the needed data to disk and update
- if list != nil {
- // Retrieve all the receipts belonging to this block and write the lookup table
- if _, err := GetBlockReceipts(ctx, p.odr, hash, number); err != nil { // ODR caches, ignore results
- return err
- }
- rawdb.WriteTxLookupEntriesByBlock(p.chainDb, block)
- // Update the transaction pool's state
- for _, tx := range list {
- delete(p.pending, tx.Hash())
- txc.setState(tx.Hash(), true)
- }
- p.mined[hash] = list
- }
- return nil
-}
-
-// rollbackTxs marks the transactions contained in recently rolled back blocks
-// as rolled back. It also removes any positional lookup entries.
-func (p *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
- batch := p.chainDb.NewBatch()
- if list, ok := p.mined[hash]; ok {
- for _, tx := range list {
- txHash := tx.Hash()
- rawdb.DeleteTxLookupEntry(batch, txHash)
- p.pending[txHash] = tx
- txc.setState(txHash, false)
- }
- delete(p.mined, hash)
- }
- batch.Write()
-}
-
-// reorgOnNewHead sets a new head header, processing (and rolling back if necessary)
-// the blocks since the last known head and returns a txStateChanges map containing
-// the recently mined and rolled back transaction hashes. If an error (context
-// timeout) occurs during checking new blocks, it leaves the locally known head
-// at the latest checked block and still returns a valid txStateChanges, making it
-// possible to continue checking the missing blocks at the next chain head event
-func (p *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) {
- txc := make(txStateChanges)
- oldh := p.chain.GetHeaderByHash(p.head)
- newh := newHeader
- // find common ancestor, create list of rolled back and new block hashes
- var oldHashes, newHashes []common.Hash
- for oldh.Hash() != newh.Hash() {
- if oldh.Number.Uint64() >= newh.Number.Uint64() {
- oldHashes = append(oldHashes, oldh.Hash())
- oldh = p.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1)
- }
- if oldh.Number.Uint64() < newh.Number.Uint64() {
- newHashes = append(newHashes, newh.Hash())
- newh = p.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1)
- if newh == nil {
- // happens when CHT syncing, nothing to do
- newh = oldh
- }
- }
- }
- if oldh.Number.Uint64() < p.clearIdx {
- p.clearIdx = oldh.Number.Uint64()
- }
- // roll back old blocks
- for _, hash := range oldHashes {
- p.rollbackTxs(hash, txc)
- }
- p.head = oldh.Hash()
- // check mined txs of new blocks (array is in reversed order)
- for i := len(newHashes) - 1; i >= 0; i-- {
- hash := newHashes[i]
- if err := p.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil {
- return txc, err
- }
- p.head = hash
- }
-
- // clear old mined tx entries of old blocks
- if idx := newHeader.Number.Uint64(); idx > p.clearIdx+txPermanent {
- idx2 := idx - txPermanent
- if len(p.mined) > 0 {
- for i := p.clearIdx; i < idx2; i++ {
- hash := rawdb.ReadCanonicalHash(p.chainDb, i)
- if list, ok := p.mined[hash]; ok {
- hashes := make([]common.Hash, len(list))
- for i, tx := range list {
- hashes[i] = tx.Hash()
- }
- p.relay.Discard(hashes)
- delete(p.mined, hash)
- }
- }
- }
- p.clearIdx = idx2
- }
-
- return txc, nil
-}
-
-// blockCheckTimeout is the time limit for checking new blocks for mined
-// transactions. Checking resumes at the next chain head event if timed out.
-const blockCheckTimeout = time.Second * 3
-
-// eventLoop processes chain head events and also notifies the tx relay backend
-// about the new head hash and tx state changes
-func (p *TxPool) eventLoop() {
- for {
- select {
- case ev := <-p.chainHeadCh:
- p.setNewHead(ev.Block.Header())
- // hack in order to avoid hogging the lock; this part will
- // be replaced by a subsequent PR.
- time.Sleep(time.Millisecond)
-
- // System stopped
- case <-p.chainHeadSub.Err():
- return
- }
- }
-}
-
-func (p *TxPool) setNewHead(head *types.Header) {
- p.mu.Lock()
- defer p.mu.Unlock()
-
- ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout)
- defer cancel()
-
- txc, _ := p.reorgOnNewHead(ctx, head)
- m, r := txc.getLists()
- p.relay.NewHead(p.head, m, r)
-
- // Update fork indicator by next pending block number
- next := new(big.Int).Add(head.Number, big.NewInt(1))
- p.homestead = p.config.IsHomestead(head.Number)
- p.eip2718 = p.config.IsEIP1559(next)
- p.eip1559 = p.config.IsEIP1559(next)
-}
-
-// Stop stops the light transaction pool
-func (p *TxPool) Stop() {
- // Unsubscribe all subscriptions registered from txpool
- p.scope.Close()
- // Unsubscribe subscriptions registered from blockchain
- p.chainHeadSub.Unsubscribe()
- close(p.quit)
- log.Info("Transaction pool stopped")
-}
-
-// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and
-// starts sending event to the given channel.
-func (p *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return p.scope.Track(p.txFeed.Subscribe(ch))
-}
-
-// Stats returns the number of currently pending (locally created) transactions
-func (p *TxPool) Stats() (pending int) {
- p.mu.RLock()
- defer p.mu.RUnlock()
-
- pending = len(p.pending)
- return
-}
-
-// validateTx checks whether a transaction is valid according to the consensus rules.
-func (p *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error {
- // Validate sender
- var (
- from common.Address
- err error
- )
-
- // check if sender is in black list
- if common.IsInBlacklist(tx.From()) {
- return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
- }
- // check if receiver is in black list
- if common.IsInBlacklist(tx.To()) {
- return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex())
- }
-
- // validate minFee slot for XDCZ
- if tx.IsXDCZApplyTransaction() {
- copyState := p.currentState(ctx).Copy()
- if err := core.ValidateXDCZApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
- return err
- }
- }
- // validate balance slot, token decimal for XDCX
- if tx.IsXDCXApplyTransaction() {
- copyState := p.currentState(ctx).Copy()
- if err := core.ValidateXDCXApplyTransaction(p.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:])); err != nil {
- return err
- }
- }
-
- // Validate the transaction sender and it's sig. Throw
- // if the from fields is invalid.
- if from, err = types.Sender(p.signer, tx); err != nil {
- return txpool.ErrInvalidSender
- }
- // Last but not least check for nonce errors
- currentState := p.currentState(ctx)
- if n := currentState.GetNonce(from); n > tx.Nonce() {
- return core.ErrNonceTooLow
- }
-
- // Check the transaction doesn't exceed the current
- // block limit gas.
- header := p.chain.GetHeaderByHash(p.head)
- if header.GasLimit < tx.Gas() {
- return txpool.ErrGasLimit
- }
-
- // Transactions can't be negative. This may never happen
- // using RLP decoded transactions but may occur if you create
- // a transaction using the RPC for example.
- if tx.Value().Sign() < 0 {
- return txpool.ErrNegativeValue
- }
-
- // Transactor should have enough funds to cover the costs
- // cost == V + GP * GL
- if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 {
- return core.ErrInsufficientFunds
- }
-
- // Should supply enough intrinsic gas
- gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, p.homestead, p.eip1559)
- if err != nil {
- return err
- }
- if tx.Gas() < gas {
- return core.ErrIntrinsicGas
- }
- return currentState.Error()
-}
-
-// add validates a new transaction and sets its state pending if processable.
-// It also updates the locally stored nonce if necessary.
-func (p *TxPool) add(ctx context.Context, tx *types.Transaction) error {
- hash := tx.Hash()
-
- if p.pending[hash] != nil {
- return fmt.Errorf("known transaction (%x)", hash[:4])
- }
- err := p.validateTx(ctx, tx)
- if err != nil {
- return err
- }
-
- if _, ok := p.pending[hash]; !ok {
- p.pending[hash] = tx
-
- nonce := tx.Nonce() + 1
-
- addr, _ := types.Sender(p.signer, tx)
- if nonce > p.nonce[addr] {
- p.nonce[addr] = nonce
- }
-
- // Notify the subscribers. This event is posted in a goroutine
- // because it's possible that somewhere during the post "Remove transaction"
- // gets called which will then wait for the global tx pool lock and deadlock.
- go p.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
- }
-
- // Print a log message if low enough level is set
- if log.Enabled(log.LevelDebug) {
- from, _ := types.Sender(p.signer, tx)
- log.Debug("Pooled new transaction", "hash", hash, "from", from, "to", tx.To())
- }
- return nil
-}
-
-// Add adds a transaction to the pool if valid and passes it to the tx relay
-// backend
-func (p *TxPool) Add(ctx context.Context, tx *types.Transaction) error {
- p.mu.Lock()
- defer p.mu.Unlock()
- data, err := tx.MarshalBinary()
- if err != nil {
- return err
- }
-
- if err := p.add(ctx, tx); err != nil {
- return err
- }
- //fmt.Println("Send", tx.Hash())
- p.relay.Send(types.Transactions{tx})
-
- p.chainDb.Put(tx.Hash().Bytes(), data)
- return nil
-}
-
-// AddTransactions adds all valid transactions to the pool and passes them to
-// the tx relay backend
-func (p *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) {
- p.mu.Lock()
- defer p.mu.Unlock()
- var sendTx types.Transactions
-
- for _, tx := range txs {
- if err := p.add(ctx, tx); err == nil {
- sendTx = append(sendTx, tx)
- }
- }
- if len(sendTx) > 0 {
- p.relay.Send(sendTx)
- }
-}
-
-// GetTransaction returns a transaction if it is contained in the pool
-// and nil otherwise.
-func (p *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
- // check the txs first
- if tx, ok := p.pending[hash]; ok {
- return tx
- }
- return nil
-}
-
-// GetTransactions returns all currently processable transactions.
-// The returned slice may be modified by the caller.
-func (p *TxPool) GetTransactions() (txs types.Transactions, err error) {
- p.mu.RLock()
- defer p.mu.RUnlock()
-
- txs = make(types.Transactions, len(p.pending))
- i := 0
- for _, tx := range p.pending {
- txs[i] = tx
- i++
- }
- return txs, nil
-}
-
-// Content retrieves the data content of the transaction pool, returning all the
-// pending as well as queued transactions, grouped by account and nonce.
-func (p *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
- p.mu.RLock()
- defer p.mu.RUnlock()
-
- // Retrieve all the pending transactions and sort by account and by nonce
- pending := make(map[common.Address]types.Transactions)
- for _, tx := range p.pending {
- account, _ := types.Sender(p.signer, tx)
- pending[account] = append(pending[account], tx)
- }
- // There are no queued transactions in a light pool, just return an empty map
- queued := make(map[common.Address]types.Transactions)
- return pending, queued
-}
-
-// ContentFrom retrieves the data content of the transaction pool, returning the
-// pending as well as queued transactions of this address, grouped by nonce.
-func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.Transactions) {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
- // Retrieve the pending transactions and sort by nonce
- var pending types.Transactions
- for _, tx := range pool.pending {
- account, _ := types.Sender(pool.signer, tx)
- if account != addr {
- continue
- }
- pending = append(pending, tx)
- }
- // There are no queued transactions in a light pool, just return an empty map
- return pending, types.Transactions{}
-}
-
-// RemoveTransactions removes all given transactions from the pool.
-func (p *TxPool) RemoveTransactions(txs types.Transactions) {
- p.mu.Lock()
- defer p.mu.Unlock()
- var hashes []common.Hash
- for _, tx := range txs {
- //self.RemoveTx(tx.Hash())
- hash := tx.Hash()
- delete(p.pending, hash)
- p.chainDb.Delete(hash[:])
- hashes = append(hashes, hash)
- }
- p.relay.Discard(hashes)
-}
-
-// RemoveTx removes the transaction with the given hash from the pool.
-func (p *TxPool) RemoveTx(hash common.Hash) {
- p.mu.Lock()
- defer p.mu.Unlock()
- // delete from pending pool
- delete(p.pending, hash)
- p.chainDb.Delete(hash[:])
- p.relay.Discard([]common.Hash{hash})
-}
diff --git a/light/txpool_test.go b/light/txpool_test.go
deleted file mode 100644
index dc7ae41482..0000000000
--- a/light/txpool_test.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2016 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package light
-
-import (
- "context"
- "math"
- "math/big"
- "testing"
- "time"
-
- "github.com/XinFinOrg/XDPoSChain/core/rawdb"
-
- "github.com/XinFinOrg/XDPoSChain/common"
- "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
- "github.com/XinFinOrg/XDPoSChain/core"
- "github.com/XinFinOrg/XDPoSChain/core/types"
- "github.com/XinFinOrg/XDPoSChain/core/vm"
- "github.com/XinFinOrg/XDPoSChain/params"
-)
-
-type testTxRelay struct {
- send, discard, mined chan int
-}
-
-func (r *testTxRelay) Send(txs types.Transactions) {
- r.send <- len(txs)
-}
-
-func (r *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) {
- m := len(mined)
- if m != 0 {
- r.mined <- m
- }
-}
-
-func (r *testTxRelay) Discard(hashes []common.Hash) {
- r.discard <- len(hashes)
-}
-
-const poolTestTxs = 1000
-const poolTestBlocks = 100
-
-// test tx 0..n-1
-var testTx [poolTestTxs]*types.Transaction
-
-// txs sent before block i
-func sentTx(i int) int {
- return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs)
-}
-
-// txs included in block i or before that (minedTx(i) <= sentTx(i))
-func minedTx(i int) int {
- return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs)
-}
-
-func txPoolTestChainGen(i int, block *core.BlockGen) {
- s := minedTx(i)
- e := minedTx(i + 1)
- for i := s; i < e; i++ {
- block.AddTx(testTx[i])
- }
-}
-
-func TestTxPool(t *testing.T) {
- for i := range testTx {
- testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), types.HomesteadSigner{}, testBankKey)
- }
-
- var (
- sdb = rawdb.NewMemoryDatabase()
- ldb = rawdb.NewMemoryDatabase()
- gspec = core.Genesis{
- Alloc: types.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
- BaseFee: big.NewInt(params.InitialBaseFee),
- }
- genesis = gspec.MustCommit(sdb)
- )
- gspec.MustCommit(ldb)
- // Assemble the test environment
- blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
- gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
- if _, err := blockchain.InsertChain(gchain); err != nil {
- panic(err)
- }
-
- odr := &testOdr{sdb: sdb, ldb: ldb}
- relay := &testTxRelay{
- send: make(chan int, 1),
- discard: make(chan int, 1),
- mined: make(chan int, 1),
- }
- lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
- txPermanent = 50
- pool := NewTxPool(params.TestChainConfig, lightchain, relay)
- ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
- defer cancel()
-
- for ii, block := range gchain {
- i := ii + 1
- s := sentTx(i - 1)
- e := sentTx(i)
- for i := s; i < e; i++ {
- pool.Add(ctx, testTx[i])
- got := <-relay.send
- exp := 1
- if got != exp {
- t.Errorf("relay.Send expected len = %d, got %d", exp, got)
- }
- }
-
- if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil {
- panic(err)
- }
-
- got := <-relay.mined
- exp := minedTx(i) - minedTx(i-1)
- if got != exp {
- t.Errorf("relay.NewHead expected len(mined) = %d, got %d", exp, got)
- }
-
- exp = 0
- if i > int(txPermanent)+1 {
- exp = minedTx(i-int(txPermanent)-1) - minedTx(i-int(txPermanent)-2)
- }
- if exp != 0 {
- got = <-relay.discard
- if got != exp {
- t.Errorf("relay.Discard expected len = %d, got %d", exp, got)
- }
- }
- }
-}