diff --git a/consensus/consensus.go b/consensus/consensus.go index 9c663c4c34..adef596a27 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -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. diff --git a/consensus/lcp/lcp.go b/consensus/lcp/lcp.go index e69e532b0f..b961663a01 100644 --- a/consensus/lcp/lcp.go +++ b/consensus/lcp/lcp.go @@ -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) } diff --git a/core/chain_makers.go b/core/chain_makers.go index 07553240a8..c2823cd06c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -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 { diff --git a/core/database_util_test.go b/core/database_util_test.go index 29234d5e2c..6f7129121a 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -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} diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 88b9e9b378..1845340e88 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -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 { diff --git a/core/types/block.go b/core/types/block.go index 32ad088eae..dd82f6ec66 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -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"` diff --git a/eth/backend.go b/eth/backend.go index c0408ae605..ae8a2857b2 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -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 diff --git a/eth/config.go b/eth/config.go index 6d726e62a5..a84b13fba1 100644 --- a/eth/config.go +++ b/eth/config.go @@ -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 diff --git a/eth/db_upgrade.go b/eth/db_upgrade.go new file mode 100644 index 0000000000..094e741555 --- /dev/null +++ b/eth/db_upgrade.go @@ -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 . + +// 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 (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 0x01 and ) + 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 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 + } +} diff --git a/miner/worker.go b/miner/worker.go index 0f605bed6d..4c947b256d 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -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)