Vdb 269- fetch logs by hash (#122)

* Upgrade geth from 1.8.15 to 1.8.18

* Update vat_tune to use shared repository methods

* Query blockchain by block hash instead of block number range

* Remove hash validation from repositories

* Fix vow flog integration test

* Update README Travis build sticker

* Update constants formatting per go fmt

* Update EthPublicKeyParser.ParsePublicKey to use discv5.PubkeyID method

* Address PR comments
This commit is contained in:
Elizabeth
2018-11-21 09:30:00 -06:00
committed by GitHub
parent b19e77e419
commit 128d20c9bf
1328 changed files with 25044 additions and 210977 deletions
+4 -4
View File
@@ -109,9 +109,9 @@ func PrintDisassembled(code string) error {
it := NewInstructionIterator(script)
for it.Next() {
if it.Arg() != nil && 0 < len(it.Arg()) {
fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
fmt.Printf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
} else {
fmt.Printf("%06v: %v\n", it.PC(), it.Op())
fmt.Printf("%05x: %v\n", it.PC(), it.Op())
}
}
return it.Error()
@@ -124,9 +124,9 @@ func Disassemble(script []byte) ([]string, error) {
it := NewInstructionIterator(script)
for it.Next() {
if it.Arg() != nil && 0 < len(it.Arg()) {
instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
instrs = append(instrs, fmt.Sprintf("%05x: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
} else {
instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op()))
instrs = append(instrs, fmt.Sprintf("%05x: %v\n", it.PC(), it.Op()))
}
}
if err := it.Error(); err != nil {
+2 -2
View File
@@ -175,7 +175,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
@@ -287,7 +287,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
if err != nil {
b.Fatalf("error creating chain: %v", err)
}
+6 -6
View File
@@ -53,12 +53,6 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor
}
return consensus.ErrPrunedAncestor
}
// 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 {
@@ -70,6 +64,12 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor
}
return consensus.ErrPrunedAncestor
}
return nil
}
+4 -4
View File
@@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header()
}
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
defer chain.Stop()
for i := 0; i < len(blocks); i++ {
@@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error
if valid {
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop()
} else {
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil)
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop()
}
@@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil)
defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
+68 -37
View File
@@ -29,6 +29,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"
@@ -43,7 +44,6 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
var (
@@ -55,6 +55,7 @@ var (
const (
bodyCacheLimit = 256
blockCacheLimit = 256
receiptsCacheLimit = 32
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
badBlockLimit = 10
@@ -111,11 +112,12 @@ type BlockChain struct {
currentBlock atomic.Value // Current head of the block chain
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing
stateCache state.Database // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
receiptsCache *lru.Cache // Cache for the most recent receipts per block
blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing
quit chan struct{} // blockchain quit channel
running int32 // running must be called atomically
@@ -128,39 +130,43 @@ type BlockChain struct {
validator Validator // block and state validator interface
vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache
badBlocks *lru.Cache // Bad block cache
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
}
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and
// Processor.
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieNodeLimit: 256 * 1024 * 1024,
TrieNodeLimit: 256,
TrieTimeLimit: 5 * time.Minute,
}
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
receiptsCache, _ := lru.New(receiptsCacheLimit)
blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks)
badBlocks, _ := lru.New(badBlockLimit)
bc := &BlockChain{
chainConfig: chainConfig,
cacheConfig: cacheConfig,
db: db,
triegc: prque.New(),
stateCache: state.NewDatabase(db),
quit: make(chan struct{}),
bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache,
blockCache: blockCache,
futureBlocks: futureBlocks,
engine: engine,
vmConfig: vmConfig,
badBlocks: badBlocks,
chainConfig: chainConfig,
cacheConfig: cacheConfig,
db: db,
triegc: prque.New(nil),
stateCache: state.NewDatabase(db),
quit: make(chan struct{}),
shouldPreserve: shouldPreserve,
bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache,
receiptsCache: receiptsCache,
blockCache: blockCache,
futureBlocks: futureBlocks,
engine: engine,
vmConfig: vmConfig,
badBlocks: badBlocks,
}
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
@@ -251,9 +257,9 @@ func (bc *BlockChain) loadLastState() error {
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(currentHeader.Time.Int64(), 0)))
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(currentBlock.Time().Int64(), 0)))
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(currentFastBlock.Time().Int64(), 0)))
return nil
}
@@ -278,6 +284,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Clear out any stale content from the caches
bc.bodyCache.Purge()
bc.bodyRLPCache.Purge()
bc.receiptsCache.Purge()
bc.blockCache.Purge()
bc.futureBlocks.Purge()
@@ -601,11 +608,18 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
if receipts, ok := bc.receiptsCache.Get(hash); ok {
return receipts.(types.Receipts)
}
number := rawdb.ReadHeaderNumber(bc.db, hash)
if number == nil {
return nil
}
return rawdb.ReadReceipts(bc.db, hash, *number)
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
bc.receiptsCache.Add(hash, receipts)
return receipts
}
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
@@ -850,13 +864,16 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
bc.mu.Unlock()
log.Info("Imported new block receipts",
"count", stats.processed,
"elapsed", common.PrettyDuration(time.Since(start)),
"number", head.Number(),
"hash", head.Hash(),
context := []interface{}{
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(head.Time().Int64(), 0)),
"size", common.StorageSize(bytes),
"ignored", stats.ignored)
}
if stats.ignored > 0 {
context = append(context, []interface{}{"ignored", stats.ignored}...)
}
log.Info("Imported new block receipts", context...)
return 0, nil
}
@@ -915,7 +932,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
} else {
// Full but not archive node, do proper garbage collection
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -float32(block.NumberU64()))
bc.triegc.Push(root, -int64(block.NumberU64()))
if current := block.NumberU64(); current > triesInMemory {
// If we exceeded our memory allowance, flush matured singleton nodes to disk
@@ -964,8 +981,17 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
reorg := externTd.Cmp(localTd) > 0
currentBlock = bc.CurrentBlock()
if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number, then at random
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
// Split same-difficulty blocks by number, then preferentially select
// the block generated by the local miner as the canonical block.
if block.NumberU64() < currentBlock.NumberU64() {
reorg = true
} else if block.NumberU64() == currentBlock.NumberU64() {
var currentPreserve, blockPreserve bool
if bc.shouldPreserve != nil {
currentPreserve, blockPreserve = bc.shouldPreserve(currentBlock), bc.shouldPreserve(block)
}
reorg = !currentPreserve && (blockPreserve || mrand.Float64() < 0.5)
}
}
if reorg {
// Reorganise the chain if the parent is not the head block
@@ -976,7 +1002,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
}
// Write the positional metadata for transaction/receipt lookups and preimages
rawdb.WriteTxLookupEntries(batch, block)
rawdb.WritePreimages(batch, block.NumberU64(), state.Preimages())
rawdb.WritePreimages(batch, state.Preimages())
status = CanonStatTy
} else {
@@ -1229,8 +1255,13 @@ func (st *insertStats) report(chain []*types.Block, index int, cache common.Stor
context := []interface{}{
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
"number", end.Number(), "hash", end.Hash(), "cache", cache,
"number", end.Number(), "hash", end.Hash(),
}
if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
context = append(context, []interface{}{"cache", cache}...)
if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...)
}
+16 -16
View File
@@ -52,7 +52,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
)
// Initialize a fresh chain with only a genesis block
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil)
// Create and inject the requested chain
if n == 0 {
return db, blockchain, nil
@@ -523,7 +523,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
blockchain.Stop()
// Create a new BlockChain and check that it rolled back the state.
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{})
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create new chain manager: %v", err)
}
@@ -635,7 +635,7 @@ func TestFastVsFullChains(t *testing.T) {
// Import the chain as an archive node for the comparison baseline
archiveDb := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil {
@@ -644,7 +644,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test
fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop()
headers := make([]*types.Header, len(blocks))
@@ -722,7 +722,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
}
@@ -735,7 +735,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated
fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop()
headers := make([]*types.Header, len(blocks))
@@ -756,7 +756,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb := ethdb.NewMemDatabase()
gspec.MustCommit(lightDb)
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
}
@@ -825,7 +825,7 @@ func TestChainTxReorgs(t *testing.T) {
}
})
// Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
}
@@ -896,7 +896,7 @@ func TestLogReorgs(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainID)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent)
@@ -943,7 +943,7 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainID)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop()
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
@@ -1072,7 +1072,7 @@ func TestEIP155Transition(t *testing.T) {
genesis = gspec.MustCommit(db)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
@@ -1179,7 +1179,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
}
genesis = gspec.MustCommit(db)
)
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
@@ -1254,7 +1254,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
@@ -1298,7 +1298,7 @@ func TestTrieForkGC(t *testing.T) {
diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
@@ -1337,7 +1337,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
@@ -1419,7 +1419,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
diskdb := ethdb.NewMemDatabase()
gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil {
b.Fatalf("failed to create tester chain: %v", err)
}
+12 -12
View File
@@ -53,14 +53,14 @@ type ChainIndexerChain interface {
// CurrentHeader retrieves the latest locally known header.
CurrentHeader() *types.Header
// SubscribeChainEvent subscribes to new head header notifications.
SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription
// SubscribeChainHeadEvent subscribes to new head header notifications.
SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription
}
// ChainIndexer does a post-processing job for equally sized sections of the
// canonical chain (like BlooomBits and CHT structures). A ChainIndexer is
// connected to the blockchain through the event system by starting a
// ChainEventLoop in a goroutine.
// ChainHeadEventLoop in a goroutine.
//
// Further child ChainIndexers can be added which use the output of the parent
// section indexer. These child indexers receive new head notifications only
@@ -142,8 +142,8 @@ func (c *ChainIndexer) AddCheckpoint(section uint64, shead common.Hash) {
// cascading background processing. Children do not need to be started, they
// are notified about new events by their parents.
func (c *ChainIndexer) Start(chain ChainIndexerChain) {
events := make(chan ChainEvent, 10)
sub := chain.SubscribeChainEvent(events)
events := make(chan ChainHeadEvent, 10)
sub := chain.SubscribeChainHeadEvent(events)
go c.eventLoop(chain.CurrentHeader(), events, sub)
}
@@ -190,7 +190,7 @@ func (c *ChainIndexer) Close() error {
// eventLoop is a secondary - optional - event loop of the indexer which is only
// started for the outermost indexer to push chain head events into a processing
// queue.
func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainEvent, sub event.Subscription) {
func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainHeadEvent, sub event.Subscription) {
// Mark the chain indexer as active, requiring an additional teardown
atomic.StoreUint32(&c.active, 1)
@@ -219,13 +219,13 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainE
}
header := ev.Block.Header()
if header.ParentHash != prevHash {
// Reorg to the common ancestor (might not exist in light sync mode, skip reorg then)
// Reorg to the common ancestor if needed (might not exist in light sync mode, skip reorg then)
// TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly?
// TODO(karalabe): This operation is expensive and might block, causing the event system to
// potentially also lock up. We need to do with on a different thread somehow.
if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
c.newHead(h.Number.Uint64(), true)
if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.Number.Uint64()) != prevHash {
if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
c.newHead(h.Number.Uint64(), true)
}
}
}
c.newHead(header.Number.Uint64(), false)
@@ -445,7 +445,7 @@ func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
func (c *ChainIndexer) loadValidSections() {
data, _ := c.indexDb.Get([]byte("count"))
if len(data) == 8 {
c.storedSections = binary.BigEndian.Uint64(data[:])
c.storedSections = binary.BigEndian.Uint64(data)
}
}
+36 -18
View File
@@ -33,12 +33,11 @@ import (
// BlockGen creates blocks for testing.
// See GenerateChain for a detailed explanation.
type BlockGen struct {
i int
parent *types.Block
chain []*types.Block
chainReader consensus.ChainReader
header *types.Header
statedb *state.StateDB
i int
parent *types.Block
chain []*types.Block
header *types.Header
statedb *state.StateDB
gasPool *GasPool
txs []*types.Transaction
@@ -67,6 +66,11 @@ func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data
}
// SetNonce sets the nonce field of the generated block.
func (b *BlockGen) SetNonce(nonce types.BlockNonce) {
b.header.Nonce = nonce
}
// AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
@@ -133,7 +137,7 @@ func (b *BlockGen) AddUncle(h *types.Header) {
// For index -1, PrevBlock returns the parent block given to GenerateChain.
func (b *BlockGen) PrevBlock(index int) *types.Block {
if index >= b.i {
panic("block index out of range")
panic(fmt.Errorf("block index %d out of range (%d,%d)", index, -1, b.i))
}
if index == -1 {
return b.parent
@@ -149,7 +153,8 @@ func (b *BlockGen) OffsetTime(seconds int64) {
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
panic("block time out of range")
}
b.header.Difficulty = b.engine.CalcDifficulty(b.chainReader, b.header.Time.Uint64(), b.parent.Header())
chainreader := &fakeChainReader{config: b.config}
b.header.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time.Uint64(), b.parent.Header())
}
// GenerateChain creates a chain of n blocks. The first block's
@@ -169,14 +174,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
config = params.TestChainConfig
}
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
chainreader := &fakeChainReader{config: config}
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
// TODO(karalabe): This is needed for clique, which depends on multiple blocks.
// It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
defer blockchain.Stop()
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
b.header = makeHeader(b.chainReader, parent, statedb, b.engine)
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine}
b.header = makeHeader(chainreader, parent, statedb, b.engine)
// Mutate the state and block according to any hard-fork specs
if daoBlock := config.DAOForkBlock; daoBlock != nil {
@@ -190,13 +191,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// Execute any user modifications to the block and finalize it
// Execute any user modifications to the block
if gen != nil {
gen(i, b)
}
if b.engine != nil {
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Finalize and seal the block
block, _ := b.engine.Finalize(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number))
if err != nil {
@@ -263,3 +265,19 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
})
return blocks
}
type fakeChainReader struct {
config *params.ChainConfig
genesis *types.Block
}
// Config returns the chain configuration.
func (cr *fakeChainReader) Config() *params.ChainConfig {
return cr.config
}
func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil }
func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
+1 -1
View File
@@ -79,7 +79,7 @@ func ExampleGenerateChain() {
})
// Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil {
+6 -6
View File
@@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer proBc.Stop()
conDb := ethdb.NewMemDatabase()
@@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil {
@@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a pro-fork block, and try to feed into the no-fork chain
db = ethdb.NewMemDatabase()
gspec.MustCommit(db)
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a no-fork block, and try to feed into the pro-fork chain
db = ethdb.NewMemDatabase()
gspec.MustCommit(db)
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db = ethdb.NewMemDatabase()
gspec.MustCommit(db)
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db = ethdb.NewMemDatabase()
gspec.MustCommit(db)
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
+1 -1
View File
@@ -355,7 +355,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
}
}
+1 -1
View File
@@ -120,7 +120,7 @@ func TestSetupGenesis(t *testing.T) {
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil)
defer bc.Stop()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)
+12 -2
View File
@@ -281,8 +281,18 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCa
}
// Report some public statistics so the user has a clue what's going on
last := chain[len(chain)-1]
log.Info("Imported new block headers", "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", last.Number, "hash", last.Hash(), "ignored", stats.ignored)
context := []interface{}{
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", last.Number, "hash", last.Hash(),
}
if timestamp := time.Unix(last.Time.Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
if stats.ignored > 0 {
context = append(context, []interface{}{"ignored", stats.ignored}...)
}
log.Info("Imported new block headers", context...)
return 0, nil
}
+1 -1
View File
@@ -278,7 +278,7 @@ func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Rece
if len(data) == 0 {
return nil
}
// Convert the revceipts from their storage form to their internal representation
// 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)
+2 -3
View File
@@ -77,9 +77,8 @@ func ReadPreimage(db DatabaseReader, hash common.Hash) []byte {
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) {
// WritePreimages writes the provided set of preimages to the database.
func WritePreimages(db DatabaseWriter, 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)
+1 -1
View File
@@ -35,7 +35,7 @@ var (
// 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 tracks the latest known incomplete block's hash during fast sync.
headFastBlockKey = []byte("LastFast")
// fastTrieProgressKey tracks the number of trie entries imported during fast sync.
+34 -10
View File
@@ -77,7 +77,7 @@ type stateObject struct {
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
cachedStorage Storage // Storage entry cache to avoid duplicate reads
originStorage Storage // Storage cache of original entries to dedup rewrites
dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags.
@@ -115,7 +115,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
cachedStorage: make(Storage),
originStorage: make(Storage),
dirtyStorage: make(Storage),
}
}
@@ -159,13 +159,25 @@ func (c *stateObject) getTrie(db Database) Trie {
return c.trie
}
// GetState returns a value in account storage.
// GetState retrieves a value from the account storage trie.
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key]
if exists {
// If we have a dirty value for this state entry, return it
value, dirty := self.dirtyStorage[key]
if dirty {
return value
}
// Load from DB in case it is missing.
// Otherwise return the entry's original value
return self.GetCommittedState(db, key)
}
// GetCommittedState retrieves a value from the committed account storage trie.
func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
// If we have the original value cached, return that
value, cached := self.originStorage[key]
if cached {
return value
}
// Otherwise load the value from the database
enc, err := self.getTrie(db).TryGet(key[:])
if err != nil {
self.setError(err)
@@ -178,22 +190,27 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
}
value.SetBytes(content)
}
self.cachedStorage[key] = value
self.originStorage[key] = value
return value
}
// SetState updates a value in account storage.
func (self *stateObject) SetState(db Database, key, value common.Hash) {
// If the new value is the same as old, don't set
prev := self.GetState(db, key)
if prev == value {
return
}
// New value is different, update and journal the change
self.db.journal.append(storageChange{
account: &self.address,
key: key,
prevalue: self.GetState(db, key),
prevalue: prev,
})
self.setState(key, value)
}
func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
}
@@ -202,6 +219,13 @@ func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db)
for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
// Skip noop changes, persist actual changes
if value == self.originStorage[key] {
continue
}
self.originStorage[key] = value
if (value == common.Hash{}) {
self.setError(tr.TryDelete(key[:]))
continue
@@ -279,7 +303,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject {
}
stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy()
stateObject.originStorage = self.originStorage.Copy()
stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted
+36 -22
View File
@@ -96,11 +96,15 @@ func (s *StateSuite) TestNull(c *checker.C) {
s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash
s.state.SetState(address, common.Hash{}, value)
s.state.Commit(false)
value = s.state.GetState(address, common.Hash{})
if value != (common.Hash{}) {
c.Errorf("expected empty hash. got %x", value)
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty current value, got %x", value)
}
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty committed value, got %x", value)
}
}
@@ -110,20 +114,24 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43})
// snapshot the genesis state
genesis := s.state.Snapshot()
// set initial state object value
s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state
snapshot := s.state.Snapshot()
// set new state object value
// set a new state object value, revert it and ensure correct content
s.state.SetState(stateobjaddr, storageaddr, data2)
// restore snapshot
s.state.RevertToSnapshot(snapshot)
// get state storage value
res := s.state.GetState(stateobjaddr, storageaddr)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(data1, checker.DeepEquals, res)
// revert up to the genesis state and ensure correct content
s.state.RevertToSnapshot(genesis)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
}
func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
@@ -208,24 +216,30 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
}
if len(so1.cachedStorage) != len(so0.cachedStorage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
if len(so1.dirtyStorage) != len(so0.dirtyStorage) {
t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage))
}
for k, v := range so1.cachedStorage {
if so0.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
for k, v := range so1.dirtyStorage {
if so0.dirtyStorage[k] != v {
t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v)
}
}
for k, v := range so0.cachedStorage {
if so1.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
for k, v := range so0.dirtyStorage {
if so1.dirtyStorage[k] != v {
t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v)
}
}
if so0.suicided != so1.suicided {
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
if len(so1.originStorage) != len(so0.originStorage) {
t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
}
if so0.deleted != so1.deleted {
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
for k, v := range so1.originStorage {
if so0.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v)
}
}
for k, v := range so0.originStorage {
if so1.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
}
}
}
+53 -17
View File
@@ -18,10 +18,10 @@
package state
import (
"errors"
"fmt"
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@@ -44,6 +44,13 @@ var (
emptyCode = crypto.Keccak256Hash(nil)
)
type proofList [][]byte
func (n *proofList) Put(key []byte, value []byte) error {
*n = append(*n, value)
return nil
}
// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
@@ -79,8 +86,6 @@ type StateDB struct {
journal *journal
validRevisions []revision
nextRevisionId int
lock sync.Mutex
}
// Create a new state from a given trie.
@@ -169,11 +174,22 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
return self.preimages
}
// AddRefund adds gas to the refund counter
func (self *StateDB) AddRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund})
self.refund += gas
}
// SubRefund removes gas from the refund counter.
// This method will panic if the refund counter goes below zero
func (self *StateDB) SubRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund})
if gas > self.refund {
panic("Refund counter below zero")
}
self.refund -= gas
}
// Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts.
func (self *StateDB) Exist(addr common.Address) bool {
@@ -236,10 +252,38 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.BytesToHash(stateObject.CodeHash())
}
func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash {
// GetState retrieves a value from the given account's storage trie.
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetState(self.db, bhash)
return stateObject.GetState(self.db, hash)
}
return common.Hash{}
}
// GetProof returns the MerkleProof for a given Account
func (self *StateDB) GetProof(a common.Address) ([][]byte, error) {
var proof proofList
err := self.trie.Prove(crypto.Keccak256(a.Bytes()), 0, &proof)
return [][]byte(proof), err
}
// GetProof returns the StorageProof for given key
func (self *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
var proof proofList
trie := self.StorageTrie(a)
if trie == nil {
return proof, errors.New("storage trie for requested address does not exist")
}
err := trie.Prove(crypto.Keccak256(key.Bytes()), 0, &proof)
return [][]byte(proof), err
}
// GetCommittedState retrieves a value from the given account's committed storage trie.
func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetCommittedState(self.db, hash)
}
return common.Hash{}
}
@@ -435,28 +479,20 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
if so == nil {
return
}
// When iterating over the storage check the cache first
for h, value := range so.cachedStorage {
cb(h, value)
}
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key))
if _, ok := so.cachedStorage[key]; !ok {
cb(key, common.BytesToHash(it.Value))
if value, dirty := so.dirtyStorage[key]; dirty {
cb(key, value)
continue
}
cb(key, common.BytesToHash(it.Value))
}
}
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
func (self *StateDB) Copy() *StateDB {
self.lock.Lock()
defer self.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &StateDB{
db: self.db,
+4 -4
View File
@@ -381,11 +381,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage.
if obj := state.getStateObject(addr); obj != nil {
state.ForEachStorage(addr, func(key, val common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
state.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
})
checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
})
}
if err != nil {
+1 -1
View File
@@ -110,7 +110,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
*usedGas += gas
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts.
// based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, failed, *usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = gas
+5 -5
View File
@@ -26,13 +26,13 @@ import (
"time"
"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"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
const (
@@ -525,7 +525,7 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
return pending, queued
}
// Pending retrieves all currently processable transactions, groupped by origin
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
@@ -547,7 +547,7 @@ func (pool *TxPool) Locals() []common.Address {
return pool.locals.flatten()
}
// local retrieves all currently known local transactions, groupped by origin
// local retrieves all currently known local transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
func (pool *TxPool) local() map[common.Address]types.Transactions {
@@ -987,11 +987,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
if pending > pool.config.GlobalSlots {
pendingBeforeCap := pending
// Assemble a spam order to penalize large transactors first
spammers := prque.New()
spammers := prque.New(nil)
for addr, list := range pool.pending {
// Only evict transactions from high rollers
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
spammers.Push(addr, float32(list.Len()))
spammers.Push(addr, int64(list.Len()))
}
}
// Gradually drop transactions from offenders
+2 -2
View File
@@ -113,7 +113,7 @@ func LogsBloom(logs []*Log) *big.Int {
}
func bloom9(b []byte) *big.Int {
b = crypto.Keccak256(b[:])
b = crypto.Keccak256(b)
r := new(big.Int)
@@ -130,7 +130,7 @@ var Bloom9 = bloom9
func BloomLookup(bin Bloom, topic bytesBacked) bool {
bloom := bin.Big()
cmp := bloom9(topic.Bytes()[:])
cmp := bloom9(topic.Bytes())
return bloom.And(bloom, cmp).Cmp(cmp) == 0
}
+1 -1
View File
@@ -47,7 +47,7 @@ type Log struct {
TxIndex uint `json:"transactionIndex" gencodec:"required"`
// hash of the block in which the transaction was included
BlockHash common.Hash `json:"blockHash"`
// index of the log in the receipt
// index of the log in the block
Index uint `json:"logIndex" gencodec:"required"`
// The Removed field is true if this log was reverted due to a chain reorganisation.
+14 -9
View File
@@ -153,16 +153,21 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if err := dec.UnmarshalJSON(input); err != nil {
return err
}
var V byte
if isProtectedV(dec.V) {
chainID := deriveChainId(dec.V).Uint64()
V = byte(dec.V.Uint64() - 35 - 2*chainID)
} else {
V = byte(dec.V.Uint64() - 27)
}
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
return ErrInvalidSig
withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0
if withSignature {
var V byte
if isProtectedV(dec.V) {
chainID := deriveChainId(dec.V).Uint64()
V = byte(dec.V.Uint64() - 35 - 2*chainID)
} else {
V = byte(dec.V.Uint64() - 27)
}
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
return ErrInvalidSig
}
}
*tx = Transaction{data: dec}
return nil
}
+3 -3
View File
@@ -136,7 +136,7 @@ func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) {
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true)
}
// WithSignature returns a new transaction with the given signature. This signature
// SignatureValues returns signature values. This signature
// needs to be in the [R || S || V] format where V is 0 or 1.
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig)
@@ -227,13 +227,13 @@ func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (commo
if !crypto.ValidateSignatureValues(V, R, S, homestead) {
return common.Address{}, ErrInvalidSig
}
// encode the snature in uncompressed format
// encode the signature in uncompressed format
r, s := R.Bytes(), S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = V
// recover the public key from the snature
// recover the public key from the signature
pub, err := crypto.Ecrecover(sighash[:], sig)
if err != nil {
return common.Address{}, err
+9 -3
View File
@@ -185,6 +185,7 @@ func TestTransactionJSON(t *testing.T) {
}
signer := NewEIP155Signer(common.Big1)
transactions := make([]*Transaction, 0, 50)
for i := uint64(0); i < 25; i++ {
var tx *Transaction
switch i % 2 {
@@ -193,20 +194,25 @@ func TestTransactionJSON(t *testing.T) {
case 1:
tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef"))
}
transactions = append(transactions, tx)
tx, err := SignTx(tx, signer, key)
signedTx, err := SignTx(tx, signer, key)
if err != nil {
t.Fatalf("could not sign transaction: %v", err)
}
transactions = append(transactions, signedTx)
}
for _, tx := range transactions {
data, err := json.Marshal(tx)
if err != nil {
t.Errorf("json.Marshal failed: %v", err)
t.Fatalf("json.Marshal failed: %v", err)
}
var parsedTx *Transaction
if err := json.Unmarshal(data, &parsedTx); err != nil {
t.Errorf("json.Unmarshal failed: %v", err)
t.Fatalf("json.Unmarshal failed: %v", err)
}
// compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
-28
View File
@@ -16,34 +16,6 @@
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
// destinations stores one map per contract (keyed by hash of code).
// The maps contain an entry for each location of a JUMPDEST
// instruction.
type destinations map[common.Hash]bitvec
// has checks whether code has a JUMPDEST at dest.
func (d destinations) has(codehash common.Hash, code []byte, dest *big.Int) bool {
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
udest := dest.Uint64()
if dest.BitLen() >= 63 || udest >= uint64(len(code)) {
return false
}
m, analysed := d[codehash]
if !analysed {
m = codeBitmap(code)
d[codehash] = m
}
return OpCode(code[udest]) == JUMPDEST && m.codeSegment(udest)
}
// bitvec is a bit vector which maps bytes in a program.
// An unset bit means the byte is an opcode, a set bit means
// it's data (i.e. argument of PUSHxx).
+23 -1
View File
@@ -16,7 +16,11 @@
package vm
import "testing"
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
)
func TestJumpDestAnalysis(t *testing.T) {
tests := []struct {
@@ -49,5 +53,23 @@ func TestJumpDestAnalysis(t *testing.T) {
t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
}
}
}
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
// 1.4 ms
code := make([]byte, 1200000)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
codeBitmap(code)
}
bench.StopTimer()
}
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
// 4 ms
code := make([]byte, 1200000)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
crypto.Keccak256Hash(code)
}
bench.StopTimer()
}
+45 -14
View File
@@ -49,7 +49,8 @@ type Contract struct {
caller ContractRef
self ContractRef
jumpdests destinations // result of JUMPDEST analysis.
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
analysis bitvec // Locally cached result of JUMPDEST analysis
Code []byte
CodeHash common.Hash
@@ -58,21 +59,17 @@ type Contract struct {
Gas uint64
value *big.Int
Args []byte
DelegateCall bool
}
// NewContract returns a new contract environment for the execution of EVM.
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
c.jumpdests = make(map[common.Hash]bitvec)
}
// Gas should be a pointer so it can safely be reduced through the run
@@ -84,10 +81,42 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
return c
}
func (c *Contract) validJumpdest(dest *big.Int) bool {
udest := dest.Uint64()
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
return false
}
// Only JUMPDESTs allowed for destinations
if OpCode(c.Code[udest]) != JUMPDEST {
return false
}
// Do we have a contract hash already?
if c.CodeHash != (common.Hash{}) {
// Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]
if !exist {
// Do the analysis and save in parent context
// We do not need to store it in c.analysis
analysis = codeBitmap(c.Code)
c.jumpdests[c.CodeHash] = analysis
}
return analysis.codeSegment(udest)
}
// We don't have the code hash, most likely a piece of initcode not already
// in state trie. In that case, we do an analysis, and save it locally, so
// we don't have to recalculate it for every JUMP instruction in the execution
// However, we don't save it within the parent context
if c.analysis == nil {
c.analysis = codeBitmap(c.Code)
}
return c.analysis.codeSegment(udest)
}
// AsDelegate sets the contract to be a delegate call and returns the current
// contract (for chaining calls)
func (c *Contract) AsDelegate() *Contract {
c.DelegateCall = true
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(*Contract)
@@ -138,12 +167,6 @@ func (c *Contract) Value() *big.Int {
return c.value
}
// SetCode sets the code to the contract
func (c *Contract) SetCode(hash common.Hash, code []byte) {
c.Code = code
c.CodeHash = hash
}
// SetCallCode sets the code of the contract and address of the backing data
// object
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
@@ -151,3 +174,11 @@ func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []by
c.CodeHash = hash
c.CodeAddr = addr
}
// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash.
// In case hash is not provided, the jumpdest analysis will not be saved to the parent context
func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) {
c.Code = codeAndHash.code
c.CodeHash = codeAndHash.hash
c.CodeAddr = addr
}
+47 -23
View File
@@ -41,7 +41,7 @@ type (
)
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
@@ -61,7 +61,7 @@ func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
}(evm.interpreter)
evm.interpreter = interpreter
}
return interpreter.Run(contract, input)
return interpreter.Run(contract, input, readOnly)
}
}
return nil, ErrNoCompatibleInterpreter
@@ -136,10 +136,28 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
interpreters: make([]Interpreter, 1),
interpreters: make([]Interpreter, 0, 1),
}
evm.interpreters[0] = NewEVMInterpreter(evm, vmConfig)
if chainConfig.IsEWASM(ctx.BlockNumber) {
// to be implemented by EVM-C and Wagon PRs.
// if vmConfig.EWASMInterpreter != "" {
// extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":")
// path := extIntOpts[0]
// options := []string{}
// if len(extIntOpts) > 1 {
// options = extIntOpts[1..]
// }
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
// } else {
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
// }
panic("No supported ewasm interpreter yet.")
}
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
// as we always want to have the built-in EVM as the failover option.
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreter = evm.interpreters[0]
return evm
@@ -194,12 +212,12 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.StateDB.CreateAccount(addr)
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// Even if the account has no code, we need to continue because it might be a precompile
start := time.Now()
// Capture the tracer start/end events in debug mode
@@ -210,7 +228,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}()
}
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
@@ -255,7 +273,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -288,7 +306,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, false)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -310,13 +328,6 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for
// child calls.
if !evm.interpreter.IsReadOnly() {
evm.interpreter.SetReadOnly(true)
defer func() { evm.interpreter.SetReadOnly(false) }()
}
var (
to = AccountRef(addr)
@@ -331,7 +342,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
ret, err = run(evm, contract, input)
ret, err = run(evm, contract, input, true)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
@@ -341,8 +352,20 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
return ret, contract.Gas, err
}
type codeAndHash struct {
code []byte
hash common.Hash
}
func (c *codeAndHash) Hash() common.Hash {
if c.hash == (common.Hash{}) {
c.hash = crypto.Keccak256Hash(c.code)
}
return c.hash
}
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@@ -371,18 +394,18 @@ func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.I
// EVM. The contract is a scoped environment for this execution context
// only.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCallCode(&address, crypto.Keccak256Hash(code), code)
contract.SetCodeOptionalHash(&address, codeAndHash)
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, address, gas, nil
}
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, code, gas, value)
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
}
start := time.Now()
ret, err := run(evm, contract, nil)
ret, err := run(evm, contract, nil, false)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
@@ -422,7 +445,7 @@ func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.I
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, code, gas, value, contractAddr)
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
}
// Create2 creates a new contract using code as deployment code.
@@ -430,8 +453,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), code)
return evm.create(caller, code, gas, endowment, contractAddr)
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
}
// ChainConfig returns the environment's chain configuration
+72 -16
View File
@@ -117,24 +117,69 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
)
// This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE)
if val == (common.Hash{}) && y.Sign() != 0 {
// 0 => non 0
return params.SstoreSetGas, nil
} else if val != (common.Hash{}) && y.Sign() == 0 {
// non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
} else {
// non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
// The legacy gas metering only takes into consideration the current state
if !evm.chainRules.IsConstantinople {
// This checks for 3 scenario's and calculates gas accordingly:
//
// 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return params.SstoreSetGas, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
}
}
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
// 2. If current value does not equal new value
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
// 2.1.1. If original value is 0, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.NetSstoreClearRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return params.NetSstoreDirtyGas, nil
}
func makeGasLog(n uint64) gasFunc {
@@ -302,6 +347,17 @@ func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(2))
if overflow {
return 0, errGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
+30 -22
View File
@@ -13,20 +13,22 @@ import (
var _ = (*structLogMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (s StructLog) MarshalJSON() ([]byte, error) {
type StructLog struct {
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
Err error `json:"-"`
OpName string `json:"opName"`
ErrorString string `json:"error"`
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
OpName string `json:"opName"`
ErrorString string `json:"error"`
}
var enc StructLog
enc.Pc = s.Pc
@@ -43,24 +45,27 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
}
enc.Storage = s.Storage
enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter
enc.Err = s.Err
enc.OpName = s.OpName()
enc.ErrorString = s.ErrorString()
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
Pc *uint64 `json:"pc"`
Op *OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
Err error `json:"-"`
Pc *uint64 `json:"pc"`
Op *OpCode `json:"op"`
Gas *math.HexOrDecimal64 `json:"gas"`
GasCost *math.HexOrDecimal64 `json:"gasCost"`
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"`
Err error `json:"-"`
}
var dec StructLog
if err := json.Unmarshal(input, &dec); err != nil {
@@ -96,6 +101,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
if dec.Depth != nil {
s.Depth = *dec.Depth
}
if dec.RefundCounter != nil {
s.RefundCounter = *dec.RefundCounter
}
if dec.Err != nil {
s.Err = dec.Err
}
+39 -15
View File
@@ -24,7 +24,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/params"
)
@@ -124,10 +124,22 @@ func opSmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
func opExp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
base, exponent := stack.pop(), stack.pop()
stack.push(math.Exp(base, exponent))
interpreter.intPool.put(base, exponent)
// some shortcuts
cmpToOne := exponent.Cmp(big1)
if cmpToOne < 0 { // Exponent is zero
// x ^ 0 == 1
stack.push(base.SetUint64(1))
} else if base.Sign() == 0 {
// 0 ^ y, if y != 0, == 0
stack.push(base.SetUint64(0))
} else if cmpToOne == 0 { // Exponent is one
// x ^ 1 == x
stack.push(base)
} else {
stack.push(math.Exp(base, exponent))
interpreter.intPool.put(base)
}
interpreter.intPool.put(exponent)
return nil, nil
}
@@ -355,7 +367,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
defer interpreter.intPool.put(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 {
if value.Sign() > 0 {
if value.Sign() >= 0 {
value.SetUint64(0)
} else {
value.SetInt64(-1)
@@ -373,13 +385,20 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
offset, size := stack.pop(), stack.pop()
data := memory.Get(offset.Int64(), size.Int64())
hash := crypto.Keccak256(data)
evm := interpreter.evm
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(common.BytesToHash(hash), data)
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewKeccak256().(keccakState)
} else {
interpreter.hasher.Reset()
}
stack.push(interpreter.intPool.get().SetBytes(hash))
interpreter.hasher.Write(data)
interpreter.hasher.Read(interpreter.hasherBuf[:])
evm := interpreter.evm
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
interpreter.intPool.put(offset, size)
return nil, nil
@@ -525,7 +544,12 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
// this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
slot := stack.peek()
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(common.BigToAddress(slot)).Bytes())
address := common.BigToAddress(slot)
if interpreter.evm.StateDB.Empty(address) {
slot.SetUint64(0)
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
return nil, nil
}
@@ -620,7 +644,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
pos := stack.pop()
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
if !contract.validJumpdest(pos) {
nop := contract.GetOp(pos.Uint64())
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
}
@@ -633,7 +657,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
pos, cond := stack.pop(), stack.pop()
if cond.Sign() != 0 {
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
if !contract.validJumpdest(pos) {
nop := contract.GetOp(pos.Uint64())
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
}
@@ -727,7 +751,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
}
func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// Pop gas. The actual gas in in interpreter.evm.callGasTemp.
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
interpreter.intPool.put(stack.pop())
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
+98
View File
@@ -17,10 +17,12 @@
package vm
import (
"bytes"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
@@ -489,3 +491,99 @@ func BenchmarkOpMstore(bench *testing.B) {
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpSHA3(bench *testing.B) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(32)
pc := uint64(0)
start := big.NewInt(0)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
stack.pushN(big.NewInt(32), start)
opSha3(&pc, evmInterpreter, nil, mem, stack)
}
poolOfIntPools.put(evmInterpreter.intPool)
}
func TestCreate2Addreses(t *testing.T) {
type testcase struct {
origin string
salt string
code string
expected string
}
for i, tt := range []testcase{
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x00",
expected: "0x4d1a2e2bb4f88f0250f26ffff098b0b30b26bf38",
},
{
origin: "0xdeadbeef00000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x00",
expected: "0xB928f69Bb1D91Cd65274e3c79d8986362984fDA3",
},
{
origin: "0xdeadbeef00000000000000000000000000000000",
salt: "0xfeed000000000000000000000000000000000000",
code: "0x00",
expected: "0xD04116cDd17beBE565EB2422F2497E06cC1C9833",
},
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0xdeadbeef",
expected: "0x70f2b2914A2a4b783FaEFb75f459A580616Fcb5e",
},
{
origin: "0x00000000000000000000000000000000deadbeef",
salt: "0xcafebabe",
code: "0xdeadbeef",
expected: "0x60f3f640a8508fC6a86d45DF051962668E1e8AC7",
},
{
origin: "0x00000000000000000000000000000000deadbeef",
salt: "0xcafebabe",
code: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
expected: "0x1d8bfDC5D46DC4f61D6b6115972536eBE6A8854C",
},
{
origin: "0x0000000000000000000000000000000000000000",
salt: "0x0000000000000000000000000000000000000000",
code: "0x",
expected: "0xE33C0C7F7df4809055C3ebA6c09CFe4BaF1BD9e0",
},
} {
origin := common.BytesToAddress(common.FromHex(tt.origin))
salt := common.BytesToHash(common.FromHex(tt.salt))
code := common.FromHex(tt.code)
codeHash := crypto.Keccak256(code)
address := crypto.CreateAddress2(origin, salt, codeHash)
/*
stack := newstack()
// salt, but we don't need that for this test
stack.push(big.NewInt(int64(len(code)))) //size
stack.push(big.NewInt(0)) // memstart
stack.push(big.NewInt(0)) // value
gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
*/
expected := common.BytesToAddress(common.FromHex(tt.expected))
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
}
}
}
+3 -1
View File
@@ -40,8 +40,10 @@ type StateDB interface {
GetCodeSize(common.Address) int
AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
@@ -64,7 +66,7 @@ type StateDB interface {
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool)
}
// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM
// CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface {
// Call another contract
+29 -17
View File
@@ -18,8 +18,10 @@ package vm
import (
"fmt"
"hash"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/params"
)
@@ -39,6 +41,11 @@ type Config struct {
// may be left uninitialised and will be set to the default
// table.
JumpTable [256]operation
// Type of the EWASM interpreter
EWASMInterpreter string
// Type of the EVM interpreter
EVMInterpreter string
}
// Interpreter is used to run Ethereum based contracts and will utilise the
@@ -48,7 +55,7 @@ type Config struct {
type Interpreter interface {
// Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred.
Run(contract *Contract, input []byte) ([]byte, error)
Run(contract *Contract, input []byte, static bool) ([]byte, error)
// CanRun tells if the contract, passed as an argument, can be
// run by the current interpreter. This is meant so that the
// caller can do something like:
@@ -61,10 +68,14 @@ type Interpreter interface {
// }
// ```
CanRun([]byte) bool
// IsReadOnly reports if the interpreter is in read only mode.
IsReadOnly() bool
// SetReadOnly sets (or unsets) read only mode in the interpreter.
SetReadOnly(bool)
}
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
type keccakState interface {
hash.Hash
Read([]byte) (int, error)
}
// EVMInterpreter represents an EVM interpreter
@@ -72,7 +83,11 @@ type EVMInterpreter struct {
evm *EVM
cfg Config
gasTable params.GasTable
intPool *intPool
intPool *intPool
hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
readOnly bool // Whether to throw on stateful modifications
returnData []byte // Last CALL's return data for subsequent reuse
@@ -125,7 +140,7 @@ func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, st
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for
// errExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if in.intPool == nil {
in.intPool = poolOfIntPools.get()
defer func() {
@@ -138,6 +153,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err
in.evm.depth++
defer func() { in.evm.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This makes also sure that the readOnly flag isn't removed for child calls.
if readOnly && !in.readOnly {
in.readOnly = true
defer func() { in.readOnly = false }()
}
// Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway.
in.returnData = nil
@@ -263,13 +285,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err
func (in *EVMInterpreter) CanRun(code []byte) bool {
return true
}
// IsReadOnly reports if the interpreter is in read only mode.
func (in *EVMInterpreter) IsReadOnly() bool {
return in.readOnly
}
// SetReadOnly sets (or unsets) read only mode in the interpreter.
func (in *EVMInterpreter) SetReadOnly(ro bool) {
in.readOnly = ro
}
+12 -11
View File
@@ -56,16 +56,17 @@ type LogConfig struct {
// StructLog is emitted to the EVM each cycle and lists information about the current internal state
// prior to the execution of the statement.
type StructLog struct {
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
Err error `json:"-"`
Pc uint64 `json:"pc"`
Op OpCode `json:"op"`
Gas uint64 `json:"gas"`
GasCost uint64 `json:"gasCost"`
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
Err error `json:"-"`
}
// overrides for gencodec
@@ -177,7 +178,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
storage = l.changedValues[contract.Address()].Copy()
}
// create a new snaptshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err}
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log)
return nil
+6 -6
View File
@@ -21,6 +21,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/params"
)
@@ -41,14 +42,15 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyStateDB struct {
NoopStateDB
ref *dummyContractRef
type dummyStatedb struct {
state.StateDB
}
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
@@ -56,9 +58,7 @@ func TestStoreCapture(t *testing.T) {
)
stack.push(big.NewInt(1))
stack.push(big.NewInt(0))
var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
if len(logger.changedValues[contract.Address()]) == 0 {
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
+1 -1
View File
@@ -29,7 +29,7 @@ type Memory struct {
lastGasCost uint64
}
// NewMemory returns a new memory memory model.
// NewMemory returns a new memory model.
func NewMemory() *Memory {
return &Memory{}
}
-70
View File
@@ -1,70 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
return true
}
func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
type NoopEVMCallContext struct{}
func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
return nil, common.Address{}, nil
}
func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
return nil, nil
}
type NoopStateDB struct{}
func (NoopStateDB) CreateAccount(common.Address) {}
func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
func (NoopStateDB) SetNonce(common.Address, uint64) {}
func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
func (NoopStateDB) SetCode(common.Address, []byte) {}
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
func (NoopStateDB) AddRefund(uint64) {}
func (NoopStateDB) GetRefund() uint64 { return 0 }
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
func (NoopStateDB) Suicide(common.Address) bool { return false }
func (NoopStateDB) HasSuicided(common.Address) bool { return false }
func (NoopStateDB) Exist(common.Address) bool { return false }
func (NoopStateDB) Empty(common.Address) bool { return false }
func (NoopStateDB) RevertToSnapshot(int) {}
func (NoopStateDB) Snapshot() int { return 0 }
func (NoopStateDB) AddLog(*types.Log) {}
func (NoopStateDB) AddPreimage(common.Hash, []byte) {}
func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {}
+55
View File
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
func TestDefaults(t *testing.T) {
@@ -148,3 +149,57 @@ func BenchmarkCall(b *testing.B) {
}
}
}
func benchmarkEVM_Create(bench *testing.B, code string) {
var (
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver"))
)
statedb.CreateAccount(sender)
statedb.SetCode(receiver, common.FromHex(code))
runtimeConfig := Config{
Origin: sender,
State: statedb,
GasLimit: 10000000,
Difficulty: big.NewInt(0x200000),
Time: new(big.Int).SetUint64(0),
Coinbase: common.Address{},
BlockNumber: new(big.Int).SetUint64(1),
ChainConfig: &params.ChainConfig{
ChainID: big.NewInt(1),
HomesteadBlock: new(big.Int),
ByzantiumBlock: new(big.Int),
ConstantinopleBlock: new(big.Int),
DAOForkBlock: new(big.Int),
DAOForkSupport: false,
EIP150Block: new(big.Int),
EIP155Block: new(big.Int),
EIP158Block: new(big.Int),
},
EVMConfig: vm.Config{},
}
// Warm up the intpools and stuff
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
Call(receiver, []byte{}, &runtimeConfig)
}
bench.StopTimer()
}
func BenchmarkEVM_CREATE_500(bench *testing.B) {
// initcode size 500K, repeatedly calls CREATE and then modifies the mem contents
benchmarkEVM_Create(bench, "5b6207a120600080f0600152600056")
}
func BenchmarkEVM_CREATE2_500(bench *testing.B) {
// initcode size 500K, repeatedly calls CREATE2 and then modifies the mem contents
benchmarkEVM_Create(bench, "5b586207a120600080f5600152600056")
}
func BenchmarkEVM_CREATE_1200(bench *testing.B) {
// initcode size 1200K, repeatedly calls CREATE and then modifies the mem contents
benchmarkEVM_Create(bench, "5b62124f80600080f0600152600056")
}
func BenchmarkEVM_CREATE2_1200(bench *testing.B) {
// initcode size 1200K, repeatedly calls CREATE2 and then modifies the mem contents
benchmarkEVM_Create(bench, "5b5862124f80600080f5600152600056")
}