ADDED LCP engine

This commit is contained in:
pavelkrolevets 2018-08-27 14:49:18 +08:00
parent 0e2486f4fc
commit 92e4d96935
10 changed files with 244 additions and 80 deletions

View file

@ -84,7 +84,7 @@ type Engine interface {
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
uncles []*types.Header, receipts []*types.Receipt, LcpContext *types.LCPContext) (*types.Block, error)
// Seal generates a new block for the given input block with the local miner's
// seal place on top.

View file

@ -356,7 +356,7 @@ func AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header
}
func (d *LCP) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt, lcpContext *types.LCPContext) (*types.Block, error) {
uncles []*types.Header, receipts []*types.Receipt, LcpContext *types.LCPContext) (*types.Block, error) {
// Accumulate block rewards and commit the final state root
AccumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
@ -364,7 +364,7 @@ func (d *LCP) Finalize(chain consensus.ChainReader, header *types.Header, state
parent := chain.GetHeaderByHash(header.ParentHash)
epochContext := &EpochContext{
statedb: state,
Context: lcpContext,
Context: LcpContext,
TimeStamp: header.Time.Int64(),
}
if timeOfFirstBlock == 0 {
@ -379,8 +379,8 @@ func (d *LCP) Finalize(chain consensus.ChainReader, header *types.Header, state
}
//update mint count trie
updateMintCnt(parent.Time.Int64(), header.Time.Int64(), header.Validator, lcpContext)
header.LCPContext = lcpContext.ToProto()
updateMintCnt(parent.Time.Int64(), header.Time.Int64(), header.Validator, LcpContext)
header.LCPContext = LcpContext.ToProto()
return types.NewBlock(header, txs, uncles, receipts), nil
}
@ -497,8 +497,8 @@ func NextSlot(now int64) int64 {
}
// update counts in MintCntTrie for the miner of newBlock
func updateMintCnt(parentBlockTime, currentBlockTime int64, validator common.Address, lcpContext *types.LCPContext) {
currentMintCntTrie := lcpContext.MintCntTrie()
func updateMintCnt(parentBlockTime, currentBlockTime int64, validator common.Address, dposContext *types.LCPContext) {
currentMintCntTrie := dposContext.MintCntTrie()
currentEpoch := parentBlockTime / epochInterval
currentEpochBytes := make([]byte, 8)
binary.BigEndian.PutUint64(currentEpochBytes, uint64(currentEpoch))
@ -524,5 +524,5 @@ func updateMintCnt(parentBlockTime, currentBlockTime int64, validator common.Add
newEpochBytes := make([]byte, 8)
binary.BigEndian.PutUint64(newEpochBytes, uint64(newEpoch))
binary.BigEndian.PutUint64(newCntBytes, uint64(cnt))
lcpContext.MintCntTrie().TryUpdate(append(newEpochBytes, validator.Bytes()...), newCntBytes)
dposContext.MintCntTrie().TryUpdate(append(newEpochBytes, validator.Bytes()...), newCntBytes)
}

View file

@ -28,6 +28,7 @@ import (
"github.com/pavelkrolevets/go-ethereum/core/vm"
"github.com/pavelkrolevets/go-ethereum/ethdb"
"github.com/pavelkrolevets/go-ethereum/params"
"github.com/pavelkrolevets/go-ethereum/consensus/lcp"
)
// BlockGen creates blocks for testing.
@ -167,7 +168,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
// Force LCP configuration if everything is empty
if config == nil {
config = params.TestChainConfig
config = params.LcpChainConfig
}
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
@ -195,9 +196,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if gen != nil {
gen(i, b)
}
lcp.AccumulateRewards(config, statedb, b.header, b.uncles)
if b.engine != nil {
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts, b.header.LCPContext)
// Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
if err != nil {
@ -206,10 +207,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}
b.header.Root = root
b.header.LCPContext = parent.Header().LCPContext
return block, b.receipts
}
return nil, nil
}
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), state.NewDatabase(db))
if err != nil {

View file

@ -66,11 +66,11 @@ func TestHeaderStorage(t *testing.T) {
// Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
// Create a test body to move around the database and make sure it's really new
dposCtx, _ := types.NewDposContext(db)
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header"), DposContext: dposCtx.ToProto()}}}
dposCtx, _ := types.NewLCPContext(db)
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header"), LCPContext: dposCtx.ToProto()}}}
hasher := sha3.NewKeccak256()
rlp.Encode(hasher, body)
@ -107,7 +107,7 @@ func TestBodyStorage(t *testing.T) {
// Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
// Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(&types.Header{
@ -159,7 +159,7 @@ func TestBlockStorage(t *testing.T) {
// Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
@ -200,7 +200,7 @@ func TestPartialBlockStorage(t *testing.T) {
// Tests block total difficulty storage and retrieval operations.
func TestTdStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
// Create a test TD to move around the database and make sure it's really new
hash, td := common.Hash{}, big.NewInt(314)
@ -225,7 +225,7 @@ func TestTdStorage(t *testing.T) {
// Tests that canonical numbers can be mapped to hashes and retrieved.
func TestCanonicalMappingStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
// Create a test canonical number and assinged hash to move around
hash, number := common.Hash{0: 0xff}, uint64(314)
@ -250,7 +250,7 @@ func TestCanonicalMappingStorage(t *testing.T) {
// Tests that head headers and head blocks can be assigned, individually.
func TestHeadStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
@ -290,11 +290,11 @@ func TestHeadStorage(t *testing.T) {
// Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
tx1 := types.NewTransaction(types.Binary, 1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx2 := types.NewTransaction(types.Binary, 2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22})
tx3 := types.NewTransaction(types.Binary, 3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33})
tx1 := types.NewTransaction(types.Binary, 1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx2 := types.NewTransaction(types.Binary, 2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
tx3 := types.NewTransaction(types.Binary, 3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
@ -335,29 +335,29 @@ func TestLookupStorage(t *testing.T) {
// Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
db:= ethdb.NewMemDatabase()
receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: big.NewInt(1),
CumulativeGasUsed: 1,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: big.NewInt(111111),
GasUsed: 111111,
}
receipt2 := &types.Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: big.NewInt(2),
CumulativeGasUsed: 2,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})},
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: big.NewInt(222222),
GasUsed: 222222,
}
receipts := []*types.Receipt{receipt1, receipt2}

View file

@ -73,7 +73,7 @@ func transaction(nonce uint64, gaslimit uint64, key *ecdsa.PrivateKey) *types.Tr
}
func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
tx, _ := types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
tx, _ := types.SignTx(types.NewTransaction(types.Binary, nonce, common.Address{}, big.NewInt(100), gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
return tx
}
@ -319,7 +319,7 @@ func TestTransactionNegativeValue(t *testing.T) {
pool, key := setupTxPool()
defer pool.Stop()
tx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
tx, _ := types.SignTx(types.NewTransaction(types.Binary,0, common.Address{}, big.NewInt(-1), 100, big.NewInt(1), nil), types.HomesteadSigner{}, key)
from, _ := deriveSender(tx)
pool.currentState.AddBalance(from, big.NewInt(1))
if err := pool.AddRemote(tx); err != ErrNegativeValue {
@ -373,9 +373,9 @@ func TestTransactionDoubleNonce(t *testing.T) {
resetState()
signer := types.HomesteadSigner{}
tx1, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key)
tx2, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key)
tx3, _ := types.SignTx(types.NewTransaction(0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key)
tx1, _ := types.SignTx(types.NewTransaction(types.Binary, 0, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), nil), signer, key)
tx2, _ := types.SignTx(types.NewTransaction(types.Binary,0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(2), nil), signer, key)
tx3, _ := types.SignTx(types.NewTransaction(types.Binary,0, common.Address{}, big.NewInt(100), 1000000, big.NewInt(1), nil), signer, key)
// Add the first two transaction, ensure higher priced stays only
if replace, err := pool.add(tx1, false); err != nil || replace {

View file

@ -73,7 +73,7 @@ type Header struct {
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Validator common.Address `json:"validator" gencodec:"required"`
LCPContext *LCPContextProto `json:"LCPContext" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Coinbase common.Address `json:"coinbase" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`

View file

@ -29,8 +29,6 @@ import (
"github.com/pavelkrolevets/go-ethereum/common"
"github.com/pavelkrolevets/go-ethereum/common/hexutil"
"github.com/pavelkrolevets/go-ethereum/consensus"
"github.com/pavelkrolevets/go-ethereum/consensus/ethash"
"github.com/pavelkrolevets/go-ethereum/consensus/lcp"
"github.com/pavelkrolevets/go-ethereum/core"
"github.com/pavelkrolevets/go-ethereum/core/bloombits"
"github.com/pavelkrolevets/go-ethereum/core/rawdb"
@ -49,6 +47,7 @@ import (
"github.com/pavelkrolevets/go-ethereum/params"
"github.com/pavelkrolevets/go-ethereum/rlp"
"github.com/pavelkrolevets/go-ethereum/rpc"
"github.com/pavelkrolevets/go-ethereum/consensus/lcp"
)
type LesServer interface {
@ -65,6 +64,7 @@ type Ethereum struct {
// Channel for shutting down the service
shutdownChan chan bool // Channel for shutting down the Ethereum
stopDbUpgrade func() error // stop chain db sequential key upgrade
// Handlers
txPool *core.TxPool
@ -87,6 +87,7 @@ type Ethereum struct {
miner *miner.Miner
gasPrice *big.Int
etherbase common.Address
coinbase common.Address
validator common.Address
periodBlock int64
maxValidators int64
@ -98,6 +99,23 @@ type Ethereum struct {
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
}
func (s *Ethereum) Validator() (validator common.Address, err error) {
s.lock.RLock()
validator = s.validator
s.lock.RUnlock()
if validator != (common.Address{}) {
return validator, nil
}
if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
return accounts[0].Address, nil
}
}
return common.Address{}, fmt.Errorf("validator address must be explicitly specified")
}
func (s *Ethereum) AddLesServer(ls LesServer) {
s.lesServer = ls
ls.SetBloomBitsIndexer(s.bloomIndexer)
@ -116,6 +134,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if err != nil {
return nil, err
}
stopDbUpgrade := upgradeDeduplicateData(chainDb)
chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
@ -128,11 +147,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
chainConfig: chainConfig,
eventMux: ctx.EventMux,
accountManager: ctx.AccountManager,
engine: CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb),
engine: lcp.New(chainConfig.LCP, chainDb),
shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade,
networkID: config.NetworkId,
gasPrice: config.GasPrice,
etherbase: config.Etherbase,
validator: config.Validator,
coinbase: config.Coinbase,
bloomRequests: make(chan chan *bloombits.Retrieval),
bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks),
}
@ -213,37 +235,6 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data
return db, nil
}
// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine {
// If LCP is requested, set it up
if chainConfig.LCP != nil {
return lcp.New(chainConfig.LCP, db)
}
// Otherwise assume proof-of-work
switch config.PowMode {
case ethash.ModeFake:
log.Warn("Ethash used in fake mode")
return ethash.NewFaker()
case ethash.ModeTest:
log.Warn("Ethash used in test mode")
return ethash.NewTester()
case ethash.ModeShared:
log.Warn("Ethash used in shared mode")
return ethash.NewShared()
default:
engine := ethash.New(ethash.Config{
CacheDir: ctx.ResolvePath(config.CacheDir),
CachesInMem: config.CachesInMem,
CachesOnDisk: config.CachesOnDisk,
DatasetDir: config.DatasetDir,
DatasetsInMem: config.DatasetsInMem,
DatasetsOnDisk: config.DatasetsOnDisk,
})
engine.SetThreads(-1) // Disable CPU mining
return engine
}
}
// APIs return the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API {
@ -337,19 +328,49 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) {
s.miner.SetEtherbase(etherbase)
}
func (s *Ethereum) StartMining(local bool) error {
eb, err := s.Etherbase()
if err != nil {
log.Error("Cannot start mining without etherbase", "err", err)
return fmt.Errorf("etherbase missing: %v", err)
// set in js console via admin interface or wrapper from cli flags
func (self *Ethereum) SetValidator(validator common.Address) {
self.lock.Lock()
self.validator = validator
self.lock.Unlock()
}
func (s *Ethereum) Coinbase() (eb common.Address, err error) {
s.lock.RLock()
coinbase := s.coinbase
s.lock.RUnlock()
if coinbase != (common.Address{}) {
return coinbase, nil
}
if clique, ok := s.engine.(*clique.Clique); ok {
wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
return accounts[0].Address, nil
}
}
return common.Address{}, fmt.Errorf("coinbase address must be explicitly specified")
}
func (s *Ethereum) StartMining(local bool) error {
validator, err := s.Validator()
if err != nil {
log.Error("Cannot start mining without validator", "err", err)
return fmt.Errorf("validator missing: %v", err)
}
eb, err := s.Coinbase()
if err != nil {
log.Error("Cannot start mining without coinbase", "err", err)
return fmt.Errorf("coinbase missing: %v", err)
}
if lcp, ok := s.engine.(*lcp.LCP); ok {
wallet, err := s.accountManager.Find(accounts.Account{Address: validator})
if wallet == nil || err != nil {
log.Error("Etherbase account unavailable locally", "err", err)
log.Error("Coinbase account unavailable locally", "err", err)
return fmt.Errorf("signer missing: %v", err)
}
clique.Authorize(eb, wallet.SignHash)
lcp.Authorize(validator, wallet.SignHash)
}
if local {
// If local (CPU) mining is started, we can disable the transaction rejection

View file

@ -96,6 +96,8 @@ type Config struct {
// Mining-related options
Etherbase common.Address `toml:",omitempty"`
Validator common.Address `toml:",omitempty"`
Coinbase common.Address `toml:",omitempty"`
MinerThreads int `toml:",omitempty"`
ExtraData []byte `toml:",omitempty"`
GasPrice *big.Int

135
eth/db_upgrade.go Normal file
View file

@ -0,0 +1,135 @@
// 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 <http://www.gnu.org/licenses/>.
// Package eth implements the Ethereum protocol.
package eth
import (
"bytes"
"time"
"github.com/pavelkrolevets/go-ethereum/common"
"github.com/pavelkrolevets/go-ethereum/core"
"github.com/pavelkrolevets/go-ethereum/ethdb"
"github.com/pavelkrolevets/go-ethereum/log"
"github.com/pavelkrolevets/go-ethereum/rlp"
)
var deduplicateData = []byte("dbUpgrade_20170714deduplicateData")
// upgradeDeduplicateData checks the chain database version and
// starts a background process to make upgrades if necessary.
// Returns a stop function that blocks until the process has
// been safely stopped.
func upgradeDeduplicateData(db ethdb.Database) func() error {
// If the database is already converted or empty, bail out
data, _ := db.Get(deduplicateData)
if len(data) > 0 && data[0] == 42 {
return nil
}
if data, _ := db.Get([]byte("LastHeader")); len(data) == 0 {
db.Put(deduplicateData, []byte{42})
return nil
}
// Start the deduplication upgrade on a new goroutine
log.Warn("Upgrading database to use lookup entries")
stop := make(chan chan error)
go func() {
// Create an iterator to read the entire database and covert old lookup entires
it := db.(*ethdb.LDBDatabase).NewIterator()
defer func() {
if it != nil {
it.Release()
}
}()
var (
converted uint64
failed error
)
for failed == nil && it.Next() {
// Skip any entries that don't look like old transaction meta entires (<hash>0x01)
key := it.Key()
if len(key) != common.HashLength+1 || key[common.HashLength] != 0x01 {
continue
}
// Skip any entries that don't contain metadata (name clash between <hash>0x01 and <some-prefix><hash>)
var meta struct {
BlockHash common.Hash
BlockIndex uint64
Index uint64
}
if err := rlp.DecodeBytes(it.Value(), &meta); err != nil {
continue
}
// Skip any already upgraded entries (clash due to <hash> ending with 0x01 (old suffix))
hash := key[:common.HashLength]
if hash[0] == byte('l') {
// Potential clash, the "old" `hash` must point to a live transaction.
if tx, _, _, _ := core.GetTransaction(db, common.BytesToHash(hash)); tx == nil || !bytes.Equal(tx.Hash().Bytes(), hash) {
continue
}
}
// Convert the old metadata to a new lookup entry, delete duplicate data
if failed = db.Put(append([]byte("l"), hash...), it.Value()); failed == nil { // Write the new looku entry
if failed = db.Delete(hash); failed == nil { // Delete the duplicate transaction data
if failed = db.Delete(append([]byte("receipts-"), hash...)); failed == nil { // Delete the duplicate receipt data
if failed = db.Delete(key); failed != nil { // Delete the old transaction metadata
break
}
}
}
}
// Bump the conversion counter, and recreate the iterator occasionally to
// avoid too high memory consumption.
converted++
if converted%100000 == 0 {
it.Release()
it = db.(*ethdb.LDBDatabase).NewIterator()
it.Seek(key)
log.Info("Deduplicating database entries", "deduped", converted)
}
// Check for termination, or continue after a bit of a timeout
select {
case errc := <-stop:
errc <- nil
return
case <-time.After(time.Microsecond * 100):
}
}
// Upgrade finished, mark a such and terminate
if failed == nil {
log.Info("Database deduplication successful", "deduped", converted)
db.Put(deduplicateData, []byte{42})
} else {
log.Error("Database deduplication failed", "deduped", converted, "err", failed)
}
it.Release()
it = nil
errc := <-stop
errc <- failed
}()
// Assembly the cancellation callback
return func() error {
errc := make(chan error)
stop <- errc
return <-errc
}
}

View file

@ -64,7 +64,7 @@ type Agent interface {
type Work struct {
config *params.ChainConfig
signer types.Signer
LCPContext *types.LCPContext
state *state.StateDB // apply state changes here
ancestors mapset.Set // ancestor set (used for checking uncle parent validity)
family mapset.Set // family set (used for checking uncle invalidity)
@ -79,6 +79,7 @@ type Work struct {
receipts []*types.Receipt
createdAt time.Time
}
type Result struct {
@ -620,10 +621,11 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
snap := env.state.Snapshot()
receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
LcpSnap := env.LCPContext.Snapshot()
receipt, _, err := core.ApplyTransaction(env.config, env.LCPContext, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
if err != nil {
env.state.RevertToSnapshot(snap)
env.LCPContext.RevertToSnapShot(LcpSnap)
return err, nil
}
env.txs = append(env.txs, tx)