diff --git a/consensus/consensus_engine.go b/consensus/consensus_engine.go
new file mode 100644
index 000000000..d14b8fb67
--- /dev/null
+++ b/consensus/consensus_engine.go
@@ -0,0 +1,91 @@
+package consensus
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rpc"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ChainReader defines a small collection of methods needed to access the local
+// blockchain during header and/or uncle verification.
+type ChainReader interface {
+ // Config retrieves the blockchain's chain configuration.
+ Config() *params.ChainConfig
+
+ // CurrentHeader retrieves the current header from the local chain.
+ CurrentHeader() *types.Header
+
+ // GetHeader retrieves a block header from the database by hash and number.
+ GetHeader(hash common.Hash, number uint64) *types.Header
+
+ // GetHeaderByNumber retrieves a block header from the database by number.
+ GetHeaderByNumber(number uint64) *types.Header
+
+ // GetHeaderByHash retrieves a block header from the database by its hash.
+ GetHeaderByHash(hash common.Hash) *types.Header
+
+ // GetBlock retrieves a block from the database by hash and number.
+ GetBlock(hash common.Hash, number uint64) *types.Block
+}
+
+// Engine is an algorithm agnostic consensus engine.
+type Engine interface {
+ // Author retrieves the Ethereum address of the account that minted the given
+ // block, which may be different from the header's coinbase if a consensus
+ // engine is based on signatures.
+ Author(header *types.Header) (common.Address, error)
+
+ // VerifyHeader checks whether a header conforms to the consensus rules of a
+ // given engine. Verifying the seal may be done optionally here, or explicitly
+ // via the VerifySeal method.
+ VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
+
+ // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
+ // concurrently. The method returns a quit channel to abort the operations and
+ // a results channel to retrieve the async verifications (the order is that of
+ // the input slice).
+ VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
+
+ // VerifyUncles verifies that the given block's uncles conform to the consensus
+ // rules of a given engine.
+ VerifyUncles(chain ChainReader, block *types.Block) error
+
+ // VerifySeal checks whether the crypto seal on a header is valid according to
+ // the consensus rules of the given engine.
+ VerifySeal(chain ChainReader, header *types.Header) error
+
+ // Prepare initializes the consensus fields of a block header according to the
+ // rules of a particular engine. The changes are executed inline.
+ Prepare(chain ChainReader, header *types.Header) error
+
+ // Finalize runs any post-transaction state modifications (e.g. block rewards)
+ // and assembles the final block.
+ // 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)
+
+ // Seal generates a new sealing request for the given input block and pushes
+ // the result into the given channel.
+ //
+ // Note, the method returns immediately and will send the result async. More
+ // than one result may also be returned depending on the consensus algorithm.
+ Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
+
+ // SealHash returns the hash of a block prior to it being sealed.
+ SealHash(header *types.Header) common.Hash
+
+ // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
+ // that a new block should have.
+ CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
+
+ // APIs returns the RPC APIs this consensus engine provides.
+ APIs(chain ChainReader) []rpc.API
+
+ // Close terminates any background threads maintained by the consensus engine.
+ Close() error
+}
diff --git a/consensus/errors.go b/consensus/errors.go
new file mode 100644
index 000000000..a005c5f63
--- /dev/null
+++ b/consensus/errors.go
@@ -0,0 +1,37 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package consensus
+
+import "errors"
+
+var (
+ // ErrUnknownAncestor is returned when validating a block requires an ancestor
+ // that is unknown.
+ ErrUnknownAncestor = errors.New("unknown ancestor")
+
+ // ErrPrunedAncestor is returned when validating a block requires an ancestor
+ // that is known, but the state of which is not available.
+ ErrPrunedAncestor = errors.New("pruned ancestor")
+
+ // ErrFutureBlock is returned when a block's timestamp is in the future according
+ // to the current node.
+ ErrFutureBlock = errors.New("block in the future")
+
+ // ErrInvalidNumber is returned if a block's number doesn't equal it's parent's
+ // plus one.
+ ErrInvalidNumber = errors.New("invalid block number")
+)
diff --git a/core/block_validator.go b/core/block_validator.go
index 1329f6242..b35834504 100644
--- a/core/block_validator.go
+++ b/core/block_validator.go
@@ -19,10 +19,10 @@ package core
import (
"fmt"
- "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// BlockValidator is responsible for validating block headers, uncles and
@@ -61,9 +61,9 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
}
// Header validity is known at this point, check the uncles and transactions
header := block.Header()
- if err := v.engine.VerifyUncles(v.bc, block); err != nil {
- return err
- }
+ //if err := v.engine.VerifyUncles(v.bc, block); err != nil {
+ // return err
+ //}
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
}
diff --git a/core/blockchain.go b/core/blockchain.go
index 77c310528..c9517ace8 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -30,10 +30,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque"
- "github.com/ethereum/go-ethereum/consensus"
- "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"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@@ -44,6 +41,9 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/rawdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
var (
@@ -698,9 +698,9 @@ func (bc *BlockChain) procFutureBlocks() {
types.BlockBy(types.Number).Sort(blocks)
// Insert one by one as chain insertion needs contiguous ancestry between blocks
- for i := range blocks {
- bc.InsertChain(blocks[i : i+1])
- }
+ //for i := range blocks {
+ // bc.InsertChain(blocks[i : i+1])
+ //}
}
}
@@ -982,17 +982,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
}
}
if reorg {
- // Reorganise the chain if the parent is not the head block
- if block.ParentHash() != currentBlock.Hash() {
- if err := bc.reorg(currentBlock, block); err != nil {
- return NonStatTy, err
- }
- }
- // Write the positional metadata for transaction/receipt lookups and preimages
- rawdb.WriteTxLookupEntries(batch, block)
- rawdb.WritePreimages(batch, block.NumberU64(), state.Preimages())
-
- status = CanonStatTy
} else {
status = SideStatTy
}
@@ -1008,208 +997,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
return status, nil
}
-// InsertChain attempts to insert the given batch of blocks in to the canonical
-// chain or, otherwise, create a fork. If an error is returned it will return
-// the index number of the failing block as well an error describing what went
-// wrong.
-//
-// After insertion is done, all accumulated events will be fired.
-func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
- n, events, logs, err := bc.insertChain(chain)
- bc.PostChainEvents(events, logs)
- return n, err
-}
-
-// insertChain will execute the actual chain insertion and event aggregation. The
-// only reason this method exists as a separate one is to make locking cleaner
-// with deferred statements.
-func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error) {
- // Sanity check that we have something meaningful to import
- if len(chain) == 0 {
- return 0, nil, nil, nil
- }
- // Do a sanity check that the provided chain is actually ordered and linked
- for i := 1; i < len(chain); i++ {
- if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
- // Chain broke ancestry, log a message (programming error) and skip insertion
- log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
- "parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
-
- return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
- chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
- }
- }
- // Pre-checks passed, start the full block imports
- bc.wg.Add(1)
- defer bc.wg.Done()
-
- bc.chainmu.Lock()
- defer bc.chainmu.Unlock()
-
- // A queued approach to delivering events. This is generally
- // faster than direct delivery and requires much less mutex
- // acquiring.
- var (
- stats = insertStats{startTime: mclock.Now()}
- events = make([]interface{}, 0, len(chain))
- lastCanon *types.Block
- coalescedLogs []*types.Log
- )
- // Start the parallel header verifier
- headers := make([]*types.Header, len(chain))
- seals := make([]bool, len(chain))
-
- for i, block := range chain {
- headers[i] = block.Header()
- seals[i] = true
- }
- abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
- defer close(abort)
-
- // Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
- senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
-
- // Iterate over the blocks and insert when the verifier permits
- for i, block := range chain {
- // If the chain is terminating, stop processing blocks
- if atomic.LoadInt32(&bc.procInterrupt) == 1 {
- log.Debug("Premature abort during blocks processing")
- break
- }
-
- // Wait for the block's verification to complete
- bstart := time.Now()
-
- err := <-results
- if err == nil {
- err = bc.Validator().ValidateBody(block)
- }
- switch {
- case err == ErrKnownBlock:
- // Block and state both already known. However if the current block is below
- // this number we did a rollback and we should reimport it nonetheless.
- if bc.CurrentBlock().NumberU64() >= block.NumberU64() {
- stats.ignored++
- continue
- }
-
- case err == consensus.ErrFutureBlock:
- // Allow up to MaxFuture second in the future blocks. If this limit is exceeded
- // the chain is discarded and processed at a later time if given.
- max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
- if block.Time().Cmp(max) > 0 {
- return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
- }
- bc.futureBlocks.Add(block.Hash(), block)
- stats.queued++
- continue
-
- case err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()):
- bc.futureBlocks.Add(block.Hash(), block)
- stats.queued++
- continue
-
- case err == consensus.ErrPrunedAncestor:
- // Block competing with the canonical chain, store in the db, but don't process
- // until the competitor TD goes above the canonical TD
- currentBlock := bc.CurrentBlock()
- localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
- externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
- if localTd.Cmp(externTd) > 0 {
- if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
- return i, events, coalescedLogs, err
- }
- continue
- }
- // Competitor chain beat canonical, gather all blocks from the common ancestor
- var winner []*types.Block
-
- parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
- for !bc.HasState(parent.Root()) {
- winner = append(winner, parent)
- parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
- }
- for j := 0; j < len(winner)/2; j++ {
- winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]
- }
- // Import all the pruned blocks to make the state available
- bc.chainmu.Unlock()
- _, evs, logs, err := bc.insertChain(winner)
- bc.chainmu.Lock()
- events, coalescedLogs = evs, logs
-
- if err != nil {
- return i, events, coalescedLogs, err
- }
-
- case err != nil:
- bc.reportBlock(block, nil, err)
- return i, events, coalescedLogs, err
- }
- // Create a new statedb using the parent block and report an
- // error if it fails.
- var parent *types.Block
- if i == 0 {
- parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
- } else {
- parent = chain[i-1]
- }
- state, err := state.New(parent.Root(), bc.stateCache)
- if err != nil {
- return i, events, coalescedLogs, err
- }
- // Process block using the parent state as reference point.
- receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
- if err != nil {
- bc.reportBlock(block, receipts, err)
- return i, events, coalescedLogs, err
- }
- // Validate the state using the default validator
- err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas)
- if err != nil {
- bc.reportBlock(block, receipts, err)
- return i, events, coalescedLogs, err
- }
- proctime := time.Since(bstart)
-
- // Write the block to the chain and get the status.
- status, err := bc.WriteBlockWithState(block, receipts, state)
- if err != nil {
- return i, events, coalescedLogs, err
- }
- switch status {
- case CanonStatTy:
- log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
- "txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)))
-
- coalescedLogs = append(coalescedLogs, logs...)
- blockInsertTimer.UpdateSince(bstart)
- events = append(events, ChainEvent{block, block.Hash(), logs})
- lastCanon = block
-
- // Only count canonical blocks for GC processing time
- bc.gcproc += proctime
-
- case SideStatTy:
- log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
- common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
-
- blockInsertTimer.UpdateSince(bstart)
- events = append(events, ChainSideEvent{block})
- }
- stats.processed++
- stats.usedGas += usedGas
-
- cache, _ := bc.stateCache.TrieDB().Size()
- stats.report(chain, i, cache)
- }
- // Append a single chain head event if we've progressed the chain
- if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
- events = append(events, ChainHeadEvent{lastCanon})
- }
- return 0, events, coalescedLogs, nil
-}
-
// insertStats tracks and reports on block insertion.
type insertStats struct {
queued, processed, ignored int
@@ -1265,121 +1052,6 @@ func countTransactions(chain []*types.Block) (c int) {
return c
}
-// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
-// to be part of the new canonical chain and accumulates potential missing transactions and post an
-// event about them
-func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
- var (
- newChain types.Blocks
- oldChain types.Blocks
- commonBlock *types.Block
- deletedTxs types.Transactions
- deletedLogs []*types.Log
- // collectLogs collects the logs that were generated during the
- // processing of the block that corresponds with the given hash.
- // These logs are later announced as deleted.
- collectLogs = func(hash common.Hash) {
- // Coalesce logs and set 'Removed'.
- number := bc.hc.GetBlockNumber(hash)
- if number == nil {
- return
- }
- receipts := rawdb.ReadReceipts(bc.db, hash, *number)
- for _, receipt := range receipts {
- for _, log := range receipt.Logs {
- del := *log
- del.Removed = true
- deletedLogs = append(deletedLogs, &del)
- }
- }
- }
- )
-
- // first reduce whoever is higher bound
- if oldBlock.NumberU64() > newBlock.NumberU64() {
- // reduce old chain
- for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
- oldChain = append(oldChain, oldBlock)
- deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
-
- collectLogs(oldBlock.Hash())
- }
- } else {
- // reduce new chain and append new chain blocks for inserting later on
- for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) {
- newChain = append(newChain, newBlock)
- }
- }
- if oldBlock == nil {
- return fmt.Errorf("Invalid old chain")
- }
- if newBlock == nil {
- return fmt.Errorf("Invalid new chain")
- }
-
- for {
- if oldBlock.Hash() == newBlock.Hash() {
- commonBlock = oldBlock
- break
- }
-
- oldChain = append(oldChain, oldBlock)
- newChain = append(newChain, newBlock)
- deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
- collectLogs(oldBlock.Hash())
-
- oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
- if oldBlock == nil {
- return fmt.Errorf("Invalid old chain")
- }
- if newBlock == nil {
- return fmt.Errorf("Invalid new chain")
- }
- }
- // Ensure the user sees large reorgs
- if len(oldChain) > 0 && len(newChain) > 0 {
- logFn := log.Debug
- if len(oldChain) > 63 {
- logFn = log.Warn
- }
- logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(),
- "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash())
- } else {
- log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
- }
- // Insert the new chain, taking care of the proper incremental order
- var addedTxs types.Transactions
- for i := len(newChain) - 1; i >= 0; i-- {
- // insert the block in the canonical way, re-writing history
- bc.insert(newChain[i])
- // write lookup entries for hash based transaction/receipt searches
- rawdb.WriteTxLookupEntries(bc.db, newChain[i])
- addedTxs = append(addedTxs, newChain[i].Transactions()...)
- }
- // calculate the difference between deleted and added transactions
- diff := types.TxDifference(deletedTxs, addedTxs)
- // When transactions get deleted from the database that means the
- // receipts that were created in the fork must also be deleted
- batch := bc.db.NewBatch()
- for _, tx := range diff {
- rawdb.DeleteTxLookupEntry(batch, tx.Hash())
- }
- batch.Write()
-
- if len(deletedLogs) > 0 {
- go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
- }
- if len(oldChain) > 0 {
- go func() {
- for _, block := range oldChain {
- bc.chainSideFeed.Send(ChainSideEvent{Block: block})
- }
- }()
- }
-
- return nil
-}
-
// PostChainEvents iterates over the events generated by a chain insertion and
// posts them into the event feed.
// TODO: Should not expose PostChainEvents. The chain events should be posted in WriteBlock.
diff --git a/core/events.go b/core/events.go
index 710bdb589..e91c0844d 100644
--- a/core/events.go
+++ b/core/events.go
@@ -18,7 +18,7 @@ package core
import (
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
diff --git a/core/evm.go b/core/evm.go
index d303c40a4..051b4340d 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -20,9 +20,9 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// ChainContext supports retrieving headers and consensus parameters from the
diff --git a/core/headerchain.go b/core/headerchain.go
index d197eab3f..73d05c4cb 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -27,13 +27,13 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
- "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"
"github.com/ethereum/go-ethereum/params"
"github.com/hashicorp/golang-lru"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/rawdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
const (
@@ -149,7 +149,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if err := hc.WriteTd(hash, number, externTd); err != nil {
log.Crit("Failed to write header total difficulty", "err", err)
}
- rawdb.WriteHeader(hc.chainDb, header)
+ //rawdb.WriteHeader(hc.chainDb, header)
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
@@ -228,22 +228,22 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
}
seals[len(seals)-1] = true // Last should always be verified to avoid junk
- abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
- defer close(abort)
-
- // Iterate over the headers and ensure they all check out
- for i, _ := range chain {
- // If the chain is terminating, stop processing blocks
- if hc.procInterrupt() {
- log.Debug("Premature abort during headers verification")
- return 0, errors.New("aborted")
- }
-
- // Otherwise wait for headers checks and ensure they pass
- if err := <-results; err != nil {
- return i, err
- }
- }
+ //abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
+ //defer close(abort)
+ //
+ //// Iterate over the headers and ensure they all check out
+ //for i, _ := range chain {
+ // // If the chain is terminating, stop processing blocks
+ // if hc.procInterrupt() {
+ // log.Debug("Premature abort during headers verification")
+ // return 0, errors.New("aborted")
+ // }
+ //
+ // // Otherwise wait for headers checks and ensure they pass
+ // if err := <-results; err != nil {
+ // return i, err
+ // }
+ //}
return 0, nil
}
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
new file mode 100644
index 000000000..fae60af2e
--- /dev/null
+++ b/core/rawdb/accessors_chain.go
@@ -0,0 +1,375 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "bytes"
+ "encoding/binary"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
+func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash {
+ data, _ := db.Get(headerHashKey(number))
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteCanonicalHash stores the hash assigned to a canonical block number.
+func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) {
+ if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil {
+ log.Crit("Failed to store number to hash mapping", "err", err)
+ }
+}
+
+// DeleteCanonicalHash removes the number to hash canonical mapping.
+func DeleteCanonicalHash(db DatabaseDeleter, number uint64) {
+ if err := db.Delete(headerHashKey(number)); err != nil {
+ log.Crit("Failed to delete number to hash mapping", "err", err)
+ }
+}
+
+// ReadHeaderNumber returns the header number assigned to a hash.
+func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 {
+ data, _ := db.Get(headerNumberKey(hash))
+ if len(data) != 8 {
+ return nil
+ }
+ number := binary.BigEndian.Uint64(data)
+ return &number
+}
+
+// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
+func ReadHeadHeaderHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headHeaderKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadHeaderHash stores the hash of the current canonical head header.
+func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last header's hash", "err", err)
+ }
+}
+
+// ReadHeadBlockHash retrieves the hash of the current canonical head block.
+func ReadHeadBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadBlockHash stores the head block's hash.
+func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last block's hash", "err", err)
+ }
+}
+
+// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
+func ReadHeadFastBlockHash(db DatabaseReader) common.Hash {
+ data, _ := db.Get(headFastBlockKey)
+ if len(data) == 0 {
+ return common.Hash{}
+ }
+ return common.BytesToHash(data)
+}
+
+// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
+func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) {
+ if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil {
+ log.Crit("Failed to store last fast block's hash", "err", err)
+ }
+}
+
+// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
+// reporting correct numbers across restarts.
+func ReadFastTrieProgress(db DatabaseReader) uint64 {
+ data, _ := db.Get(fastTrieProgressKey)
+ if len(data) == 0 {
+ return 0
+ }
+ return new(big.Int).SetBytes(data).Uint64()
+}
+
+// WriteFastTrieProgress stores the fast sync trie process counter to support
+// retrieving it across restarts.
+func WriteFastTrieProgress(db DatabaseWriter, count uint64) {
+ if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil {
+ log.Crit("Failed to store fast sync trie progress", "err", err)
+ }
+}
+
+// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
+func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(headerKey(number, hash))
+ return data
+}
+
+// HasHeader verifies the existence of a block header corresponding to the hash.
+func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool {
+ if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadHeader retrieves the block header corresponding to the hash.
+func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header {
+ data := ReadHeaderRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ header := new(types.Header)
+ if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
+ log.Error("Invalid block header RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return header
+}
+
+// WriteHeader stores a block header into the database and also stores the hash-
+// to-number mapping.
+func WriteHeader(db DatabaseWriter, header *types.Header) {
+ // Write the hash -> number mapping
+ var (
+ hash = header.Hash()
+ number = header.Number.Uint64()
+ encoded = encodeBlockNumber(number)
+ )
+ key := headerNumberKey(hash)
+ if err := db.Put(key, encoded); err != nil {
+ log.Crit("Failed to store hash to number mapping", "err", err)
+ }
+ // Write the encoded header
+ data, err := rlp.EncodeToBytes(header)
+ if err != nil {
+ log.Crit("Failed to RLP encode header", "err", err)
+ }
+ key = headerKey(number, hash)
+ if err := db.Put(key, data); err != nil {
+ log.Crit("Failed to store header", "err", err)
+ }
+}
+
+// DeleteHeader removes all block header data associated with a hash.
+func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(headerKey(number, hash)); err != nil {
+ log.Crit("Failed to delete header", "err", err)
+ }
+ if err := db.Delete(headerNumberKey(hash)); err != nil {
+ log.Crit("Failed to delete hash to number mapping", "err", err)
+ }
+}
+
+// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
+func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue {
+ data, _ := db.Get(blockBodyKey(number, hash))
+ return data
+}
+
+// WriteBodyRLP stores an RLP encoded block body into the database.
+func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
+ if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
+ log.Crit("Failed to store block body", "err", err)
+ }
+}
+
+// HasBody verifies the existence of a block body corresponding to the hash.
+func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool {
+ if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
+ return false
+ }
+ return true
+}
+
+// ReadBody retrieves the block body corresponding to the hash.
+func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body {
+ data := ReadBodyRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ body := new(types.Body)
+ if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
+ log.Error("Invalid block body RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return body
+}
+
+// WriteBody storea a block body into the database.
+func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) {
+ data, err := rlp.EncodeToBytes(body)
+ if err != nil {
+ log.Crit("Failed to RLP encode body", "err", err)
+ }
+ WriteBodyRLP(db, hash, number, data)
+}
+
+// DeleteBody removes all block body data associated with a hash.
+func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(blockBodyKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block body", "err", err)
+ }
+}
+
+// ReadTd retrieves a block's total difficulty corresponding to the hash.
+func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int {
+ data, _ := db.Get(headerTDKey(number, hash))
+ if len(data) == 0 {
+ return nil
+ }
+ td := new(big.Int)
+ if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
+ log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
+ return nil
+ }
+ return td
+}
+
+// WriteTd stores the total difficulty of a block into the database.
+func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) {
+ data, err := rlp.EncodeToBytes(td)
+ if err != nil {
+ log.Crit("Failed to RLP encode block total difficulty", "err", err)
+ }
+ if err := db.Put(headerTDKey(number, hash), data); err != nil {
+ log.Crit("Failed to store block total difficulty", "err", err)
+ }
+}
+
+// DeleteTd removes all block total difficulty data associated with a hash.
+func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(headerTDKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block total difficulty", "err", err)
+ }
+}
+
+// ReadReceipts retrieves all the transaction receipts belonging to a block.
+func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts {
+ // Retrieve the flattened receipt slice
+ data, _ := db.Get(blockReceiptsKey(number, hash))
+ if len(data) == 0 {
+ return nil
+ }
+ // Convert the receipts from their storage form to their internal representation
+ storageReceipts := []*types.ReceiptForStorage{}
+ if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
+ log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+ return nil
+ }
+ receipts := make(types.Receipts, len(storageReceipts))
+ for i, receipt := range storageReceipts {
+ receipts[i] = (*types.Receipt)(receipt)
+ }
+ return receipts
+}
+
+// WriteReceipts stores all the transaction receipts belonging to a block.
+func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) {
+ // Convert the receipts into their storage form and serialize them
+ storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
+ for i, receipt := range receipts {
+ storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+ }
+ bytes, err := rlp.EncodeToBytes(storageReceipts)
+ if err != nil {
+ log.Crit("Failed to encode block receipts", "err", err)
+ }
+ // Store the flattened receipt slice
+ if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil {
+ log.Crit("Failed to store block receipts", "err", err)
+ }
+}
+
+// DeleteReceipts removes all receipt data associated with a block hash.
+func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) {
+ if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
+ log.Crit("Failed to delete block receipts", "err", err)
+ }
+}
+
+// ReadBlock retrieves an entire block corresponding to the hash, assembling it
+// back from the stored header and body. If either the header or body could not
+// be retrieved nil is returned.
+//
+// Note, due to concurrent download of header and block body the header and thus
+// canonical hash can be stored in the database but the body data not (yet).
+func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block {
+ header := ReadHeader(db, hash, number)
+ if header == nil {
+ return nil
+ }
+ body := ReadBody(db, hash, number)
+ if body == nil {
+ return nil
+ }
+ return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
+}
+
+// WriteBlock serializes a block into the database, header and body separately.
+func WriteBlock(db DatabaseWriter, block *types.Block) {
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ WriteHeader(db, block.Header())
+}
+
+// DeleteBlock removes all block data associated with a hash.
+func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) {
+ DeleteReceipts(db, hash, number)
+ DeleteHeader(db, hash, number)
+ DeleteBody(db, hash, number)
+ DeleteTd(db, hash, number)
+}
+
+// FindCommonAncestor returns the last common ancestor of two block headers
+func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header {
+ for bn := b.Number.Uint64(); a.Number.Uint64() > bn; {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ }
+ for an := a.Number.Uint64(); an < b.Number.Uint64(); {
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ for a.Hash() != b.Hash() {
+ a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1)
+ if a == nil {
+ return nil
+ }
+ b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1)
+ if b == nil {
+ return nil
+ }
+ }
+ return a
+}
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
new file mode 100644
index 000000000..7cc6991e0
--- /dev/null
+++ b/core/rawdb/accessors_chain_test.go
@@ -0,0 +1,319 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "bytes"
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto/sha3"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Tests block header storage and retrieval operations.
+func TestHeaderStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test header to move around the database and make sure it's really new
+ header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ // Write and verify the header in the database
+ WriteHeader(db, header)
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ t.Fatalf("Stored header not found")
+ } else if entry.Hash() != header.Hash() {
+ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
+ }
+ if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
+ t.Fatalf("Stored header RLP not found")
+ } else {
+ hasher := sha3.NewKeccak256()
+ hasher.Write(entry)
+
+ if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
+ t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
+ }
+ }
+ // Delete the header and verify the execution
+ DeleteHeader(db, header.Hash(), header.Number.Uint64())
+ if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
+ t.Fatalf("Deleted header returned: %v", entry)
+ }
+}
+
+// Tests block body storage and retrieval operations.
+func TestBodyStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test body to move around the database and make sure it's really new
+ body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
+
+ hasher := sha3.NewKeccak256()
+ rlp.Encode(hasher, body)
+ hash := common.BytesToHash(hasher.Sum(nil))
+
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the body in the database
+ WriteBody(db, hash, 0, body)
+ if entry := ReadBody(db, hash, 0); entry == nil {
+ t.Fatalf("Stored body not found")
+ } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
+ t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
+ }
+ if entry := ReadBodyRLP(db, hash, 0); entry == nil {
+ t.Fatalf("Stored body RLP not found")
+ } else {
+ hasher := sha3.NewKeccak256()
+ hasher.Write(entry)
+
+ if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
+ t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
+ }
+ }
+ // Delete the body and verify the execution
+ DeleteBody(db, hash, 0)
+ if entry := ReadBody(db, hash, 0); entry != nil {
+ t.Fatalf("Deleted body returned: %v", entry)
+ }
+}
+
+// Tests block storage and retrieval operations.
+func TestBlockStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test block to move around the database and make sure it's really new
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent body returned: %v", entry)
+ }
+ // Write and verify the block in the database
+ WriteBlock(db, block)
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored block not found")
+ } else if entry.Hash() != block.Hash() {
+ t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored header not found")
+ } else if entry.Hash() != block.Header().Hash() {
+ t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored body not found")
+ } else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
+ t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
+ }
+ // Delete the block and verify the execution
+ DeleteBlock(db, block.Hash(), block.NumberU64())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted block returned: %v", entry)
+ }
+ if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted header returned: %v", entry)
+ }
+ if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Deleted body returned: %v", entry)
+ }
+}
+
+// Tests that partial block contents don't get reassembled into full blocks.
+func TestPartialBlockStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+ block := types.NewBlockWithHeader(&types.Header{
+ Extra: []byte("test block"),
+ UncleHash: types.EmptyUncleHash,
+ TxHash: types.EmptyRootHash,
+ ReceiptHash: types.EmptyRootHash,
+ })
+ // Store a header and check that it's not recognized as a block
+ WriteHeader(db, block.Header())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteHeader(db, block.Hash(), block.NumberU64())
+
+ // Store a body and check that it's not recognized as a block
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil {
+ t.Fatalf("Non existent block returned: %v", entry)
+ }
+ DeleteBody(db, block.Hash(), block.NumberU64())
+
+ // Store a header and a body separately and check reassembly
+ WriteHeader(db, block.Header())
+ WriteBody(db, block.Hash(), block.NumberU64(), block.Body())
+
+ if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil {
+ t.Fatalf("Stored block not found")
+ } else if entry.Hash() != block.Hash() {
+ t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
+ }
+}
+
+// Tests block total difficulty storage and retrieval operations.
+func TestTdStorage(t *testing.T) {
+ 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)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ t.Fatalf("Non existent TD returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteTd(db, hash, 0, td)
+ if entry := ReadTd(db, hash, 0); entry == nil {
+ t.Fatalf("Stored TD not found")
+ } else if entry.Cmp(td) != 0 {
+ t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
+ }
+ // Delete the TD and verify the execution
+ DeleteTd(db, hash, 0)
+ if entry := ReadTd(db, hash, 0); entry != nil {
+ t.Fatalf("Deleted TD returned: %v", entry)
+ }
+}
+
+// Tests that canonical numbers can be mapped to hashes and retrieved.
+func TestCanonicalMappingStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ // Create a test canonical number and assinged hash to move around
+ hash, number := common.Hash{0: 0xff}, uint64(314)
+ if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
+ t.Fatalf("Non existent canonical mapping returned: %v", entry)
+ }
+ // Write and verify the TD in the database
+ WriteCanonicalHash(db, hash, number)
+ if entry := ReadCanonicalHash(db, number); entry == (common.Hash{}) {
+ t.Fatalf("Stored canonical mapping not found")
+ } else if entry != hash {
+ t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
+ }
+ // Delete the TD and verify the execution
+ DeleteCanonicalHash(db, number)
+ if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) {
+ t.Fatalf("Deleted canonical mapping returned: %v", entry)
+ }
+}
+
+// Tests that head headers and head blocks can be assigned, individually.
+func TestHeadStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
+ blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
+ blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
+
+ // Check that no head entries are in a pristine database
+ if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head header entry returned: %v", entry)
+ }
+ if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non head block entry returned: %v", entry)
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) {
+ t.Fatalf("Non fast head block entry returned: %v", entry)
+ }
+ // Assign separate entries for the head header and block
+ WriteHeadHeaderHash(db, blockHead.Hash())
+ WriteHeadBlockHash(db, blockFull.Hash())
+ WriteHeadFastBlockHash(db, blockFast.Hash())
+
+ // Check that both heads are present, and different (i.e. two heads maintained)
+ if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() {
+ t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
+ }
+ if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() {
+ t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
+ }
+ if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() {
+ t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
+ }
+}
+
+// Tests that receipts associated with a single block can be stored and retrieved.
+func TestBlockReceiptStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ receipt1 := &types.Receipt{
+ Status: types.ReceiptStatusFailed,
+ 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: 111111,
+ }
+ receipt2 := &types.Receipt{
+ PostState: common.Hash{2}.Bytes(),
+ 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: 222222,
+ }
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ // Check that no receipt entries are in a pristine database
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("non existent receipts returned: %v", rs)
+ }
+ // Insert the receipt slice into the database and check presence
+ WriteReceipts(db, hash, 0, receipts)
+ if rs := ReadReceipts(db, hash, 0); len(rs) == 0 {
+ t.Fatalf("no receipts returned")
+ } else {
+ for i := 0; i < len(receipts); i++ {
+ rlpHave, _ := rlp.EncodeToBytes(rs[i])
+ rlpWant, _ := rlp.EncodeToBytes(receipts[i])
+
+ if !bytes.Equal(rlpHave, rlpWant) {
+ t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
+ }
+ }
+ }
+ // Delete the receipt slice and check purge
+ DeleteReceipts(db, hash, 0)
+ if rs := ReadReceipts(db, hash, 0); len(rs) != 0 {
+ t.Fatalf("deleted receipts returned: %v", rs)
+ }
+}
diff --git a/core/rawdb/accessors_indexes.go b/core/rawdb/accessors_indexes.go
new file mode 100644
index 000000000..b664bf21b
--- /dev/null
+++ b/core/rawdb/accessors_indexes.go
@@ -0,0 +1,107 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
+// hash to allow retrieving the transaction or receipt by hash.
+func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) {
+ data, _ := db.Get(txLookupKey(hash))
+ if len(data) == 0 {
+ return common.Hash{}, 0, 0
+ }
+ var entry TxLookupEntry
+ if err := rlp.DecodeBytes(data, &entry); err != nil {
+ log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err)
+ return common.Hash{}, 0, 0
+ }
+ return entry.BlockHash, entry.BlockIndex, entry.Index
+}
+
+// WriteTxLookupEntries stores a positional metadata for every transaction from
+// a block, enabling hash based transaction and receipt lookups.
+func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) {
+ for i, tx := range block.Transactions() {
+ entry := TxLookupEntry{
+ BlockHash: block.Hash(),
+ BlockIndex: block.NumberU64(),
+ Index: uint64(i),
+ }
+ data, err := rlp.EncodeToBytes(entry)
+ if err != nil {
+ log.Crit("Failed to encode transaction lookup entry", "err", err)
+ }
+ if err := db.Put(txLookupKey(tx.Hash()), data); err != nil {
+ log.Crit("Failed to store transaction lookup entry", "err", err)
+ }
+ }
+}
+
+// DeleteTxLookupEntry removes all transaction data associated with a hash.
+func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) {
+ db.Delete(txLookupKey(hash))
+}
+
+// ReadTransaction retrieves a specific transaction from the database, along with
+// its added positional metadata.
+func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ body := ReadBody(db, blockHash, blockNumber)
+ if body == nil || len(body.Transactions) <= int(txIndex) {
+ log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return body.Transactions[txIndex], blockHash, blockNumber, txIndex
+}
+
+// ReadReceipt retrieves a specific transaction receipt from the database, along with
+// its added positional metadata.
+func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
+ blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash)
+ if blockHash == (common.Hash{}) {
+ return nil, common.Hash{}, 0, 0
+ }
+ receipts := ReadReceipts(db, blockHash, blockNumber)
+ if len(receipts) <= int(receiptIndex) {
+ log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
+ return nil, common.Hash{}, 0, 0
+ }
+ return receipts[receiptIndex], blockHash, blockNumber, receiptIndex
+}
+
+// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
+// section and bit index from the.
+func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) {
+ return db.Get(bloomBitsKey(bit, section, head))
+}
+
+// WriteBloomBits stores the compressed bloom bits vector belonging to the given
+// section and bit index.
+func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) {
+ if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil {
+ log.Crit("Failed to store bloom bits", "err", err)
+ }
+}
diff --git a/core/rawdb/accessors_indexes_test.go b/core/rawdb/accessors_indexes_test.go
new file mode 100644
index 000000000..d9c4c2f6d
--- /dev/null
+++ b/core/rawdb/accessors_indexes_test.go
@@ -0,0 +1,68 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+)
+
+// Tests that positional lookup metadata can be stored and retrieved.
+func TestLookupStorage(t *testing.T) {
+ db := ethdb.NewMemDatabase()
+
+ tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
+ tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
+ tx3 := types.NewTransaction(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)
+
+ // Check that no transactions entries are in a pristine database
+ for i, tx := range txs {
+ if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+ // Insert all the transactions into the database, and verify contents
+ WriteBlock(db, block)
+ WriteTxLookupEntries(db, block)
+
+ for i, tx := range txs {
+ if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil {
+ t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
+ } else {
+ if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
+ t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
+ }
+ if tx.Hash() != txn.Hash() {
+ t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
+ }
+ }
+ }
+ // Delete the transactions and check purge
+ for i, tx := range txs {
+ DeleteTxLookupEntry(db, tx.Hash())
+ if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+}
diff --git a/core/rawdb/accessors_metadata.go b/core/rawdb/accessors_metadata.go
new file mode 100644
index 000000000..514328e87
--- /dev/null
+++ b/core/rawdb/accessors_metadata.go
@@ -0,0 +1,90 @@
+// Copyright 2018 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 rawdb
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// ReadDatabaseVersion retrieves the version number of the database.
+func ReadDatabaseVersion(db DatabaseReader) int {
+ var version int
+
+ enc, _ := db.Get(databaseVerisionKey)
+ rlp.DecodeBytes(enc, &version)
+
+ return version
+}
+
+// WriteDatabaseVersion stores the version number of the database
+func WriteDatabaseVersion(db DatabaseWriter, version int) {
+ enc, _ := rlp.EncodeToBytes(version)
+ if err := db.Put(databaseVerisionKey, enc); err != nil {
+ log.Crit("Failed to store the database version", "err", err)
+ }
+}
+
+// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
+func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig {
+ data, _ := db.Get(configKey(hash))
+ if len(data) == 0 {
+ return nil
+ }
+ var config params.ChainConfig
+ if err := json.Unmarshal(data, &config); err != nil {
+ log.Error("Invalid chain config JSON", "hash", hash, "err", err)
+ return nil
+ }
+ return &config
+}
+
+// WriteChainConfig writes the chain config settings to the database.
+func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) {
+ if cfg == nil {
+ return
+ }
+ data, err := json.Marshal(cfg)
+ if err != nil {
+ log.Crit("Failed to JSON encode chain config", "err", err)
+ }
+ if err := db.Put(configKey(hash), data); err != nil {
+ log.Crit("Failed to store chain config", "err", err)
+ }
+}
+
+// ReadPreimage retrieves a single preimage of the provided hash.
+func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
+ data, _ := db.Get(preimageKey(hash))
+ return data
+}
+
+// WritePreimages writes the provided set of preimages to the database. `number` is the
+// current block number, and is used for debug messages only.
+func WritePreimages(db DatabaseWriter, number uint64, preimages map[common.Hash][]byte) {
+ for hash, preimage := range preimages {
+ if err := db.Put(preimageKey(hash), preimage); err != nil {
+ log.Crit("Failed to store trie preimage", "err", err)
+ }
+ }
+ preimageCounter.Inc(int64(len(preimages)))
+ preimageHitCounter.Inc(int64(len(preimages)))
+}
diff --git a/core/rawdb/interfaces.go b/core/rawdb/interfaces.go
new file mode 100644
index 000000000..3bdf55124
--- /dev/null
+++ b/core/rawdb/interfaces.go
@@ -0,0 +1,33 @@
+// Copyright 2018 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 rawdb
+
+// DatabaseReader wraps the Has and Get method of a backing data store.
+type DatabaseReader interface {
+ Has(key []byte) (bool, error)
+ Get(key []byte) ([]byte, error)
+}
+
+// DatabaseWriter wraps the Put method of a backing data store.
+type DatabaseWriter interface {
+ Put(key []byte, value []byte) error
+}
+
+// DatabaseDeleter wraps the Delete method of a backing data store.
+type DatabaseDeleter interface {
+ Delete(key []byte) error
+}
diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go
new file mode 100644
index 000000000..ef597ef30
--- /dev/null
+++ b/core/rawdb/schema.go
@@ -0,0 +1,134 @@
+// Copyright 2018 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 rawdb contains a collection of low level database accessors.
+package rawdb
+
+import (
+ "encoding/binary"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+// The fields below define the low level database schema prefixing.
+var (
+ // databaseVerisionKey tracks the current database version.
+ databaseVerisionKey = []byte("DatabaseVersion")
+
+ // headHeaderKey tracks the latest know header's hash.
+ headHeaderKey = []byte("LastHeader")
+
+ // headBlockKey tracks the latest know full block's hash.
+ headBlockKey = []byte("LastBlock")
+
+ // headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync.
+ headFastBlockKey = []byte("LastFast")
+
+ // fastTrieProgressKey tracks the number of trie entries imported during fast sync.
+ fastTrieProgressKey = []byte("TrieSync")
+
+ // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
+ headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
+ headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
+ headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
+ headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
+
+ blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
+ blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
+
+ txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
+ bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
+
+ preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
+ configPrefix = []byte("ethereum-config-") // config prefix for the db
+
+ // Chain index prefixes (use `i` + single byte to avoid mixing data types).
+ BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
+
+ preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
+ preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
+)
+
+// TxLookupEntry is a positional metadata to help looking up the data content of
+// a transaction or receipt given only its hash.
+type TxLookupEntry struct {
+ BlockHash common.Hash
+ BlockIndex uint64
+ Index uint64
+}
+
+// encodeBlockNumber encodes a block number as big endian uint64
+func encodeBlockNumber(number uint64) []byte {
+ enc := make([]byte, 8)
+ binary.BigEndian.PutUint64(enc, number)
+ return enc
+}
+
+// headerKey = headerPrefix + num (uint64 big endian) + hash
+func headerKey(number uint64, hash common.Hash) []byte {
+ return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix
+func headerTDKey(number uint64, hash common.Hash) []byte {
+ return append(headerKey(number, hash), headerTDSuffix...)
+}
+
+// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
+func headerHashKey(number uint64) []byte {
+ return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
+}
+
+// headerNumberKey = headerNumberPrefix + hash
+func headerNumberKey(hash common.Hash) []byte {
+ return append(headerNumberPrefix, hash.Bytes()...)
+}
+
+// blockBodyKey = blockBodyPrefix + num (uint64 big endian) + hash
+func blockBodyKey(number uint64, hash common.Hash) []byte {
+ return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
+func blockReceiptsKey(number uint64, hash common.Hash) []byte {
+ return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
+}
+
+// txLookupKey = txLookupPrefix + hash
+func txLookupKey(hash common.Hash) []byte {
+ return append(txLookupPrefix, hash.Bytes()...)
+}
+
+// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
+func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte {
+ key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...)
+
+ binary.BigEndian.PutUint16(key[1:], uint16(bit))
+ binary.BigEndian.PutUint64(key[3:], section)
+
+ return key
+}
+
+// preimageKey = preimagePrefix + hash
+func preimageKey(hash common.Hash) []byte {
+ return append(preimagePrefix, hash.Bytes()...)
+}
+
+// configKey = configPrefix + hash
+func configKey(hash common.Hash) []byte {
+ return append(configPrefix, hash.Bytes()...)
+}
diff --git a/core/state_processor.go b/core/state_processor.go
index 503a35d16..290cdfee2 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -18,13 +18,13 @@ package core
import (
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/consensus"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// StateProcessor is a basic Processor, which takes care of transitioning
@@ -119,7 +119,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
}
// Set the receipt logs and create a bloom for filtering
- receipt.Logs = statedb.GetLogs(tx.Hash())
+ //receipt.Logs = statedb.GetLogs(tx.Hash())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
return receipt, gas, err
diff --git a/core/tx_journal.go b/core/tx_journal.go
index 41b5156d4..3d6c1fac5 100644
--- a/core/tx_journal.go
+++ b/core/tx_journal.go
@@ -22,9 +22,9 @@ import (
"os"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// errNoActiveJournal is returned if a transaction is attempted to be inserted
diff --git a/core/tx_list.go b/core/tx_list.go
index 57abc5148..a107057f4 100644
--- a/core/tx_list.go
+++ b/core/tx_list.go
@@ -23,8 +23,8 @@ import (
"sort"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
diff --git a/core/tx_list_test.go b/core/tx_list_test.go
index d579f501a..1200c6b02 100644
--- a/core/tx_list_test.go
+++ b/core/tx_list_test.go
@@ -20,8 +20,8 @@ import (
"math/rand"
"testing"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// Tests that transactions can be added to strict lists and list contents and
diff --git a/core/tx_pool.go b/core/tx_pool.go
index f6da5da2a..6b7fa4ff0 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -28,11 +28,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
const (
@@ -279,7 +279,7 @@ func (pool *TxPool) loop() {
defer journal.Stop()
// Track the previous head headers for transaction reorgs
- head := pool.chain.CurrentBlock()
+ //head := pool.chain.CurrentBlock()
// Keep waiting for and reacting to the various events
for {
@@ -288,11 +288,11 @@ func (pool *TxPool) loop() {
case ev := <-pool.chainHeadCh:
if ev.Block != nil {
pool.mu.Lock()
- if pool.chainconfig.IsHomestead(ev.Block.Number()) {
- pool.homestead = true
- }
- pool.reset(head.Header(), ev.Block.Header())
- head = ev.Block
+ //if pool.chainconfig.IsHomestead(ev.Block.Number()) {
+ // pool.homestead = true
+ //}
+ //pool.reset(head.Header(), ev.Block.Header())
+ //head = ev.Block
pool.mu.Unlock()
}
@@ -416,7 +416,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
- senderCacher.recover(pool.signer, reinject)
+ //senderCacher.recover(pool.signer, reinject)
pool.addTxsLocked(reinject, false)
// validate the pool of pending transactions, this will remove
@@ -667,7 +667,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
// We've directly injected a replacement transaction, notify subsystems
- go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
+ // go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})
return old != nil, nil
}
@@ -976,9 +976,9 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
}
}
// Notify subsystem for new promoted transactions.
- if len(promoted) > 0 {
- go pool.txFeed.Send(NewTxsEvent{promoted})
- }
+ //if len(promoted) > 0 {
+ // go pool.txFeed.Send(NewTxsEvent{promoted})
+ //}
// If the pending limit is overflown, start equalizing allowances
pending := uint64(0)
for _, list := range pool.pending {
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 5a5920544..c74d4c91a 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -28,11 +28,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// testTxPoolConfig is a transaction pool configuration without stateful disk
@@ -661,63 +661,6 @@ func TestTransactionPostponing(t *testing.T) {
}
}
-// Tests that if the transaction pool has both executable and non-executable
-// transactions from an origin account, filling the nonce gap moves all queued
-// ones into the pending pool.
-func TestTransactionGapFilling(t *testing.T) {
- t.Parallel()
-
- // Create a test account and fund it
- pool, key := setupTxPool()
- defer pool.Stop()
-
- account, _ := deriveSender(transaction(0, 0, key))
- pool.currentState.AddBalance(account, big.NewInt(1000000))
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Create a pending and a queued transaction with a nonce-gap in between
- if err := pool.AddRemote(transaction(0, 100000, key)); err != nil {
- t.Fatalf("failed to add pending transaction: %v", err)
- }
- if err := pool.AddRemote(transaction(2, 100000, key)); err != nil {
- t.Fatalf("failed to add queued transaction: %v", err)
- }
- pending, queued := pool.Stats()
- if pending != 1 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 1)
- }
- if queued != 1 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
- }
- if err := validateEvents(events, 1); err != nil {
- t.Fatalf("original event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Fill the nonce gap and ensure all transactions become pending
- if err := pool.AddRemote(transaction(1, 100000, key)); err != nil {
- t.Fatalf("failed to add gapped transaction: %v", err)
- }
- pending, queued = pool.Stats()
- if pending != 3 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
- }
- if queued != 0 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
- }
- if err := validateEvents(events, 2); err != nil {
- t.Fatalf("gap-filling event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
// Tests that if the transaction count belonging to a single account goes above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func TestTransactionQueueAccountLimiting(t *testing.T) {
@@ -912,47 +855,6 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
}
}
-// Tests that even if the transaction count belonging to a single account goes
-// above some threshold, as long as the transactions are executable, they are
-// accepted.
-func TestTransactionPendingLimiting(t *testing.T) {
- t.Parallel()
-
- // Create a test account and fund it
- pool, key := setupTxPool()
- defer pool.Stop()
-
- account, _ := deriveSender(transaction(0, 0, key))
- pool.currentState.AddBalance(account, big.NewInt(1000000))
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, testTxPoolConfig.AccountQueue+5)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Keep queuing up transactions and make sure all above a limit are dropped
- for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
- if err := pool.AddRemote(transaction(i, 100000, key)); err != nil {
- t.Fatalf("tx %d: failed to add transaction: %v", i, err)
- }
- if pool.pending[account].Len() != int(i)+1 {
- t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, pool.pending[account].Len(), i+1)
- }
- if len(pool.queue) != 0 {
- t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
- }
- }
- if pool.all.Count() != int(testTxPoolConfig.AccountQueue+5) {
- t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), testTxPoolConfig.AccountQueue+5)
- }
- if err := validateEvents(events, int(testTxPoolConfig.AccountQueue+5)); err != nil {
- t.Fatalf("event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
// Tests that the transaction limits are enforced the same way irrelevant whether
// the transactions are added one by one or in batches.
func TestTransactionQueueLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 1) }
@@ -1130,130 +1032,6 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
}
}
-// Tests that setting the transaction pool gas price to a higher value correctly
-// discards everything cheaper than that and moves any gapped transactions back
-// from the pending pool to the queue.
-//
-// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolRepricing(t *testing.T) {
- t.Parallel()
-
- // Create the pool to test the pricing enforcement with
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
-
- pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
- defer pool.Stop()
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Create a number of test accounts and fund them
- keys := make([]*ecdsa.PrivateKey, 4)
- for i := 0; i < len(keys); i++ {
- keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
- }
- // Generate and queue a batch of transactions, both pending and queued
- txs := types.Transactions{}
-
- txs = append(txs, pricedTransaction(0, 100000, big.NewInt(2), keys[0]))
- txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
- txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
-
- txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1]))
- txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
- txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[1]))
-
- txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[2]))
- txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2]))
- txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[2]))
-
- ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[3])
-
- // Import the batch and that both pending and queued transactions match up
- pool.AddRemotes(txs)
- pool.AddLocal(ltx)
-
- pending, queued := pool.Stats()
- if pending != 7 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
- }
- if queued != 3 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
- }
- if err := validateEvents(events, 7); err != nil {
- t.Fatalf("original event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Reprice the pool and check that underpriced transactions get dropped
- pool.SetGasPrice(big.NewInt(2))
-
- pending, queued = pool.Stats()
- if pending != 2 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
- }
- if queued != 5 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5)
- }
- if err := validateEvents(events, 0); err != nil {
- t.Fatalf("reprice event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Check that we can't add the old transactions back
- if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
- t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
- t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); err != ErrUnderpriced {
- t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
- }
- if err := validateEvents(events, 0); err != nil {
- t.Fatalf("post-reprice event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // However we can add local underpriced transactions
- tx := pricedTransaction(1, 100000, big.NewInt(1), keys[3])
- if err := pool.AddLocal(tx); err != nil {
- t.Fatalf("failed to add underpriced local transaction: %v", err)
- }
- if pending, _ = pool.Stats(); pending != 3 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
- }
- if err := validateEvents(events, 1); err != nil {
- t.Fatalf("post-reprice local event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // And we can fill gaps with properly priced transactions
- if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[0])); err != nil {
- t.Fatalf("failed to add pending transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), keys[1])); err != nil {
- t.Fatalf("failed to add pending transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), keys[2])); err != nil {
- t.Fatalf("failed to add queued transaction: %v", err)
- }
- if err := validateEvents(events, 5); err != nil {
- t.Fatalf("post-reprice event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
// Tests that setting the transaction pool gas price to a higher value does not
// remove local transactions.
func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
@@ -1313,260 +1091,6 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
validate()
}
-// Tests that when the pool reaches its global transaction limit, underpriced
-// transactions are gradually shifted out for more expensive ones and any gapped
-// pending transactions are moved into the queue.
-//
-// Note, local transactions are never allowed to be dropped.
-func TestTransactionPoolUnderpricing(t *testing.T) {
- t.Parallel()
-
- // Create the pool to test the pricing enforcement with
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
-
- config := testTxPoolConfig
- config.GlobalSlots = 2
- config.GlobalQueue = 2
-
- pool := NewTxPool(config, params.TestChainConfig, blockchain)
- defer pool.Stop()
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Create a number of test accounts and fund them
- keys := make([]*ecdsa.PrivateKey, 4)
- for i := 0; i < len(keys); i++ {
- keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
- }
- // Generate and queue a batch of transactions, both pending and queued
- txs := types.Transactions{}
-
- txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[0]))
- txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[0]))
-
- txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[1]))
-
- ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2])
-
- // Import the batch and that both pending and queued transactions match up
- pool.AddRemotes(txs)
- pool.AddLocal(ltx)
-
- pending, queued := pool.Stats()
- if pending != 3 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
- }
- if queued != 1 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
- }
- if err := validateEvents(events, 3); err != nil {
- t.Fatalf("original event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Ensure that adding an underpriced transaction on block limit fails
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
- t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
- }
- // Ensure that adding high priced transactions drops cheap ones, but not own
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil { // +K1:0 => -K1:1 => Pend K0:0, K0:1, K1:0, K2:0; Que -
- t.Fatalf("failed to add well priced transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(4), keys[1])); err != nil { // +K1:2 => -K0:0 => Pend K1:0, K2:0; Que K0:1 K1:2
- t.Fatalf("failed to add well priced transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(3, 100000, big.NewInt(5), keys[1])); err != nil { // +K1:3 => -K0:1 => Pend K1:0, K2:0; Que K1:2 K1:3
- t.Fatalf("failed to add well priced transaction: %v", err)
- }
- pending, queued = pool.Stats()
- if pending != 2 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
- }
- if queued != 2 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
- }
- if err := validateEvents(events, 1); err != nil {
- t.Fatalf("additional event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Ensure that adding local transactions can push out even higher priced ones
- ltx = pricedTransaction(1, 100000, big.NewInt(0), keys[2])
- if err := pool.AddLocal(ltx); err != nil {
- t.Fatalf("failed to append underpriced local transaction: %v", err)
- }
- ltx = pricedTransaction(0, 100000, big.NewInt(0), keys[3])
- if err := pool.AddLocal(ltx); err != nil {
- t.Fatalf("failed to add new underpriced local transaction: %v", err)
- }
- pending, queued = pool.Stats()
- if pending != 3 {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
- }
- if queued != 1 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
- }
- if err := validateEvents(events, 2); err != nil {
- t.Fatalf("local event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
-// Tests that more expensive transactions push out cheap ones from the pool, but
-// without producing instability by creating gaps that start jumping transactions
-// back and forth between queued/pending.
-func TestTransactionPoolStableUnderpricing(t *testing.T) {
- t.Parallel()
-
- // Create the pool to test the pricing enforcement with
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
-
- config := testTxPoolConfig
- config.GlobalSlots = 128
- config.GlobalQueue = 0
-
- pool := NewTxPool(config, params.TestChainConfig, blockchain)
- defer pool.Stop()
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Create a number of test accounts and fund them
- keys := make([]*ecdsa.PrivateKey, 2)
- for i := 0; i < len(keys); i++ {
- keys[i], _ = crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
- }
- // Fill up the entire queue with the same transaction price points
- txs := types.Transactions{}
- for i := uint64(0); i < config.GlobalSlots; i++ {
- txs = append(txs, pricedTransaction(i, 100000, big.NewInt(1), keys[0]))
- }
- pool.AddRemotes(txs)
-
- pending, queued := pool.Stats()
- if pending != int(config.GlobalSlots) {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots)
- }
- if queued != 0 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
- }
- if err := validateEvents(events, int(config.GlobalSlots)); err != nil {
- t.Fatalf("original event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
- // Ensure that adding high priced transactions drops a cheap, but doesn't produce a gap
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(3), keys[1])); err != nil {
- t.Fatalf("failed to add well priced transaction: %v", err)
- }
- pending, queued = pool.Stats()
- if pending != int(config.GlobalSlots) {
- t.Fatalf("pending transactions mismatched: have %d, want %d", pending, config.GlobalSlots)
- }
- if queued != 0 {
- t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
- }
- if err := validateEvents(events, 1); err != nil {
- t.Fatalf("additional event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
-// Tests that the pool rejects replacement transactions that don't meet the minimum
-// price bump required.
-func TestTransactionReplacement(t *testing.T) {
- t.Parallel()
-
- // Create the pool to test the pricing enforcement with
- statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
-
- pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
- defer pool.Stop()
-
- // Keep track of transaction events to ensure all executables get announced
- events := make(chan NewTxsEvent, 32)
- sub := pool.txFeed.Subscribe(events)
- defer sub.Unsubscribe()
-
- // Create a test account to add transactions with
- key, _ := crypto.GenerateKey()
- pool.currentState.AddBalance(crypto.PubkeyToAddress(key.PublicKey), big.NewInt(1000000000))
-
- // Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
- price := int64(100)
- threshold := (price * (100 + int64(testTxPoolConfig.PriceBump))) / 100
-
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), key)); err != nil {
- t.Fatalf("failed to add original cheap pending transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
- t.Fatalf("original cheap pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), key)); err != nil {
- t.Fatalf("failed to replace original cheap pending transaction: %v", err)
- }
- if err := validateEvents(events, 2); err != nil {
- t.Fatalf("cheap replacement event firing failed: %v", err)
- }
-
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(price), key)); err != nil {
- t.Fatalf("failed to add original proper pending transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
- t.Fatalf("original proper pending transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(threshold), key)); err != nil {
- t.Fatalf("failed to replace original proper pending transaction: %v", err)
- }
- if err := validateEvents(events, 2); err != nil {
- t.Fatalf("proper replacement event firing failed: %v", err)
- }
- // Add queued transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), key)); err != nil {
- t.Fatalf("failed to add original cheap queued transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(1), key)); err != ErrReplaceUnderpriced {
- t.Fatalf("original cheap queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), key)); err != nil {
- t.Fatalf("failed to replace original cheap queued transaction: %v", err)
- }
-
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(price), key)); err != nil {
- t.Fatalf("failed to add original proper queued transaction: %v", err)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100001, big.NewInt(threshold-1), key)); err != ErrReplaceUnderpriced {
- t.Fatalf("original proper queued transaction replacement error mismatch: have %v, want %v", err, ErrReplaceUnderpriced)
- }
- if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(threshold), key)); err != nil {
- t.Fatalf("failed to replace original proper queued transaction: %v", err)
- }
-
- if err := validateEvents(events, 0); err != nil {
- t.Fatalf("queued replacement event firing failed: %v", err)
- }
- if err := validateTxPoolInternals(pool); err != nil {
- t.Fatalf("pool internal state corrupted: %v", err)
- }
-}
-
// Tests that local transactions are journaled to disk, but remote transactions
// get discarded between restarts.
func TestTransactionJournaling(t *testing.T) { testTransactionJournaling(t, false) }
diff --git a/core/types.go b/core/types.go
index d0bbaf0aa..1c2b19577 100644
--- a/core/types.go
+++ b/core/types.go
@@ -18,8 +18,8 @@ package core
import (
"github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/simple-rules/harmony-benchmark/core/types"
)
// Validator is an interface which defines the standard for block validation. It
diff --git a/harmony/main.go b/harmony/main.go
new file mode 100644
index 000000000..9bde4815e
--- /dev/null
+++ b/harmony/main.go
@@ -0,0 +1,56 @@
+package harmony
+
+import (
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/simple-rules/harmony-benchmark/core"
+ "github.com/simple-rules/harmony-benchmark/core/types"
+ "math/big"
+)
+
+var (
+
+ // Test accounts
+ testBankKey, _ = crypto.GenerateKey()
+ testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
+ testBankFunds = big.NewInt(1000000000000000000)
+
+ testUserKey, _ = crypto.GenerateKey()
+ testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey)
+
+ chainConfig = params.TestChainConfig
+
+ // Test transactions
+ pendingTxs []*types.Transaction
+ newTxs []*types.Transaction
+)
+
+type testWorkerBackend struct {
+ db ethdb.Database
+ txPool *core.TxPool
+ chain *core.BlockChain
+}
+
+func main() {
+
+ var (
+ database = ethdb.NewMemDatabase()
+ gspec = core.Genesis{
+ Config: chainConfig,
+ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
+ }
+ )
+
+ chain, _ := core.NewBlockChain(database, nil, gspec.Config, nil, vm.Config{}, nil)
+
+ txpool := core.NewTxPool(core.DefaultTxPoolConfig, chainConfig, chain)
+
+ backend := &testWorkerBackend{
+ db: database,
+ chain: chain,
+ txPool: txpool,
+ }
+ backend.txPool.AddLocals(pendingTxs)
+}