forked from cerc-io/ipld-eth-server
Update dependencies
- uses newer version of go-ethereum required for go1.11
This commit is contained in:
+70
-36
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -47,6 +48,7 @@ var (
|
||||
// interface. It only does header validation during chain insertion.
|
||||
type LightChain struct {
|
||||
hc *core.HeaderChain
|
||||
indexerConfig *IndexerConfig
|
||||
chainDb ethdb.Database
|
||||
odr OdrBackend
|
||||
chainFeed event.Feed
|
||||
@@ -80,13 +82,14 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
|
||||
bc := &LightChain{
|
||||
chainDb: odr.Database(),
|
||||
odr: odr,
|
||||
quit: make(chan struct{}),
|
||||
bodyCache: bodyCache,
|
||||
bodyRLPCache: bodyRLPCache,
|
||||
blockCache: blockCache,
|
||||
engine: engine,
|
||||
chainDb: odr.Database(),
|
||||
indexerConfig: odr.IndexerConfig(),
|
||||
odr: odr,
|
||||
quit: make(chan struct{}),
|
||||
bodyCache: bodyCache,
|
||||
bodyRLPCache: bodyRLPCache,
|
||||
blockCache: blockCache,
|
||||
engine: engine,
|
||||
}
|
||||
var err error
|
||||
bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt)
|
||||
@@ -115,19 +118,19 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.
|
||||
}
|
||||
|
||||
// addTrustedCheckpoint adds a trusted checkpoint to the blockchain
|
||||
func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) {
|
||||
func (self *LightChain) addTrustedCheckpoint(cp TrustedCheckpoint) {
|
||||
if self.odr.ChtIndexer() != nil {
|
||||
StoreChtRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot)
|
||||
self.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
|
||||
StoreChtRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.CHTRoot)
|
||||
self.odr.ChtIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead)
|
||||
}
|
||||
if self.odr.BloomTrieIndexer() != nil {
|
||||
StoreBloomTrieRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot)
|
||||
self.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
|
||||
StoreBloomTrieRoot(self.chainDb, cp.SectionIdx, cp.SectionHead, cp.BloomRoot)
|
||||
self.odr.BloomTrieIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead)
|
||||
}
|
||||
if self.odr.BloomIndexer() != nil {
|
||||
self.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead)
|
||||
self.odr.BloomIndexer().AddCheckpoint(cp.SectionIdx, cp.SectionHead)
|
||||
}
|
||||
log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.sectionIdx+1)*CHTFrequencyClient-1, "hash", cp.sectionHead)
|
||||
log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.SectionIdx+1)*self.indexerConfig.ChtSize-1, "hash", cp.SectionHead)
|
||||
}
|
||||
|
||||
func (self *LightChain) getProcInterrupt() bool {
|
||||
@@ -142,7 +145,7 @@ func (self *LightChain) Odr() OdrBackend {
|
||||
// loadLastState loads the last known chain state from the database. This method
|
||||
// assumes that the chain manager mutex is held.
|
||||
func (self *LightChain) loadLastState() error {
|
||||
if head := core.GetHeadHeaderHash(self.chainDb); head == (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadHeaderHash(self.chainDb); head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
self.Reset()
|
||||
} else {
|
||||
@@ -189,12 +192,9 @@ func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
|
||||
defer bc.mu.Unlock()
|
||||
|
||||
// Prepare the genesis block and reinitialise the chain
|
||||
if err := core.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||
log.Crit("Failed to write genesis block TD", "err", err)
|
||||
}
|
||||
if err := core.WriteBlock(bc.chainDb, genesis); err != nil {
|
||||
log.Crit("Failed to write genesis block", "err", err)
|
||||
}
|
||||
rawdb.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||
rawdb.WriteBlock(bc.chainDb, genesis)
|
||||
|
||||
bc.genesisBlock = genesis
|
||||
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
||||
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||
@@ -223,7 +223,11 @@ func (self *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.B
|
||||
body := cached.(*types.Body)
|
||||
return body, nil
|
||||
}
|
||||
body, err := GetBody(ctx, self.odr, hash, self.hc.GetBlockNumber(hash))
|
||||
number := self.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil, errors.New("unknown block")
|
||||
}
|
||||
body, err := GetBody(ctx, self.odr, hash, *number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -239,7 +243,11 @@ func (self *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.R
|
||||
if cached, ok := self.bodyRLPCache.Get(hash); ok {
|
||||
return cached.(rlp.RawValue), nil
|
||||
}
|
||||
body, err := GetBodyRLP(ctx, self.odr, hash, self.hc.GetBlockNumber(hash))
|
||||
number := self.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil, errors.New("unknown block")
|
||||
}
|
||||
body, err := GetBodyRLP(ctx, self.odr, hash, *number)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -274,7 +282,11 @@ func (self *LightChain) GetBlock(ctx context.Context, hash common.Hash, number u
|
||||
// GetBlockByHash retrieves a block from the database or ODR service by hash,
|
||||
// caching it if found.
|
||||
func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return self.GetBlock(ctx, hash, self.hc.GetBlockNumber(hash))
|
||||
number := self.hc.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil, errors.New("unknown block")
|
||||
}
|
||||
return self.GetBlock(ctx, hash, *number)
|
||||
}
|
||||
|
||||
// GetBlockByNumber retrieves a block from the database or ODR service by
|
||||
@@ -423,6 +435,18 @@ func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
|
||||
return self.hc.GetBlockHashesFromHash(hash, max)
|
||||
}
|
||||
|
||||
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
|
||||
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
|
||||
// number of blocks to be individually checked before we reach the canonical chain.
|
||||
//
|
||||
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
||||
func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
||||
bc.chainmu.Lock()
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||
}
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
@@ -442,22 +466,32 @@ func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64)
|
||||
func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() }
|
||||
|
||||
func (self *LightChain) SyncCht(ctx context.Context) bool {
|
||||
// If we don't have a CHT indexer, abort
|
||||
if self.odr.ChtIndexer() == nil {
|
||||
return false
|
||||
}
|
||||
headNum := self.CurrentHeader().Number.Uint64()
|
||||
chtCount, _, _ := self.odr.ChtIndexer().Sections()
|
||||
if headNum+1 < chtCount*CHTFrequencyClient {
|
||||
num := chtCount*CHTFrequencyClient - 1
|
||||
header, err := GetHeaderByNumber(ctx, self.odr, num)
|
||||
if header != nil && err == nil {
|
||||
self.mu.Lock()
|
||||
if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
|
||||
self.hc.SetCurrentHeader(header)
|
||||
}
|
||||
self.mu.Unlock()
|
||||
return true
|
||||
// Ensure the remote CHT head is ahead of us
|
||||
head := self.CurrentHeader().Number.Uint64()
|
||||
sections, _, _ := self.odr.ChtIndexer().Sections()
|
||||
|
||||
latest := sections*self.indexerConfig.ChtSize - 1
|
||||
if clique := self.hc.Config().Clique; clique != nil {
|
||||
latest -= latest % clique.Epoch // epoch snapshot for clique
|
||||
}
|
||||
if head >= latest {
|
||||
return false
|
||||
}
|
||||
// Retrieve the latest useful header and update to it
|
||||
if header, err := GetHeaderByNumber(ctx, self.odr, latest); header != nil && err == nil {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
// Ensure the chain didn't move past the latest block while retrieving it
|
||||
if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
|
||||
log.Info("Updated latest header based on CHT", "number", header.Number, "hash", header.Hash())
|
||||
self.hc.SetCurrentHeader(header)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+13
-7
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -51,10 +52,10 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
|
||||
// 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, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
gspec := core.Genesis{Config: params.TestChainConfig}
|
||||
genesis := gspec.MustCommit(db)
|
||||
blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFaker())
|
||||
blockchain, _ := NewLightChain(&dummyOdr{db: db, indexerConfig: TestClientIndexerConfig}, gspec.Config, ethash.NewFaker())
|
||||
|
||||
// Create and inject the requested chain
|
||||
if n == 0 {
|
||||
@@ -68,7 +69,7 @@ func newCanonical(n int) (ethdb.Database, *LightChain, error) {
|
||||
|
||||
// newTestLightChain creates a LightChain that doesn't validate anything.
|
||||
func newTestLightChain() *LightChain {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
db := ethdb.NewMemDatabase()
|
||||
gspec := &core.Genesis{
|
||||
Difficulty: big.NewInt(1),
|
||||
Config: params.TestChainConfig,
|
||||
@@ -122,8 +123,8 @@ func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error
|
||||
}
|
||||
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
|
||||
lightchain.mu.Lock()
|
||||
core.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash)))
|
||||
core.WriteHeader(lightchain.chainDb, header)
|
||||
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.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
@@ -264,7 +265,8 @@ func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.
|
||||
|
||||
type dummyOdr struct {
|
||||
OdrBackend
|
||||
db ethdb.Database
|
||||
db ethdb.Database
|
||||
indexerConfig *IndexerConfig
|
||||
}
|
||||
|
||||
func (odr *dummyOdr) Database() ethdb.Database {
|
||||
@@ -275,6 +277,10 @@ func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (odr *dummyOdr) IndexerConfig() *IndexerConfig {
|
||||
return odr.indexerConfig
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -325,7 +331,7 @@ func TestBadHeaderHashes(t *testing.T) {
|
||||
func TestReorgBadHeaderHashes(t *testing.T) {
|
||||
bc := newTestLightChain()
|
||||
|
||||
// Create a chain, import and ban aferwards
|
||||
// Create a chain, import and ban afterwards
|
||||
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||
|
||||
if _, err := bc.InsertHeaderChain(headers, 1); err != nil {
|
||||
|
||||
+16
-8
@@ -20,10 +20,12 @@ package light
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
@@ -32,6 +34,9 @@ import (
|
||||
// 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
|
||||
@@ -39,6 +44,7 @@ type OdrBackend interface {
|
||||
BloomTrieIndexer() *core.ChainIndexer
|
||||
BloomIndexer() *core.ChainIndexer
|
||||
Retrieve(ctx context.Context, req OdrRequest) error
|
||||
IndexerConfig() *IndexerConfig
|
||||
}
|
||||
|
||||
// OdrRequest is an interface for retrieval requests
|
||||
@@ -112,7 +118,7 @@ type BlockRequest struct {
|
||||
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *BlockRequest) StoreResult(db ethdb.Database) {
|
||||
core.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp)
|
||||
rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp)
|
||||
}
|
||||
|
||||
// ReceiptsRequest is the ODR request type for retrieving block bodies
|
||||
@@ -125,12 +131,13 @@ type ReceiptsRequest struct {
|
||||
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
|
||||
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts)
|
||||
rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts)
|
||||
}
|
||||
|
||||
// ChtRequest is the ODR request type for state/storage trie entries
|
||||
type ChtRequest struct {
|
||||
OdrRequest
|
||||
Config *IndexerConfig
|
||||
ChtNum, BlockNum uint64
|
||||
ChtRoot common.Hash
|
||||
Header *types.Header
|
||||
@@ -140,16 +147,17 @@ type ChtRequest struct {
|
||||
|
||||
// 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
|
||||
core.WriteHeader(db, req.Header)
|
||||
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
||||
core.WriteTd(db, hash, num, req.Td)
|
||||
core.WriteCanonicalHash(db, hash, num)
|
||||
|
||||
rawdb.WriteHeader(db, req.Header)
|
||||
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
|
||||
Config *IndexerConfig
|
||||
BloomTrieNum uint64
|
||||
BitIdx uint
|
||||
SectionIdxList []uint64
|
||||
@@ -161,11 +169,11 @@ type BloomRequest struct {
|
||||
// StoreResult stores the retrieved data in local database
|
||||
func (req *BloomRequest) StoreResult(db ethdb.Database) {
|
||||
for i, sectionIdx := range req.SectionIdxList {
|
||||
sectionHead := core.GetCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1)
|
||||
sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*req.Config.BloomTrieSize-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.
|
||||
core.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i])
|
||||
rawdb.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i])
|
||||
}
|
||||
}
|
||||
|
||||
+28
-10
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -54,8 +55,9 @@ var (
|
||||
|
||||
type testOdr struct {
|
||||
OdrBackend
|
||||
sdb, ldb ethdb.Database
|
||||
disable bool
|
||||
indexerConfig *IndexerConfig
|
||||
sdb, ldb ethdb.Database
|
||||
disable bool
|
||||
}
|
||||
|
||||
func (odr *testOdr) Database() ethdb.Database {
|
||||
@@ -70,9 +72,15 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
|
||||
}
|
||||
switch req := req.(type) {
|
||||
case *BlockRequest:
|
||||
req.Rlp = core.GetBodyRLP(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
|
||||
number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
|
||||
if number != nil {
|
||||
req.Rlp = rawdb.ReadBodyRLP(odr.sdb, req.Hash, *number)
|
||||
}
|
||||
case *ReceiptsRequest:
|
||||
req.Receipts = core.GetBlockReceipts(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
|
||||
number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash)
|
||||
if number != nil {
|
||||
req.Receipts = rawdb.ReadReceipts(odr.sdb, req.Hash, *number)
|
||||
}
|
||||
case *TrieRequest:
|
||||
t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb))
|
||||
nodes := NewNodeSet()
|
||||
@@ -85,6 +93,10 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (odr *testOdr) IndexerConfig() *IndexerConfig {
|
||||
return odr.indexerConfig
|
||||
}
|
||||
|
||||
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) }
|
||||
@@ -108,9 +120,15 @@ 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 {
|
||||
receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash))
|
||||
number := rawdb.ReadHeaderNumber(db, bhash)
|
||||
if number != nil {
|
||||
receipts = rawdb.ReadReceipts(db, bhash, *number)
|
||||
}
|
||||
} else {
|
||||
receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash))
|
||||
number := rawdb.ReadHeaderNumber(db, bhash)
|
||||
if number != nil {
|
||||
receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, *number)
|
||||
}
|
||||
}
|
||||
if receipts == nil {
|
||||
return nil, nil
|
||||
@@ -232,8 +250,8 @@ func testChainGen(i int, block *core.BlockGen) {
|
||||
|
||||
func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
||||
var (
|
||||
sdb, _ = ethdb.NewMemDatabase()
|
||||
ldb, _ = ethdb.NewMemDatabase()
|
||||
sdb = ethdb.NewMemDatabase()
|
||||
ldb = ethdb.NewMemDatabase()
|
||||
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
|
||||
genesis = gspec.MustCommit(sdb)
|
||||
)
|
||||
@@ -245,7 +263,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
odr := &testOdr{sdb: sdb, ldb: ldb}
|
||||
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
|
||||
lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -260,7 +278,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
||||
|
||||
test := func(expFail int) {
|
||||
for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ {
|
||||
bhash := core.GetCanonicalHash(sdb, 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)
|
||||
|
||||
+29
-24
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -31,10 +32,10 @@ var sha3_nil = crypto.Keccak256Hash(nil)
|
||||
|
||||
func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) {
|
||||
db := odr.Database()
|
||||
hash := core.GetCanonicalHash(db, number)
|
||||
hash := rawdb.ReadCanonicalHash(db, number)
|
||||
if (hash != common.Hash{}) {
|
||||
// if there is a canonical hash, there is a header too
|
||||
header := core.GetHeader(db, hash, number)
|
||||
header := rawdb.ReadHeader(db, hash, number)
|
||||
if header == nil {
|
||||
panic("Canonical hash present but header not found")
|
||||
}
|
||||
@@ -47,21 +48,21 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
|
||||
)
|
||||
if odr.ChtIndexer() != nil {
|
||||
chtCount, sectionHeadNum, sectionHead = odr.ChtIndexer().Sections()
|
||||
canonicalHash := core.GetCanonicalHash(db, sectionHeadNum)
|
||||
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
|
||||
sectionHeadNum = chtCount*odr.IndexerConfig().ChtSize - 1
|
||||
sectionHead = odr.ChtIndexer().SectionHead(chtCount - 1)
|
||||
canonicalHash = core.GetCanonicalHash(db, sectionHeadNum)
|
||||
canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
if number >= chtCount*CHTFrequencyClient {
|
||||
if number >= chtCount*odr.IndexerConfig().ChtSize {
|
||||
return nil, ErrNoTrustedCht
|
||||
}
|
||||
r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number}
|
||||
r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number, Config: odr.IndexerConfig()}
|
||||
if err := odr.Retrieve(ctx, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,7 +70,7 @@ func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*typ
|
||||
}
|
||||
|
||||
func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) {
|
||||
hash := core.GetCanonicalHash(odr.Database(), number)
|
||||
hash := rawdb.ReadCanonicalHash(odr.Database(), number)
|
||||
if (hash != common.Hash{}) {
|
||||
return hash, nil
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (commo
|
||||
|
||||
// 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 := core.GetBodyRLP(odr.Database(), hash, number); data != nil {
|
||||
if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil {
|
||||
return data, nil
|
||||
}
|
||||
r := &BlockRequest{Hash: hash, Number: number}
|
||||
@@ -111,7 +112,7 @@ func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint6
|
||||
// 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 := core.GetHeader(odr.Database(), hash, number)
|
||||
header := rawdb.ReadHeader(odr.Database(), hash, number)
|
||||
if header == nil {
|
||||
return nil, ErrNoHeader
|
||||
}
|
||||
@@ -127,7 +128,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint
|
||||
// in a block given by its hash.
|
||||
func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
|
||||
// Retrieve the potentially incomplete receipts from disk or network
|
||||
receipts := core.GetBlockReceipts(odr.Database(), hash, number)
|
||||
receipts := rawdb.ReadReceipts(odr.Database(), hash, number)
|
||||
if receipts == nil {
|
||||
r := &ReceiptsRequest{Hash: hash, Number: number}
|
||||
if err := odr.Retrieve(ctx, r); err != nil {
|
||||
@@ -141,11 +142,13 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
genesis := core.GetCanonicalHash(odr.Database(), 0)
|
||||
config, _ := core.GetChainConfig(odr.Database(), genesis)
|
||||
genesis := rawdb.ReadCanonicalHash(odr.Database(), 0)
|
||||
config := rawdb.ReadChainConfig(odr.Database(), genesis)
|
||||
|
||||
core.SetReceiptsData(config, block, receipts)
|
||||
core.WriteBlockReceipts(odr.Database(), hash, number, receipts)
|
||||
if err := core.SetReceiptsData(config, block, receipts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteReceipts(odr.Database(), hash, number, receipts)
|
||||
}
|
||||
return receipts, nil
|
||||
}
|
||||
@@ -154,7 +157,7 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
|
||||
// block given by its hash.
|
||||
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 := core.GetBlockReceipts(odr.Database(), hash, number)
|
||||
receipts := rawdb.ReadReceipts(odr.Database(), hash, number)
|
||||
if receipts == nil {
|
||||
r := &ReceiptsRequest{Hash: hash, Number: number}
|
||||
if err := odr.Retrieve(ctx, r); err != nil {
|
||||
@@ -172,9 +175,9 @@ func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number
|
||||
|
||||
// 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 (
|
||||
db = odr.Database()
|
||||
result = make([][]byte, len(sectionIdxList))
|
||||
reqList []uint64
|
||||
reqIdx []int
|
||||
)
|
||||
@@ -185,27 +188,28 @@ func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxLi
|
||||
)
|
||||
if odr.BloomTrieIndexer() != nil {
|
||||
bloomTrieCount, sectionHeadNum, sectionHead = odr.BloomTrieIndexer().Sections()
|
||||
canonicalHash := core.GetCanonicalHash(db, sectionHeadNum)
|
||||
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
|
||||
sectionHeadNum = bloomTrieCount*odr.IndexerConfig().BloomTrieSize - 1
|
||||
sectionHead = odr.BloomTrieIndexer().SectionHead(bloomTrieCount - 1)
|
||||
canonicalHash = core.GetCanonicalHash(db, sectionHeadNum)
|
||||
canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, sectionIdx := range sectionIdxList {
|
||||
sectionHead := core.GetCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1)
|
||||
sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*odr.IndexerConfig().BloomSize-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 := core.GetBloomBits(db, bitIdx, sectionIdx, sectionHead)
|
||||
bloomBits, err := rawdb.ReadBloomBits(db, bitIdx, sectionIdx, sectionHead)
|
||||
if err == nil {
|
||||
result[i] = bloomBits
|
||||
} else {
|
||||
// TODO(rjl493456442) Convert sectionIndex to BloomTrie relative index
|
||||
if sectionIdx >= bloomTrieCount {
|
||||
return nil, ErrNoTrustedBloomTrie
|
||||
}
|
||||
@@ -217,7 +221,8 @@ func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxLi
|
||||
return result, nil
|
||||
}
|
||||
|
||||
r := &BloomRequest{BloomTrieRoot: GetBloomTrieRoot(db, bloomTrieCount-1, sectionHead), BloomTrieNum: bloomTrieCount - 1, BitIdx: bitIdx, SectionIdxList: reqList}
|
||||
r := &BloomRequest{BloomTrieRoot: GetBloomTrieRoot(db, bloomTrieCount-1, sectionHead), BloomTrieNum: bloomTrieCount - 1,
|
||||
BitIdx: bitIdx, SectionIdxList: reqList, Config: odr.IndexerConfig()}
|
||||
if err := odr.Retrieve(ctx, r); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
||||
+231
-103
@@ -17,14 +17,17 @@
|
||||
package light
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -33,56 +36,112 @@ import (
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
)
|
||||
|
||||
const (
|
||||
// CHTFrequencyClient is the block frequency for creating CHTs on the client side.
|
||||
CHTFrequencyClient = 32768
|
||||
// IndexerConfig includes a set of configs for chain indexers.
|
||||
type IndexerConfig struct {
|
||||
// The block frequency for creating CHTs.
|
||||
ChtSize uint64
|
||||
|
||||
// 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
|
||||
// A special auxiliary field represents client's chtsize for server config, otherwise represents server's chtsize.
|
||||
PairChtSize uint64
|
||||
|
||||
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
|
||||
// The number of confirmations needed to generate/accept a canonical hash help trie.
|
||||
ChtConfirms uint64
|
||||
|
||||
// The block frequency for creating new bloom bits.
|
||||
BloomSize uint64
|
||||
|
||||
// The number of confirmation needed before a bloom section is considered probably final and its rotated bits
|
||||
// are calculated.
|
||||
BloomConfirms uint64
|
||||
|
||||
// The block frequency for creating BloomTrie.
|
||||
BloomTrieSize uint64
|
||||
|
||||
// The number of confirmations needed to generate/accept a bloom trie.
|
||||
BloomTrieConfirms uint64
|
||||
}
|
||||
|
||||
var (
|
||||
// DefaultServerIndexerConfig wraps a set of configs as a default indexer config for server side.
|
||||
DefaultServerIndexerConfig = &IndexerConfig{
|
||||
ChtSize: params.CHTFrequencyServer,
|
||||
PairChtSize: params.CHTFrequencyClient,
|
||||
ChtConfirms: params.HelperTrieProcessConfirmations,
|
||||
BloomSize: params.BloomBitsBlocks,
|
||||
BloomConfirms: params.BloomConfirms,
|
||||
BloomTrieSize: params.BloomTrieFrequency,
|
||||
BloomTrieConfirms: params.HelperTrieProcessConfirmations,
|
||||
}
|
||||
// DefaultClientIndexerConfig wraps a set of configs as a default indexer config for client side.
|
||||
DefaultClientIndexerConfig = &IndexerConfig{
|
||||
ChtSize: params.CHTFrequencyClient,
|
||||
PairChtSize: params.CHTFrequencyServer,
|
||||
ChtConfirms: params.HelperTrieConfirmations,
|
||||
BloomSize: params.BloomBitsBlocksClient,
|
||||
BloomConfirms: params.HelperTrieConfirmations,
|
||||
BloomTrieSize: params.BloomTrieFrequency,
|
||||
BloomTrieConfirms: params.HelperTrieConfirmations,
|
||||
}
|
||||
// TestServerIndexerConfig wraps a set of configs as a test indexer config for server side.
|
||||
TestServerIndexerConfig = &IndexerConfig{
|
||||
ChtSize: 256,
|
||||
PairChtSize: 2048,
|
||||
ChtConfirms: 16,
|
||||
BloomSize: 256,
|
||||
BloomConfirms: 16,
|
||||
BloomTrieSize: 2048,
|
||||
BloomTrieConfirms: 16,
|
||||
}
|
||||
// TestClientIndexerConfig wraps a set of configs as a test indexer config for client side.
|
||||
TestClientIndexerConfig = &IndexerConfig{
|
||||
ChtSize: 2048,
|
||||
PairChtSize: 256,
|
||||
ChtConfirms: 128,
|
||||
BloomSize: 2048,
|
||||
BloomConfirms: 128,
|
||||
BloomTrieSize: 2048,
|
||||
BloomTrieConfirms: 128,
|
||||
}
|
||||
)
|
||||
|
||||
// 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, bloomTrieRoot common.Hash
|
||||
type TrustedCheckpoint struct {
|
||||
name string
|
||||
SectionIdx uint64
|
||||
SectionHead, CHTRoot, BloomRoot common.Hash
|
||||
}
|
||||
|
||||
var (
|
||||
mainnetCheckpoint = trustedCheckpoint{
|
||||
name: "mainnet",
|
||||
sectionIdx: 157,
|
||||
sectionHead: common.HexToHash("1963c080887ca7f406c2bb114293eea83e54f783f94df24b447f7e3b6317c747"),
|
||||
chtRoot: common.HexToHash("42abc436567dfb678a38fa6a9f881aa4c8a4cc8eaa2def08359292c3d0bd48ec"),
|
||||
bloomTrieRoot: common.HexToHash("281c9f8fb3cb8b37ae45e9907ef8f3b19cd22c54e297c2d6c09c1db1593dce42"),
|
||||
}
|
||||
|
||||
ropstenCheckpoint = trustedCheckpoint{
|
||||
name: "ropsten",
|
||||
sectionIdx: 83,
|
||||
sectionHead: common.HexToHash("3ca623586bc0da35f1fc8d9b6b55950f3b1f69be9c6501846a2df672adb61236"),
|
||||
chtRoot: common.HexToHash("8f08ec7783969768c6ef06e5fe3398223cbf4ae2907b676da7b6fe6c7f55b059"),
|
||||
bloomTrieRoot: common.HexToHash("02d86d3c6a87f8f8a92c2a59bbba2132ff6f9f61b0915a5dc28a9d8279219fd0"),
|
||||
}
|
||||
)
|
||||
|
||||
// trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to
|
||||
var trustedCheckpoints = map[common.Hash]trustedCheckpoint{
|
||||
params.MainnetGenesisHash: mainnetCheckpoint,
|
||||
params.TestnetGenesisHash: ropstenCheckpoint,
|
||||
var trustedCheckpoints = map[common.Hash]TrustedCheckpoint{
|
||||
params.MainnetGenesisHash: {
|
||||
name: "mainnet",
|
||||
SectionIdx: 187,
|
||||
SectionHead: common.HexToHash("e6baa034efa31562d71ff23676512dec6562c1ad0301e08843b907e81958c696"),
|
||||
CHTRoot: common.HexToHash("28001955219719cf06de1b08648969139d123a9835fc760547a1e4dabdabc15a"),
|
||||
BloomRoot: common.HexToHash("395ca2373fc662720ac6b58b3bbe71f68aa0f38b63b2d3553dd32ff3c51eebc4"),
|
||||
},
|
||||
params.TestnetGenesisHash: {
|
||||
name: "ropsten",
|
||||
SectionIdx: 117,
|
||||
SectionHead: common.HexToHash("9529b38631ae30783f56cbe4c3b9f07575b770ecba4f6e20a274b1e2f40fede1"),
|
||||
CHTRoot: common.HexToHash("6f48e9f101f1fac98e7d74fbbcc4fda138358271ffd974d40d2506f0308bb363"),
|
||||
BloomRoot: common.HexToHash("8242342e66e942c0cd893484e6736b9862ceb88b43ca344bb06a8285ac1b6d64"),
|
||||
},
|
||||
params.RinkebyGenesisHash: {
|
||||
name: "rinkeby",
|
||||
SectionIdx: 85,
|
||||
SectionHead: common.HexToHash("92cfa67afc4ad8ab0dcbc6fa49efd14b5b19402442e7317e6bc879d85f89d64d"),
|
||||
CHTRoot: common.HexToHash("2802ec92cd7a54a75bca96afdc666ae7b99e5d96cf8192dcfb09588812f51564"),
|
||||
BloomRoot: common.HexToHash("ebefeb31a9a42866d8cf2d2477704b4c3d7c20d0e4e9b5aaa77f396e016a1263"),
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
|
||||
ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie")
|
||||
ErrNoHeader = errors.New("Header not found")
|
||||
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-"
|
||||
)
|
||||
@@ -93,8 +152,8 @@ type ChtNode struct {
|
||||
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
|
||||
// GetChtRoot reads the CHT root associated 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)
|
||||
@@ -102,66 +161,88 @@ func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) c
|
||||
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
|
||||
// StoreChtRoot writes the CHT root associated 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
|
||||
// ChtIndexerBackend implements core.ChainIndexerBackend.
|
||||
type ChtIndexerBackend struct {
|
||||
diskdb ethdb.Database
|
||||
diskdb, trieTable ethdb.Database
|
||||
odr OdrBackend
|
||||
triedb *trie.Database
|
||||
section, sectionSize uint64
|
||||
lastHash common.Hash
|
||||
trie *trie.Trie
|
||||
}
|
||||
|
||||
// NewBloomTrieIndexer creates a BloomTrie chain indexer
|
||||
func NewChtIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer {
|
||||
var sectionSize, confirmReq uint64
|
||||
if clientMode {
|
||||
sectionSize = CHTFrequencyClient
|
||||
confirmReq = HelperTrieConfirmations
|
||||
} else {
|
||||
sectionSize = CHTFrequencyServer
|
||||
confirmReq = HelperTrieProcessConfirmations
|
||||
}
|
||||
idb := ethdb.NewTable(db, "chtIndex-")
|
||||
// NewChtIndexer creates a Cht chain indexer
|
||||
func NewChtIndexer(db ethdb.Database, odr OdrBackend, size, confirms uint64) *core.ChainIndexer {
|
||||
trieTable := ethdb.NewTable(db, ChtTablePrefix)
|
||||
backend := &ChtIndexerBackend{
|
||||
diskdb: db,
|
||||
triedb: trie.NewDatabase(ethdb.NewTable(db, ChtTablePrefix)),
|
||||
sectionSize: sectionSize,
|
||||
odr: odr,
|
||||
trieTable: trieTable,
|
||||
triedb: trie.NewDatabase(trieTable),
|
||||
sectionSize: size,
|
||||
}
|
||||
return core.NewChainIndexer(db, ethdb.NewTable(db, "chtIndex-"), backend, size, confirms, 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, Config: c.odr.IndexerConfig()}
|
||||
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
|
||||
}
|
||||
}
|
||||
return core.NewChainIndexer(db, idb, backend, sectionSize, confirmReq, time.Millisecond*100, "cht")
|
||||
}
|
||||
|
||||
// Reset implements core.ChainIndexerBackend
|
||||
func (c *ChtIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) error {
|
||||
func (c *ChtIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
|
||||
var root common.Hash
|
||||
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(header *types.Header) {
|
||||
func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) error {
|
||||
hash, num := header.Hash(), header.Number.Uint64()
|
||||
c.lastHash = hash
|
||||
|
||||
td := core.GetTd(c.diskdb, hash, num)
|
||||
td := rawdb.ReadTd(c.diskdb, hash, num)
|
||||
if td == nil {
|
||||
panic(nil)
|
||||
}
|
||||
@@ -169,6 +250,7 @@ func (c *ChtIndexerBackend) Process(header *types.Header) {
|
||||
binary.BigEndian.PutUint64(encNumber[:], num)
|
||||
data, _ := rlp.EncodeToBytes(ChtNode{hash, td})
|
||||
c.trie.Update(encNumber[:], data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit implements core.ChainIndexerBackend
|
||||
@@ -179,19 +261,13 @@ func (c *ChtIndexerBackend) Commit() error {
|
||||
}
|
||||
c.triedb.Commit(root, false)
|
||||
|
||||
if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 {
|
||||
log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", c.lastHash, "root", root)
|
||||
if ((c.section+1)*c.sectionSize)%params.CHTFrequencyClient == 0 {
|
||||
log.Info("Storing CHT", "section", c.section*c.sectionSize/params.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
|
||||
ethBloomBitsConfirmations = 256
|
||||
)
|
||||
|
||||
var (
|
||||
bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash
|
||||
BloomTrieTablePrefix = "blt-"
|
||||
@@ -214,52 +290,105 @@ func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root
|
||||
|
||||
// BloomTrieIndexerBackend implements core.ChainIndexerBackend
|
||||
type BloomTrieIndexerBackend struct {
|
||||
diskdb ethdb.Database
|
||||
triedb *trie.Database
|
||||
section, parentSectionSize, bloomTrieRatio uint64
|
||||
trie *trie.Trie
|
||||
sectionHeads []common.Hash
|
||||
diskdb, trieTable ethdb.Database
|
||||
triedb *trie.Database
|
||||
odr OdrBackend
|
||||
section uint64
|
||||
parentSize uint64
|
||||
size uint64
|
||||
bloomTrieRatio uint64
|
||||
trie *trie.Trie
|
||||
sectionHeads []common.Hash
|
||||
}
|
||||
|
||||
// NewBloomTrieIndexer creates a BloomTrie chain indexer
|
||||
func NewBloomTrieIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer {
|
||||
func NewBloomTrieIndexer(db ethdb.Database, odr OdrBackend, parentSize, size uint64) *core.ChainIndexer {
|
||||
trieTable := ethdb.NewTable(db, BloomTrieTablePrefix)
|
||||
backend := &BloomTrieIndexerBackend{
|
||||
diskdb: db,
|
||||
triedb: trie.NewDatabase(ethdb.NewTable(db, BloomTrieTablePrefix)),
|
||||
diskdb: db,
|
||||
odr: odr,
|
||||
trieTable: trieTable,
|
||||
triedb: trie.NewDatabase(trieTable),
|
||||
parentSize: parentSize,
|
||||
size: size,
|
||||
}
|
||||
idb := ethdb.NewTable(db, "bltIndex-")
|
||||
|
||||
var confirmReq uint64
|
||||
if clientMode {
|
||||
backend.parentSectionSize = BloomTrieFrequency
|
||||
confirmReq = HelperTrieConfirmations
|
||||
} else {
|
||||
backend.parentSectionSize = ethBloomBitsSection
|
||||
confirmReq = HelperTrieProcessConfirmations
|
||||
}
|
||||
backend.bloomTrieRatio = BloomTrieFrequency / backend.parentSectionSize
|
||||
backend.bloomTrieRatio = size / parentSize
|
||||
backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio)
|
||||
return core.NewChainIndexer(db, idb, backend, BloomTrieFrequency, confirmReq-ethBloomBitsConfirmations, time.Millisecond*100, "bloomtrie")
|
||||
return core.NewChainIndexer(db, ethdb.NewTable(db, "bltIndex-"), backend, size, 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}, Config: b.odr.IndexerConfig()}
|
||||
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(section uint64, lastSectionHead common.Hash) error {
|
||||
func (b *BloomTrieIndexerBackend) Reset(ctx context.Context, section uint64, lastSectionHead common.Hash) error {
|
||||
var root common.Hash
|
||||
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(header *types.Header) {
|
||||
num := header.Number.Uint64() - b.section*BloomTrieFrequency
|
||||
if (num+1)%b.parentSectionSize == 0 {
|
||||
b.sectionHeads[num/b.parentSectionSize] = header.Hash()
|
||||
func (b *BloomTrieIndexerBackend) Process(ctx context.Context, header *types.Header) error {
|
||||
num := header.Number.Uint64() - b.section*b.size
|
||||
if (num+1)%b.parentSize == 0 {
|
||||
b.sectionHeads[num/b.parentSize] = header.Hash()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit implements core.ChainIndexerBackend
|
||||
@@ -272,11 +401,11 @@ func (b *BloomTrieIndexerBackend) Commit() error {
|
||||
binary.BigEndian.PutUint64(encKey[2:10], b.section)
|
||||
var decomp []byte
|
||||
for j := uint64(0); j < b.bloomTrieRatio; j++ {
|
||||
data, err := core.GetBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[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))
|
||||
decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSize/8))
|
||||
if err2 != nil {
|
||||
return err2
|
||||
}
|
||||
@@ -299,8 +428,7 @@ func (b *BloomTrieIndexerBackend) Commit() error {
|
||||
b.triedb.Commit(root, false)
|
||||
|
||||
sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
|
||||
log.Info("Storing bloom trie", "section", b.section, "head", sectionHead, "root", root, "compression", float64(compSize)/float64(decompSize))
|
||||
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
|
||||
}
|
||||
|
||||
+5
-5
@@ -34,10 +34,10 @@ import (
|
||||
|
||||
func TestNodeIterator(t *testing.T) {
|
||||
var (
|
||||
fulldb, _ = ethdb.NewMemDatabase()
|
||||
lightdb, _ = ethdb.NewMemDatabase()
|
||||
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
|
||||
genesis = gspec.MustCommit(fulldb)
|
||||
fulldb = ethdb.NewMemDatabase()
|
||||
lightdb = ethdb.NewMemDatabase()
|
||||
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
|
||||
genesis = gspec.MustCommit(fulldb)
|
||||
)
|
||||
gspec.MustCommit(lightdb)
|
||||
blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
|
||||
@@ -47,7 +47,7 @@ func TestNodeIterator(t *testing.T) {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
odr := &testOdr{sdb: fulldb, ldb: lightdb}
|
||||
odr := &testOdr{sdb: fulldb, ldb: lightdb, indexerConfig: TestClientIndexerConfig}
|
||||
head := blockchain.CurrentHeader()
|
||||
lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root)
|
||||
fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root)
|
||||
|
||||
+15
-11
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -88,7 +89,7 @@ type TxRelayBackend interface {
|
||||
func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool {
|
||||
pool := &TxPool{
|
||||
config: config,
|
||||
signer: types.NewEIP155Signer(config.ChainId),
|
||||
signer: types.NewEIP155Signer(config.ChainID),
|
||||
nonce: make(map[common.Address]uint64),
|
||||
pending: make(map[common.Hash]*types.Transaction),
|
||||
mined: make(map[common.Hash][]*types.Transaction),
|
||||
@@ -183,9 +184,8 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number
|
||||
if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results
|
||||
return err
|
||||
}
|
||||
if err := core.WriteTxLookupEntries(pool.chainDb, block); err != nil {
|
||||
return err
|
||||
}
|
||||
rawdb.WriteTxLookupEntries(pool.chainDb, block)
|
||||
|
||||
// Update the transaction pool's state
|
||||
for _, tx := range list {
|
||||
delete(pool.pending, tx.Hash())
|
||||
@@ -199,15 +199,17 @@ func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number
|
||||
// rollbackTxs marks the transactions contained in recently rolled back blocks
|
||||
// as rolled back. It also removes any positional lookup entries.
|
||||
func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) {
|
||||
batch := pool.chainDb.NewBatch()
|
||||
if list, ok := pool.mined[hash]; ok {
|
||||
for _, tx := range list {
|
||||
txHash := tx.Hash()
|
||||
core.DeleteTxLookupEntry(pool.chainDb, txHash)
|
||||
rawdb.DeleteTxLookupEntry(batch, txHash)
|
||||
pool.pending[txHash] = tx
|
||||
txc.setState(txHash, false)
|
||||
}
|
||||
delete(pool.mined, hash)
|
||||
}
|
||||
batch.Write()
|
||||
}
|
||||
|
||||
// reorgOnNewHead sets a new head header, processing (and rolling back if necessary)
|
||||
@@ -258,7 +260,7 @@ func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header)
|
||||
idx2 := idx - txPermanent
|
||||
if len(pool.mined) > 0 {
|
||||
for i := pool.clearIdx; i < idx2; i++ {
|
||||
hash := core.GetCanonicalHash(pool.chainDb, i)
|
||||
hash := rawdb.ReadCanonicalHash(pool.chainDb, i)
|
||||
if list, ok := pool.mined[hash]; ok {
|
||||
hashes := make([]common.Hash, len(list))
|
||||
for i, tx := range list {
|
||||
@@ -321,9 +323,9 @@ func (pool *TxPool) Stop() {
|
||||
log.Info("Transaction pool stopped")
|
||||
}
|
||||
|
||||
// SubscribeTxPreEvent registers a subscription of core.TxPreEvent and
|
||||
// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and
|
||||
// starts sending event to the given channel.
|
||||
func (pool *TxPool) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
|
||||
func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
|
||||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
@@ -412,7 +414,7 @@ func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error {
|
||||
// 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 self.txFeed.Send(core.TxPreEvent{Tx: tx})
|
||||
go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}})
|
||||
}
|
||||
|
||||
// Print a log message if low enough level is set
|
||||
@@ -504,14 +506,16 @@ func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common
|
||||
func (self *TxPool) RemoveTransactions(txs types.Transactions) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
var hashes []common.Hash
|
||||
batch := self.chainDb.NewBatch()
|
||||
for _, tx := range txs {
|
||||
//self.RemoveTx(tx.Hash())
|
||||
hash := tx.Hash()
|
||||
delete(self.pending, hash)
|
||||
self.chainDb.Delete(hash[:])
|
||||
batch.Delete(hash.Bytes())
|
||||
hashes = append(hashes, hash)
|
||||
}
|
||||
batch.Write()
|
||||
self.relay.Discard(hashes)
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -81,8 +81,8 @@ func TestTxPool(t *testing.T) {
|
||||
}
|
||||
|
||||
var (
|
||||
sdb, _ = ethdb.NewMemDatabase()
|
||||
ldb, _ = ethdb.NewMemDatabase()
|
||||
sdb = ethdb.NewMemDatabase()
|
||||
ldb = ethdb.NewMemDatabase()
|
||||
gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}}
|
||||
genesis = gspec.MustCommit(sdb)
|
||||
)
|
||||
@@ -94,7 +94,7 @@ func TestTxPool(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
odr := &testOdr{sdb: sdb, ldb: ldb}
|
||||
odr := &testOdr{sdb: sdb, ldb: ldb, indexerConfig: TestClientIndexerConfig}
|
||||
relay := &testTxRelay{
|
||||
send: make(chan int, 1),
|
||||
discard: make(chan int, 1),
|
||||
|
||||
Reference in New Issue
Block a user