aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/tx_pool.go255
-rw-r--r--core/types/block.go66
-rw-r--r--core/vm/errors.go49
-rw-r--r--core/vm/evm.go289
-rw-r--r--core/vm/instructions.go971
-rw-r--r--core/vm/interface.go2
-rw-r--r--core/vm/interpreter.go94
-rw-r--r--core/vm/jump_table.go181
-rw-r--r--core/vm/opcodes.go62
-rw-r--r--eth/api.go128
-rw-r--r--eth/api_backend.go107
-rw-r--r--eth/api_tracer.go90
-rw-r--r--eth/backend.go181
-rw-r--r--hacked-list.txt2
14 files changed, 1288 insertions, 1189 deletions
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 5b2a3c0..848627a 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -18,7 +18,6 @@ package core
import (
"errors"
- "fmt"
"math"
"math/big"
"sort"
@@ -28,26 +27,38 @@ import (
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/prque"
- "github.com/ava-labs/go-ethereum/event"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/prque"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
)
const (
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
chainHeadChanSize = 10
+
+ // txSlotSize is used to calculate how many data slots a single transaction
+ // takes up based on its size. The slots are used as DoS protection, ensuring
+ // that validating a new transaction remains a constant operation (in reality
+ // O(maxslots), where max slots are 4 currently).
+ txSlotSize = 32 * 1024
+
+ // txMaxSize is the maximum size a single transaction can have. This field has
+ // non-trivial consequences: larger transactions are significantly harder and
+ // more expensive to propagate; larger transactions also take more resources
+ // to validate whether they fit into the pool or not.
+ txMaxSize = 4 * txSlotSize // 128KB
)
var (
+ // ErrAlreadyKnown is returned if the transactions is already contained
+ // within the pool.
+ ErrAlreadyKnown = errors.New("already known")
+
// ErrInvalidSender is returned if the transaction contains an invalid signature.
ErrInvalidSender = errors.New("invalid sender")
- // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
- // one present in the local chain.
- ErrNonceTooLow = errors.New("nonce too low")
-
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
// configured for the transaction pool.
ErrUnderpriced = errors.New("transaction underpriced")
@@ -56,19 +67,11 @@ var (
// with a different one without the required price bump.
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
- // ErrInsufficientFunds is returned if the total cost of executing a transaction
- // is higher than the balance of the user's account.
- ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
-
- // ErrIntrinsicGas is returned if the transaction is specified to use less gas
- // than required to start the invocation.
- ErrIntrinsicGas = errors.New("intrinsic gas too low")
-
// ErrGasLimit is returned if a transaction's requested gas limit exceeds the
// maximum allowance of the current block.
ErrGasLimit = errors.New("exceeds block gas limit")
- // ErrNegativeValue is a sanity error to ensure noone is able to specify a
+ // ErrNegativeValue is a sanity error to ensure no one is able to specify a
// transaction with a negative value.
ErrNegativeValue = errors.New("negative value")
@@ -95,15 +98,18 @@ var (
queuedReplaceMeter = metrics.NewRegisteredMeter("txpool/queued/replace", nil)
queuedRateLimitMeter = metrics.NewRegisteredMeter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
+ queuedEvictionMeter = metrics.NewRegisteredMeter("txpool/queued/eviction", nil) // Dropped due to lifetime
// General tx metrics
- validMeter = metrics.NewRegisteredMeter("txpool/valid", nil)
+ knownTxMeter = metrics.NewRegisteredMeter("txpool/known", nil)
+ validTxMeter = metrics.NewRegisteredMeter("txpool/valid", nil)
invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil)
underpricedTxMeter = metrics.NewRegisteredMeter("txpool/underpriced", nil)
- pendingCounter = metrics.NewRegisteredCounter("txpool/pending", nil)
- queuedCounter = metrics.NewRegisteredCounter("txpool/queued", nil)
- localCounter = metrics.NewRegisteredCounter("txpool/local", nil)
+ pendingGauge = metrics.NewRegisteredGauge("txpool/pending", nil)
+ queuedGauge = metrics.NewRegisteredGauge("txpool/queued", nil)
+ localGauge = metrics.NewRegisteredGauge("txpool/local", nil)
+ slotsGauge = metrics.NewRegisteredGauge("txpool/slots", nil)
)
// TxStatus is the current status of a transaction as seen by the pool.
@@ -359,9 +365,11 @@ func (pool *TxPool) loop() {
}
// Any non-locals old enough should be removed
if time.Since(pool.beats[addr]) > pool.config.Lifetime {
- for _, tx := range pool.queue[addr].Flatten() {
+ list := pool.queue[addr].Flatten()
+ for _, tx := range list {
pool.removeTx(tx.Hash(), true)
}
+ queuedEvictionMeter.Mark(int64(len(list)))
}
}
pool.mu.Unlock()
@@ -517,8 +525,8 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
// validateTx checks whether a transaction is valid according to the consensus
// rules and adheres to some heuristic limits of the local node (price and size).
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
- // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
- if tx.Size() > 32*1024 {
+ // Reject transactions over defined size to prevent DOS attacks
+ if uint64(tx.Size()) > txMaxSize {
return ErrOversizedData
}
// Transactions can't be negative. This may never happen using RLP decoded
@@ -537,7 +545,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
}
// Drop non-local transactions under our own minimal accepted gas price
local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
- if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
+ if !local && tx.GasPriceIntCmp(pool.gasPrice) < 0 {
return ErrUnderpriced
}
// Ensure the transaction adheres to nonce ordering
@@ -572,16 +580,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
hash := tx.Hash()
if pool.all.Get(hash) != nil {
log.Trace("Discarding already known transaction", "hash", hash)
- return false, fmt.Errorf("known transaction: %x", hash)
+ knownTxMeter.Mark(1)
+ return false, ErrAlreadyKnown
}
-
// If the transaction fails basic validation, discard it
if err := pool.validateTx(tx, local); err != nil {
log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
invalidTxMeter.Mark(1)
return false, err
}
-
// If the transaction pool is full, discard underpriced transactions
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
// If the new transaction is underpriced, don't accept it
@@ -591,14 +598,13 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
return false, ErrUnderpriced
}
// New transaction is better than our worse ones, make room for it
- drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
+ drop := pool.priced.Discard(pool.all.Slots()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+numSlots(tx), pool.locals)
for _, tx := range drop {
log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
underpricedTxMeter.Mark(1)
pool.removeTx(tx.Hash(), false)
}
}
-
// Try to replace an existing transaction in the pending pool
from, _ := types.Sender(pool.signer, tx) // already validated
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
@@ -619,15 +625,16 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
pool.journalTx(from, tx)
pool.queueTxEvent(tx)
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
+
+ // Successful promotion, bump the heartbeat
+ pool.beats[from] = time.Now()
return old != nil, nil
}
-
// New transaction isn't replacing a pending one, push into queue
replaced, err = pool.enqueueTx(hash, tx)
if err != nil {
return false, err
}
-
// Mark local addresses and journal local transactions
if local {
if !pool.locals.contains(from) {
@@ -636,7 +643,7 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
}
}
if local || pool.locals.contains(from) {
- localCounter.Inc(1)
+ localGauge.Inc(1)
}
pool.journalTx(from, tx)
@@ -666,12 +673,16 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
queuedReplaceMeter.Mark(1)
} else {
// Nothing was replaced, bump the queued counter
- queuedCounter.Inc(1)
+ queuedGauge.Inc(1)
}
if pool.all.Get(hash) == nil {
pool.all.Add(tx)
pool.priced.Put(tx)
}
+ // If we never record the heartbeat, do it right now.
+ if _, exist := pool.beats[from]; !exist {
+ pool.beats[from] = time.Now()
+ }
return old != nil, nil
}
@@ -703,7 +714,6 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
// An older transaction was better, discard this
pool.all.Remove(hash)
pool.priced.Removed(1)
-
pendingDiscardMeter.Mark(1)
return false
}
@@ -711,11 +721,10 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
if old != nil {
pool.all.Remove(old.Hash())
pool.priced.Removed(1)
-
pendingReplaceMeter.Mark(1)
} else {
// Nothing was replaced, bump the pending counter
- pendingCounter.Inc(1)
+ pendingGauge.Inc(1)
}
// Failsafe to work around direct pending inserts (tests)
if pool.all.Get(hash) == nil {
@@ -723,9 +732,10 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
pool.priced.Put(tx)
}
// Set the potentially new pending nonce and notify any subsystems of the new tx
- pool.beats[addr] = time.Now()
pool.pendingNonces.set(addr, tx.Nonce()+1)
+ // Successful promotion, bump the heartbeat
+ pool.beats[addr] = time.Now()
return true
}
@@ -776,15 +786,47 @@ func (pool *TxPool) AddRemote(tx *types.Transaction) error {
// addTxs attempts to queue a batch of transactions if they are valid.
func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
- // Cache senders in transactions before obtaining lock (pool.signer is immutable)
- for _, tx := range txs {
- types.Sender(pool.signer, tx)
+ // Filter out known ones without obtaining the pool lock or recovering signatures
+ var (
+ errs = make([]error, len(txs))
+ news = make([]*types.Transaction, 0, len(txs))
+ )
+ for i, tx := range txs {
+ // If the transaction is known, pre-set the error slot
+ if pool.all.Get(tx.Hash()) != nil {
+ errs[i] = ErrAlreadyKnown
+ knownTxMeter.Mark(1)
+ continue
+ }
+ // Exclude transactions with invalid signatures as soon as
+ // possible and cache senders in transactions before
+ // obtaining lock
+ _, err := types.Sender(pool.signer, tx)
+ if err != nil {
+ errs[i] = ErrInvalidSender
+ invalidTxMeter.Mark(1)
+ continue
+ }
+ // Accumulate all unknown transactions for deeper processing
+ news = append(news, tx)
+ }
+ if len(news) == 0 {
+ return errs
}
+ // Process all the new transaction and merge any errors into the original slice
pool.mu.Lock()
- errs, dirtyAddrs := pool.addTxsLocked(txs, local)
+ newErrs, dirtyAddrs := pool.addTxsLocked(news, local)
pool.mu.Unlock()
+ var nilSlot = 0
+ for _, err := range newErrs {
+ for errs[nilSlot] != nil {
+ nilSlot++
+ }
+ errs[nilSlot] = err
+ }
+ // Reorg the pool internals if needed and return
done := pool.requestPromoteExecutables(dirtyAddrs)
if sync {
<-done
@@ -804,26 +846,29 @@ func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) ([]error,
dirty.addTx(tx)
}
}
- validMeter.Mark(int64(len(dirty.accounts)))
+ validTxMeter.Mark(int64(len(dirty.accounts)))
return errs, dirty
}
// Status returns the status (unknown/pending/queued) of a batch of transactions
// identified by their hashes.
func (pool *TxPool) Status(hashes []common.Hash) []TxStatus {
- pool.mu.RLock()
- defer pool.mu.RUnlock()
-
status := make([]TxStatus, len(hashes))
for i, hash := range hashes {
- if tx := pool.all.Get(hash); tx != nil {
- from, _ := types.Sender(pool.signer, tx) // already validated
- if pool.pending[from] != nil && pool.pending[from].txs.items[tx.Nonce()] != nil {
- status[i] = TxStatusPending
- } else {
- status[i] = TxStatusQueued
- }
+ tx := pool.Get(hash)
+ if tx == nil {
+ continue
+ }
+ from, _ := types.Sender(pool.signer, tx) // already validated
+ pool.mu.RLock()
+ if txList := pool.pending[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil {
+ status[i] = TxStatusPending
+ } else if txList := pool.queue[from]; txList != nil && txList.txs.items[tx.Nonce()] != nil {
+ status[i] = TxStatusQueued
}
+ // implicit else: the tx may have been included into a block between
+ // checking pool.Get and obtaining the lock. In that case, TxStatusUnknown is correct
+ pool.mu.RUnlock()
}
return status
}
@@ -833,6 +878,12 @@ func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
return pool.all.Get(hash)
}
+// Has returns an indicator whether txpool has a transaction cached with the
+// given hash.
+func (pool *TxPool) Has(hash common.Hash) bool {
+ return pool.all.Get(hash) != nil
+}
+
// removeTx removes a single transaction from the queue, moving all subsequent
// transactions back to the future queue.
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
@@ -849,7 +900,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
pool.priced.Removed(1)
}
if pool.locals.contains(addr) {
- localCounter.Dec(1)
+ localGauge.Dec(1)
}
// Remove the transaction from the pending lists and reset the account nonce
if pending := pool.pending[addr]; pending != nil {
@@ -857,7 +908,6 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
// If no more pending transactions are left, remove the list
if pending.Empty() {
delete(pool.pending, addr)
- delete(pool.beats, addr)
}
// Postpone any invalidated transactions
for _, tx := range invalids {
@@ -866,7 +916,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
// Update the account nonce if needed
pool.pendingNonces.setIfLower(addr, tx.Nonce())
// Reduce the pending counter
- pendingCounter.Dec(int64(1 + len(invalids)))
+ pendingGauge.Dec(int64(1 + len(invalids)))
return
}
}
@@ -874,10 +924,11 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
if future := pool.queue[addr]; future != nil {
if removed, _ := future.Remove(tx); removed {
// Reduce the queued counter
- queuedCounter.Dec(1)
+ queuedGauge.Dec(1)
}
if future.Empty() {
delete(pool.queue, addr)
+ delete(pool.beats, addr)
}
}
}
@@ -989,7 +1040,10 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
defer close(done)
var promoteAddrs []common.Address
- if dirtyAccounts != nil {
+ if dirtyAccounts != nil && reset == nil {
+ // Only dirty accounts need to be promoted, unless we're resetting.
+ // For resets, all addresses in the tx queue will be promoted and
+ // the flatten operation can be avoided.
promoteAddrs = dirtyAccounts.flatten()
}
pool.mu.Lock()
@@ -1005,20 +1059,14 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
}
}
// Reset needs promote for all addresses
- promoteAddrs = promoteAddrs[:0]
+ promoteAddrs = make([]common.Address, 0, len(pool.queue))
for addr := range pool.queue {
promoteAddrs = append(promoteAddrs, addr)
}
}
// Check for pending transactions for every account that sent new ones
promoted := pool.promoteExecutables(promoteAddrs)
- for _, tx := range promoted {
- addr, _ := types.Sender(pool.signer, tx)
- if _, ok := events[addr]; !ok {
- events[addr] = newTxSortedMap()
- }
- events[addr].Put(tx)
- }
+
// If a new block appeared, validate the pool of pending transactions. This will
// remove any transaction that has been included in the block or was invalidated
// because of another transaction (e.g. higher gas price).
@@ -1031,12 +1079,19 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
// Update all accounts to the latest known pending nonce
for addr, list := range pool.pending {
- txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
- pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1)
+ highestPending := list.LastElement()
+ pool.pendingNonces.set(addr, highestPending.Nonce()+1)
}
pool.mu.Unlock()
// Notify subsystems for newly added transactions
+ for _, tx := range promoted {
+ addr, _ := types.Sender(pool.signer, tx)
+ if _, ok := events[addr]; !ok {
+ events[addr] = newTxSortedMap()
+ }
+ events[addr].Put(tx)
+ }
if len(events) > 0 {
var txs []*types.Transaction
for _, set := range events {
@@ -1152,15 +1207,15 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range forwards {
hash := tx.Hash()
pool.all.Remove(hash)
- log.Trace("Removed old queued transaction", "hash", hash)
}
+ log.Trace("Removed old queued transactions", "count", len(forwards))
// Drop all transactions that are too costly (low balance or out of gas)
drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
for _, tx := range drops {
hash := tx.Hash()
pool.all.Remove(hash)
- log.Trace("Removed unpayable queued transaction", "hash", hash)
}
+ log.Trace("Removed unpayable queued transactions", "count", len(drops))
queuedNofundsMeter.Mark(int64(len(drops)))
// Gather all executable transactions and promote them
@@ -1168,11 +1223,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range readies {
hash := tx.Hash()
if pool.promoteTx(addr, hash, tx) {
- log.Trace("Promoting queued transaction", "hash", hash)
promoted = append(promoted, tx)
}
}
- queuedCounter.Dec(int64(len(readies)))
+ log.Trace("Promoted queued transactions", "count", len(promoted))
+ queuedGauge.Dec(int64(len(readies)))
// Drop all transactions over the allowed limit
var caps types.Transactions
@@ -1187,13 +1242,14 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
}
// Mark all the items dropped as removed
pool.priced.Removed(len(forwards) + len(drops) + len(caps))
- queuedCounter.Dec(int64(len(forwards) + len(drops) + len(caps)))
+ queuedGauge.Dec(int64(len(forwards) + len(drops) + len(caps)))
if pool.locals.contains(addr) {
- localCounter.Dec(int64(len(forwards) + len(drops) + len(caps)))
+ localGauge.Dec(int64(len(forwards) + len(drops) + len(caps)))
}
// Delete the entire queue entry if it became empty.
if list.Empty() {
delete(pool.queue, addr)
+ delete(pool.beats, addr)
}
}
return promoted
@@ -1248,9 +1304,9 @@ func (pool *TxPool) truncatePending() {
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
}
pool.priced.Removed(len(caps))
- pendingCounter.Dec(int64(len(caps)))
+ pendingGauge.Dec(int64(len(caps)))
if pool.locals.contains(offenders[i]) {
- localCounter.Dec(int64(len(caps)))
+ localGauge.Dec(int64(len(caps)))
}
pending--
}
@@ -1275,9 +1331,9 @@ func (pool *TxPool) truncatePending() {
log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
}
pool.priced.Removed(len(caps))
- pendingCounter.Dec(int64(len(caps)))
+ pendingGauge.Dec(int64(len(caps)))
if pool.locals.contains(addr) {
- localCounter.Dec(int64(len(caps)))
+ localGauge.Dec(int64(len(caps)))
}
pending--
}
@@ -1361,9 +1417,9 @@ func (pool *TxPool) demoteUnexecutables() {
log.Trace("Demoting pending transaction", "hash", hash)
pool.enqueueTx(hash, tx)
}
- pendingCounter.Dec(int64(len(olds) + len(drops) + len(invalids)))
+ pendingGauge.Dec(int64(len(olds) + len(drops) + len(invalids)))
if pool.locals.contains(addr) {
- localCounter.Dec(int64(len(olds) + len(drops) + len(invalids)))
+ localGauge.Dec(int64(len(olds) + len(drops) + len(invalids)))
}
// If there's a gap in front, alert (should never happen) and postpone all transactions
if list.Len() > 0 && list.txs.Get(nonce) == nil {
@@ -1373,12 +1429,13 @@ func (pool *TxPool) demoteUnexecutables() {
log.Error("Demoting invalidated transaction", "hash", hash)
pool.enqueueTx(hash, tx)
}
- pendingCounter.Dec(int64(len(gapped)))
+ pendingGauge.Dec(int64(len(gapped)))
+ // This might happen in a reorg, so log it to the metering
+ blockReorgInvalidatedTx.Mark(int64(len(gapped)))
}
- // Delete the entire queue entry if it became empty.
+ // Delete the entire pending entry if it became empty.
if list.Empty() {
delete(pool.pending, addr)
- delete(pool.beats, addr)
}
}
}
@@ -1422,6 +1479,10 @@ func (as *accountSet) contains(addr common.Address) bool {
return exist
}
+func (as *accountSet) empty() bool {
+ return len(as.accounts) == 0
+}
+
// containsTx checks if the sender of a given tx is within the set. If the sender
// cannot be derived, this method returns false.
func (as *accountSet) containsTx(tx *types.Transaction) bool {
@@ -1475,8 +1536,9 @@ func (as *accountSet) merge(other *accountSet) {
// peeking into the pool in TxPool.Get without having to acquire the widely scoped
// TxPool.mu mutex.
type txLookup struct {
- all map[common.Hash]*types.Transaction
- lock sync.RWMutex
+ all map[common.Hash]*types.Transaction
+ slots int
+ lock sync.RWMutex
}
// newTxLookup returns a new txLookup structure.
@@ -1514,11 +1576,22 @@ func (t *txLookup) Count() int {
return len(t.all)
}
+// Slots returns the current number of slots used in the lookup.
+func (t *txLookup) Slots() int {
+ t.lock.RLock()
+ defer t.lock.RUnlock()
+
+ return t.slots
+}
+
// Add adds a transaction to the lookup.
func (t *txLookup) Add(tx *types.Transaction) {
t.lock.Lock()
defer t.lock.Unlock()
+ t.slots += numSlots(tx)
+ slotsGauge.Update(int64(t.slots))
+
t.all[tx.Hash()] = tx
}
@@ -1527,5 +1600,13 @@ func (t *txLookup) Remove(hash common.Hash) {
t.lock.Lock()
defer t.lock.Unlock()
+ t.slots -= numSlots(t.all[hash])
+ slotsGauge.Update(int64(t.slots))
+
delete(t.all, hash)
}
+
+// numSlots calculates the number of slots needed for a single transaction.
+func numSlots(tx *types.Transaction) int {
+ return int((tx.Size() + txSlotSize - 1) / txSlotSize)
+}
diff --git a/core/types/block.go b/core/types/block.go
index 4096d86..37f3464 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -24,18 +24,19 @@ import (
"io"
"math/big"
"reflect"
- "sort"
+ "sync"
"sync/atomic"
"time"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
var (
- EmptyRootHash = DeriveSha(Transactions{})
+ EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
EmptyUncleHash = rlpHash([]*Header(nil))
)
@@ -132,13 +133,33 @@ func (h *Header) SanityCheck() error {
return nil
}
+// hasherPool holds LegacyKeccak hashers.
+var hasherPool = sync.Pool{
+ New: func() interface{} {
+ return sha3.NewLegacyKeccak256()
+ },
+}
+
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewLegacyKeccak256()
- rlp.Encode(hw, x)
- hw.Sum(h[:0])
+ sha := hasherPool.Get().(crypto.KeccakState)
+ defer hasherPool.Put(sha)
+ sha.Reset()
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
return h
}
+// EmptyBody returns true if there is no additional 'body' to complete the header
+// that is: no transactions and no uncles.
+func (h *Header) EmptyBody() bool {
+ return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash
+}
+
+// EmptyReceipts returns true if there are no receipts for this header/block.
+func (h *Header) EmptyReceipts() bool {
+ return h.ReceiptHash == EmptyRootHash
+}
+
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
@@ -214,14 +235,14 @@ type storageblock struct {
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
-func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, extdata []byte) *Block {
+func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher Hasher, extdata []byte) *Block {
b := &Block{header: CopyHeader(header), td: new(big.Int)}
// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
b.header.TxHash = EmptyRootHash
} else {
- b.header.TxHash = DeriveSha(Transactions(txs))
+ b.header.TxHash = DeriveSha(Transactions(txs), hasher)
b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs)
}
@@ -229,7 +250,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
if len(receipts) == 0 {
b.header.ReceiptHash = EmptyRootHash
} else {
- b.header.ReceiptHash = DeriveSha(Receipts(receipts))
+ b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts)
}
@@ -467,26 +488,3 @@ func (b *Block) Hash() common.Hash {
}
type Blocks []*Block
-
-type BlockBy func(b1, b2 *Block) bool
-
-func (self BlockBy) Sort(blocks Blocks) {
- bs := blockSorter{
- blocks: blocks,
- by: self,
- }
- sort.Sort(bs)
-}
-
-type blockSorter struct {
- blocks Blocks
- by func(b1, b2 *Block) bool
-}
-
-func (self blockSorter) Len() int { return len(self.blocks) }
-func (self blockSorter) Swap(i, j int) {
- self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
-}
-func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
-
-func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 94f0ed8..9f3f335 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -16,16 +16,57 @@
package vm
-import "errors"
+import (
+ "errors"
+ "fmt"
+)
-// List execution errors
+// List evm execution errors
var (
+ // ErrInvalidSubroutineEntry means that a BEGINSUB was reached via iteration,
+ // as opposed to from a JUMPSUB instruction
+ ErrInvalidSubroutineEntry = errors.New("invalid subroutine entry")
ErrOutOfGas = errors.New("out of gas")
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
ErrDepth = errors.New("max call depth exceeded")
- ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
ErrIncompatibleAccount = errors.New("incompatible account")
ErrContractAddressCollision = errors.New("contract address collision")
- ErrNoCompatibleInterpreter = errors.New("no compatible interpreter")
+ ErrExecutionReverted = errors.New("execution reverted")
+ ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
+ ErrInvalidJump = errors.New("invalid jump destination")
+ ErrWriteProtection = errors.New("write protection")
+ ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
+ ErrGasUintOverflow = errors.New("gas uint64 overflow")
+ ErrInvalidRetsub = errors.New("invalid retsub")
+ ErrReturnStackExceeded = errors.New("return stack limit reached")
)
+
+// ErrStackUnderflow wraps an evm error when the items on the stack less
+// than the minimal requirement.
+type ErrStackUnderflow struct {
+ stackLen int
+ required int
+}
+
+func (e *ErrStackUnderflow) Error() string {
+ return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required)
+}
+
+// ErrStackOverflow wraps an evm error when the items on the stack exceeds
+// the maximum allowance.
+type ErrStackOverflow struct {
+ stackLen int
+ limit int
+}
+
+func (e *ErrStackOverflow) Error() string {
+ return fmt.Sprintf("stack limit reached %d (%d)", e.stackLen, e.limit)
+}
+
+// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
+type ErrInvalidOpCode struct {
+ opcode OpCode
+}
+
+func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
diff --git a/core/vm/evm.go b/core/vm/evm.go
index be8b240..85b7ba7 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -17,13 +17,15 @@
package vm
import (
+ "errors"
"math/big"
"sync/atomic"
"time"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/holiman/uint256"
)
// emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -42,20 +44,24 @@ type (
GetHashFunc func(uint64) common.Hash
)
+func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
+ var precompiles map[common.Address]PrecompiledContract
+ switch {
+ case evm.chainRules.IsYoloV1:
+ precompiles = PrecompiledContractsYoloV1
+ case evm.chainRules.IsIstanbul:
+ precompiles = PrecompiledContractsIstanbul
+ case evm.chainRules.IsByzantium:
+ precompiles = PrecompiledContractsByzantium
+ default:
+ precompiles = PrecompiledContractsHomestead
+ }
+ p, ok := precompiles[addr]
+ return p, ok
+}
+
// 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, readOnly bool) ([]byte, error) {
- if contract.CodeAddr != nil {
- precompiles := PrecompiledContractsHomestead
- if evm.chainRules.IsByzantium {
- precompiles = PrecompiledContractsByzantium
- }
- if evm.chainRules.IsIstanbul {
- precompiles = PrecompiledContractsIstanbul
- }
- if p := precompiles[*contract.CodeAddr]; p != nil {
- return RunPrecompiledContract(p, input, contract)
- }
- }
for _, interpreter := range evm.interpreters {
if interpreter.CanRun(contract.Code) {
if evm.interpreter != interpreter {
@@ -69,7 +75,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
return interpreter.Run(contract, input, readOnly)
}
}
- return nil, ErrNoCompatibleInterpreter
+ return nil, errors.New("no compatible interpreter")
}
// Context provides the EVM with auxiliary information. Once provided
@@ -194,29 +200,19 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
-
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
- if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
+ snapshot := evm.StateDB.Snapshot()
+ p, isPrecompile := evm.precompile(addr)
- var (
- to = AccountRef(addr)
- snapshot = evm.StateDB.Snapshot()
- )
if !evm.StateDB.Exist(addr) {
- precompiles := PrecompiledContractsHomestead
- if evm.chainRules.IsByzantium {
- precompiles = PrecompiledContractsByzantium
- }
- if evm.chainRules.IsIstanbul {
- precompiles = PrecompiledContractsIstanbul
- }
- if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
@@ -226,35 +222,47 @@ 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()
+ evm.Transfer(evm.StateDB, caller.Address(), addr, value)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
-
- defer func() { // Lazy evaluation of the parameters
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
- }()
+ defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
+ evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
+ }(gas, time.Now())
+ }
+
+ if isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ // 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.
+ code := evm.StateDB.GetCode(addr)
+ if len(code) == 0 {
+ ret, err = nil, nil // gas is unchanged
+ } else {
+ addrCopy := addr
+ // If the account has no code, we can abort here
+ // The depth-check is already done, and precompiles handled above
+ contract := NewContract(caller, AccountRef(addrCopy), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
+ ret, err = run(evm, contract, input, false)
+ gas = contract.Gas
+ }
}
- 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
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
- contract.UseGas(contract.Gas)
+ if err != ErrExecutionReverted {
+ gas = 0
}
+ // TODO: consider clearing up unused snapshots:
+ //} else {
+ // evm.StateDB.DiscardSnapshot(snapshot)
}
- return ret, contract.Gas, err
+ return ret, gas, err
}
// This allows the user transfer balance of a specified coinId in addition to a normal Call().
@@ -262,35 +270,27 @@ func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
-
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
- // Fail if we're trying to transfer more than the available balance
- if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
- return nil, gas, ErrInsufficientBalance
- }
- var to = AccountRef(addr)
- mcerr := evm.Context.CanTransferMC(evm.StateDB, caller.Address(), to.Address(), coinID, value2)
+ mcerr := evm.Context.CanTransferMC(evm.StateDB, caller.Address(), addr, coinID, value2)
if mcerr == 1 {
return nil, gas, ErrInsufficientBalance
} else if mcerr != 0 {
return nil, gas, ErrIncompatibleAccount
}
- var snapshot = evm.StateDB.Snapshot()
+ // Fail if we're trying to transfer more than the available balance
+ if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ return nil, gas, ErrInsufficientBalance
+ }
+ snapshot := evm.StateDB.Snapshot()
+ p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) {
- precompiles := PrecompiledContractsHomestead
- if evm.chainRules.IsByzantium {
- precompiles = PrecompiledContractsByzantium
- }
- if evm.chainRules.IsIstanbul {
- precompiles = PrecompiledContractsIstanbul
- }
- if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
@@ -300,36 +300,48 @@ func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte
}
evm.StateDB.CreateAccount(addr)
}
- evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
- evm.TransferMultiCoin(evm.StateDB, caller.Address(), to.Address(), coinID, value2)
- // 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()
+ evm.Transfer(evm.StateDB, caller.Address(), addr, value)
+ evm.TransferMultiCoin(evm.StateDB, caller.Address(), addr, coinID, value2)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
-
- defer func() { // Lazy evaluation of the parameters
- evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
- }()
+ defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
+ evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
+ }(gas, time.Now())
+ }
+
+ if isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ // 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.
+ code := evm.StateDB.GetCode(addr)
+ if len(code) == 0 {
+ ret, err = nil, nil // gas is unchanged
+ } else {
+ addrCopy := addr
+ // If the account has no code, we can abort here
+ // The depth-check is already done, and precompiles handled above
+ contract := NewContract(caller, AccountRef(addrCopy), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
+ ret, err = run(evm, contract, input, false)
+ gas = contract.Gas
+ }
}
- 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
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
- contract.UseGas(contract.Gas)
+ if err != ErrExecutionReverted {
+ gas = 0
}
+ // TODO: consider clearing up unused snapshots:
+ //} else {
+ // evm.StateDB.DiscardSnapshot(snapshot)
}
- return ret, contract.Gas, err
+ return ret, gas, err
}
// CallCode executes the contract associated with the addr with the given input
@@ -343,33 +355,38 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
-
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
- if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ // Note although it's noop to transfer X ether to caller itself. But
+ // if caller doesn't have enough balance, it would be an error to allow
+ // over-charging itself. So the check here is necessary.
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
+ var snapshot = evm.StateDB.Snapshot()
- var (
- snapshot = evm.StateDB.Snapshot()
- to = AccountRef(caller.Address())
- )
- // 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))
-
- ret, err = run(evm, contract, input, false)
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ addrCopy := addr
+ // 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, AccountRef(caller.Address()), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = run(evm, contract, input, false)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
- contract.UseGas(contract.Gas)
+ if err != ErrExecutionReverted {
+ gas = 0
}
}
- return ret, contract.Gas, err
+ return ret, gas, err
}
// DelegateCall executes the contract associated with the addr with the given input
@@ -385,24 +402,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
+ var snapshot = evm.StateDB.Snapshot()
- var (
- snapshot = evm.StateDB.Snapshot()
- to = AccountRef(caller.Address())
- )
-
- // Initialise a new contract and make initialise the delegate values
- contract := NewContract(caller, to, nil, gas).AsDelegate()
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
- ret, err = run(evm, contract, input, false)
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ addrCopy := addr
+ // Initialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = run(evm, contract, input, false)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
- contract.UseGas(contract.Gas)
+ if err != ErrExecutionReverted {
+ gas = 0
}
}
- return ret, contract.Gas, err
+ return ret, gas, err
}
// StaticCall executes the contract associated with the addr with the given input
@@ -417,33 +436,43 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
-
- var (
- to = AccountRef(addr)
- snapshot = evm.StateDB.Snapshot()
- )
- // 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, new(big.Int), gas)
- contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+ // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
+ // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
+ // after all empty accounts were deleted, so this is not required. However, if we omit this,
+ // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
+ // We could change this, but for now it's left for legacy reasons
+ var snapshot = evm.StateDB.Snapshot()
// We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
- evm.StateDB.AddBalance(addr, bigZero)
-
- // 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, true)
+ evm.StateDB.AddBalance(addr, big0)
+
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ // At this point, we use a copy of address. If we don't, the go compiler will
+ // leak the 'contract' to the outer scope, and make allocation for 'contract'
+ // even if the actual execution ends on RunPrecompiled above.
+ addrCopy := addr
+ // 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, AccountRef(addrCopy), new(big.Int), gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ // 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, true)
+ gas = contract.Gas
+ }
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
- contract.UseGas(contract.Gas)
+ if err != ErrExecutionReverted {
+ gas = 0
}
}
- return ret, contract.Gas, err
+ return ret, gas, err
}
type codeAndHash struct {
@@ -520,13 +549,13 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// when we're in homestead this also counts for code storage gas errors.
if maxCodeSizeExceeded || (err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
evm.StateDB.RevertToSnapshot(snapshot)
- if err != errExecutionReverted {
+ if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
// Assign err if contract code size exceeds the max while the err is still empty.
if maxCodeSizeExceeded && err == nil {
- err = errMaxCodeSizeExceeded
+ err = ErrMaxCodeSizeExceeded
}
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
@@ -545,9 +574,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) {
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
- contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
+ contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index ecdccbd..35ce39f 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,374 +17,223 @@
package vm
import (
- "errors"
- "math/big"
-
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
-var (
- bigZero = new(big.Int)
- tt255 = math.BigPow(2, 255)
- errWriteProtection = errors.New("evm: write protection")
- errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
- errExecutionReverted = errors.New("evm: execution reverted")
- errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
- errInvalidJump = errors.New("evm: invalid jump destination")
-)
-
-func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- math.U256(y.Add(x, y))
-
- interpreter.intPool.put(x)
+func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Add(&x, y)
return nil, nil
}
-func opSub(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- math.U256(y.Sub(x, y))
-
- interpreter.intPool.put(x)
+func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Sub(&x, y)
return nil, nil
}
-func opMul(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- stack.push(math.U256(x.Mul(x, y)))
-
- interpreter.intPool.put(y)
-
+func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mul(&x, y)
return nil, nil
}
-func opDiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if y.Sign() != 0 {
- math.U256(y.Div(x, y))
- } else {
- y.SetUint64(0)
- }
- interpreter.intPool.put(x)
+func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Div(&x, y)
return nil, nil
}
-func opSdiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := math.S256(stack.pop()), math.S256(stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 || x.Sign() == 0 {
- stack.push(res)
- } else {
- if x.Sign() != y.Sign() {
- res.Div(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Div(x.Abs(x), y.Abs(y))
- }
- stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SDiv(&x, y)
return nil, nil
}
-func opMod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- if y.Sign() == 0 {
- stack.push(x.SetUint64(0))
- } else {
- stack.push(math.U256(x.Mod(x, y)))
- }
- interpreter.intPool.put(y)
+func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mod(&x, y)
return nil, nil
}
-func opSmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := math.S256(stack.pop()), math.S256(stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 {
- stack.push(res)
- } else {
- if x.Sign() < 0 {
- res.Mod(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Mod(x.Abs(x), y.Abs(y))
- }
- stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SMod(&x, y)
return nil, nil
}
-func opExp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- base, exponent := stack.pop(), stack.pop()
- // 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)
+func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ base, exponent := callContext.stack.pop(), callContext.stack.peek()
+ exponent.Exp(&base, exponent)
return nil, nil
}
-func opSignExtend(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- back := stack.pop()
- if back.Cmp(big.NewInt(31)) < 0 {
- bit := uint(back.Uint64()*8 + 7)
- num := stack.pop()
- mask := back.Lsh(common.Big1, bit)
- mask.Sub(mask, common.Big1)
- if num.Bit(int(bit)) > 0 {
- num.Or(num, mask.Not(mask))
- } else {
- num.And(num, mask)
- }
-
- stack.push(math.U256(num))
- }
-
- interpreter.intPool.put(back)
+func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ back, num := callContext.stack.pop(), callContext.stack.peek()
+ num.ExtendSign(num, &back)
return nil, nil
}
-func opNot(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x := stack.peek()
- math.U256(x.Not(x))
+func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ x.Not(x)
return nil, nil
}
-func opLt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
+func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Lt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opGt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
+func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Gt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opSlt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(1)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(0)
-
- default:
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Slt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opSgt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(0)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(1)
-
- default:
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Sgt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opEq(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) == 0 {
- y.SetUint64(1)
+func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Eq(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opIszero(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x := stack.peek()
- if x.Sign() > 0 {
- x.SetUint64(0)
+func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ if x.IsZero() {
+ x.SetOne()
} else {
- x.SetUint64(1)
+ x.Clear()
}
return nil, nil
}
-func opAnd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- stack.push(x.And(x, y))
-
- interpreter.intPool.put(y)
+func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.And(&x, y)
return nil, nil
}
-func opOr(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- y.Or(x, y)
-
- interpreter.intPool.put(x)
+func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Or(&x, y)
return nil, nil
}
-func opXor(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- y.Xor(x, y)
-
- interpreter.intPool.put(x)
+func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Xor(&x, y)
return nil, nil
}
-func opByte(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- th, val := stack.pop(), stack.peek()
- if th.Cmp(common.Big32) < 0 {
- b := math.Byte(val, 32, int(th.Int64()))
- val.SetUint64(uint64(b))
- } else {
- val.SetUint64(0)
- }
- interpreter.intPool.put(th)
+func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ th, val := callContext.stack.pop(), callContext.stack.peek()
+ val.Byte(&th)
return nil, nil
}
-func opAddmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Add(x, y)
- x.Mod(x, z)
- stack.push(math.U256(x))
+func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ if z.IsZero() {
+ z.Clear()
} else {
- stack.push(x.SetUint64(0))
+ z.AddMod(&x, &y, z)
}
- interpreter.intPool.put(y, z)
return nil, nil
}
-func opMulmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Mul(x, y)
- x.Mod(x, z)
- stack.push(math.U256(x))
- } else {
- stack.push(x.SetUint64(0))
- }
- interpreter.intPool.put(y, z)
+func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ z.MulMod(&x, &y, z)
return nil, nil
}
// opSHL implements Shift Left
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
-func opSHL(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(stack.pop()), math.U256(stack.peek())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Lsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Lsh(value, n))
-
return nil, nil
}
// opSHR implements Logical Shift Right
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
-func opSHR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(stack.pop()), math.U256(stack.peek())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Rsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Rsh(value, n))
-
return nil, nil
}
// opSAR implements Arithmetic Shift Right
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
-func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
- shift, value := math.U256(stack.pop()), math.S256(stack.pop())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
+func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.GtUint64(256) {
if value.Sign() >= 0 {
- value.SetUint64(0)
+ value.Clear()
} else {
- value.SetInt64(-1)
+ // Max negative shift: all bits set
+ value.SetAllOne()
}
- stack.push(math.U256(value))
return nil, nil
}
n := uint(shift.Uint64())
- value.Rsh(value, n)
- stack.push(math.U256(value))
-
+ value.SRsh(value, n)
return nil, nil
}
-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())
+func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.peek()
+ data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@@ -398,127 +247,149 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
- stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
- interpreter.intPool.put(offset, size)
+ size.SetBytes(interpreter.hasherBuf[:])
return nil, nil
}
-
-func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
+func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
return nil, nil
}
-func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- slot := stack.peek()
- slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
+func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ address := common.Address(slot.Bytes20())
+ slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
-func opBalanceMultiCoin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- addr, cid := stack.pop(), stack.pop()
- stack.push(interpreter.evm.StateDB.GetBalanceMultiCoin(common.BigToAddress(addr), common.BigToHash(cid)))
+func opBalanceMultiCoin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ addr, cid := callContext.stack.pop(), callContext.stack.pop()
+ callContext.stack.push(interpreter.evm.StateDB.GetBalanceMultiCoin(common.BigToAddress(addr), common.BigToHash(cid)))
return nil, nil
}
-func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
+func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
-
-func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
+func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
return nil, nil
}
-func opCallValue(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().Set(contract.value))
+func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(callContext.contract.value)
+ callContext.stack.push(v)
return nil, nil
}
-func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(getDataBig(contract.Input, stack.pop(), big32)))
+func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(callContext.contract.Input, offset, 32)
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
return nil, nil
}
-func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetInt64(int64(len(contract.Input))))
+func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
return nil, nil
}
-func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- dataOffset = stack.pop()
- length = stack.pop()
+ memOffset = callContext.stack.pop()
+ dataOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(contract.Input, dataOffset, length))
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+ callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
- interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil
}
-func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
+func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
-func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- dataOffset = stack.pop()
- length = stack.pop()
-
- end = interpreter.intPool.get().Add(dataOffset, length)
+ memOffset = callContext.stack.pop()
+ dataOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- defer interpreter.intPool.put(memOffset, dataOffset, length, end)
- if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
- return nil, errReturnDataOutOfBounds
+ offset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ return nil, ErrReturnDataOutOfBounds
}
- memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
-
+ // we can reuse dataOffset now (aliasing it for clarity)
+ var end = dataOffset
+ end.Add(&dataOffset, &length)
+ end64, overflow := end.Uint64WithOverflow()
+ if overflow || uint64(len(interpreter.returnData)) < end64 {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
return nil, nil
}
-func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- slot := stack.peek()
- slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
-
+func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
return nil, nil
}
-func opCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- l := interpreter.intPool.get().SetInt64(int64(len(contract.Code)))
- stack.push(l)
-
+func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ l := new(uint256.Int)
+ l.SetUint64(uint64(len(callContext.contract.Code)))
+ callContext.stack.push(l)
return nil, nil
}
-func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- codeOffset = stack.pop()
- length = stack.pop()
+ memOffset = callContext.stack.pop()
+ codeOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- codeCopy := getDataBig(contract.Code, codeOffset, length)
- memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
-func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- addr = common.BigToAddress(stack.pop())
+ stack = callContext.stack
+ a = stack.pop()
memOffset = stack.pop()
codeOffset = stack.pop()
length = stack.pop()
)
- codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
- memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ addr := common.Address(a.Bytes20())
+ codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
@@ -548,381 +419,463 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
//
// (6) Caller tries to get the code hash for an account which is marked as deleted,
// 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()
- address := common.BigToAddress(slot)
+func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) {
- slot.SetUint64(0)
+ slot.Clear()
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
return nil, nil
}
-func opGasprice(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
+func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.GasPrice)
+ callContext.stack.push(v)
return nil, nil
}
-func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- num := stack.pop()
-
- n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
- stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
+func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ num := callContext.stack.peek()
+ num64, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+ var upper, lower uint64
+ upper = interpreter.evm.BlockNumber.Uint64()
+ if upper < 257 {
+ lower = 0
} else {
- stack.push(interpreter.intPool.getZero())
+ lower = upper - 256
+ }
+ if num64 >= lower && num64 < upper {
+ num.SetBytes(interpreter.evm.GetHash(num64).Bytes())
+ } else {
+ num.Clear()
}
- interpreter.intPool.put(num, n)
return nil, nil
}
-func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
+func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil
}
-func opTimestamp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
+func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Time)
+ callContext.stack.push(v)
return nil, nil
}
-func opNumber(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
+func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
+ callContext.stack.push(v)
return nil, nil
}
-func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Difficulty)))
+func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Difficulty)
+ callContext.stack.push(v)
return nil, nil
}
-func opGasLimit(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit)))
+func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
return nil, nil
}
-func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- interpreter.intPool.put(stack.pop())
+func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.pop()
return nil, nil
}
-func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset := stack.pop()
- val := interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32))
- stack.push(val)
-
- interpreter.intPool.put(offset)
+func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v := callContext.stack.peek()
+ offset := int64(v.Uint64())
+ v.SetBytes(callContext.memory.GetPtr(offset, 32))
return nil, nil
}
-func opMstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// pop value of the stack
- mStart, val := stack.pop(), stack.pop()
- memory.Set32(mStart.Uint64(), val)
-
- interpreter.intPool.put(mStart, val)
+ mStart, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.Set32(mStart.Uint64(), &val)
return nil, nil
}
-func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- off, val := stack.pop().Int64(), stack.pop().Int64()
- memory.store[off] = byte(val & 0xff)
-
+func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ off, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil
}
-func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- loc := stack.peek()
- val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
+func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ loc := callContext.stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash)
loc.SetBytes(val.Bytes())
return nil, nil
}
-func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- loc := common.BigToHash(stack.pop())
- val := stack.pop()
- if err := interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)); err != nil {
+func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ loc := callContext.stack.pop()
+ val := callContext.stack.pop()
+ if err := interpreter.evm.StateDB.SetState(callContext.contract.Address(),
+ common.Hash(loc.Bytes32()), common.Hash(val.Bytes32())); err != nil {
return nil, err
}
- interpreter.intPool.put(val)
return nil, nil
}
-func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- pos := stack.pop()
- if !contract.validJumpdest(pos) {
- return nil, errInvalidJump
+func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ pos := callContext.stack.pop()
+ if !callContext.contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
}
*pc = pos.Uint64()
-
- interpreter.intPool.put(pos)
return nil, nil
}
-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.validJumpdest(pos) {
- return nil, errInvalidJump
+func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ pos, cond := callContext.stack.pop(), callContext.stack.pop()
+ if !cond.IsZero() {
+ if !callContext.contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
}
*pc = pos.Uint64()
} else {
*pc++
}
+ return nil, nil
+}
- interpreter.intPool.put(pos, cond)
+func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
return nil, nil
}
-func opJumpdest(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opBeginSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ return nil, ErrInvalidSubroutineEntry
+}
+
+func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if len(callContext.rstack.data) >= 1023 {
+ return nil, ErrReturnStackExceeded
+ }
+ pos := callContext.stack.pop()
+ if !pos.IsUint64() {
+ return nil, ErrInvalidJump
+ }
+ posU64 := pos.Uint64()
+ if !callContext.contract.validJumpSubdest(posU64) {
+ return nil, ErrInvalidJump
+ }
+ callContext.rstack.push(uint32(*pc))
+ *pc = posU64 + 1
return nil, nil
}
-func opPc(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(*pc))
+func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if len(callContext.rstack.data) == 0 {
+ return nil, ErrInvalidRetsub
+ }
+ // Other than the check that the return stack is not empty, there is no
+ // need to validate the pc from 'returns', since we only ever push valid
+ //values onto it via jumpsub.
+ *pc = uint64(callContext.rstack.pop()) + 1
return nil, nil
}
-func opMsize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetInt64(int64(memory.Len())))
+func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil
}
-func opGas(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(contract.Gas))
+func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
return nil, nil
}
-func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
+ return nil, nil
+}
+
+func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- value = stack.pop()
- offset, size = stack.pop(), stack.pop()
- input = memory.Get(offset.Int64(), size.Int64())
- gas = contract.Gas
+ value = callContext.stack.pop()
+ offset, size = callContext.stack.pop(), callContext.stack.pop()
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = callContext.contract.Gas
)
if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64
}
+ // reuse size int for stackvalue
+ stackvalue := size
+
+ callContext.contract.UseGas(gas)
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
+ bigVal = value.ToBig()
+ }
- contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value)
+ res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
- stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
- stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
- contract.Gas += returnGas
- interpreter.intPool.put(value, offset, size)
+ callContext.stack.push(&stackvalue)
+ callContext.contract.Gas += returnGas
- if suberr == errExecutionReverted {
+ if suberr == ErrExecutionReverted {
return res, nil
}
return nil, nil
}
-func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- endowment = stack.pop()
- offset, size = stack.pop(), stack.pop()
- salt = stack.pop()
- input = memory.Get(offset.Int64(), size.Int64())
- gas = contract.Gas
+ endowment = callContext.stack.pop()
+ offset, size = callContext.stack.pop(), callContext.stack.pop()
+ salt = callContext.stack.pop()
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = callContext.contract.Gas
)
// Apply EIP150
gas -= gas / 64
- contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt)
+ callContext.contract.UseGas(gas)
+ // reuse size int for stackvalue
+ stackvalue := size
+ //TODO: use uint256.Int instead of converting with toBig()
+ bigEndowment := big0
+ if !endowment.IsZero() {
+ bigEndowment = endowment.ToBig()
+ }
+ res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
+ bigEndowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
- stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
- contract.Gas += returnGas
- interpreter.intPool.put(endowment, offset, size, salt)
+ callContext.stack.push(&stackvalue)
+ callContext.contract.Gas += returnGas
- if suberr == errExecutionReverted {
+ if suberr == ErrExecutionReverted {
return res, nil
}
return nil, nil
}
-func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ // We can use this as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
- args := memory.Get(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ var bigVal = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
+ if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value)
+
+ ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal)
+
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
- if err == nil || err == errExecutionReverted {
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ stack.push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opCallExpert(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCallExpert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ // We can use this as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, cid, value2, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
+ toAddr := common.Address(addr.Bytes20())
coinID := common.BigToHash(cid)
- value = math.U256(value)
- value2 = math.U256(value2)
// Get the arguments from the memory.
- args := memory.Get(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ var bigVal = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
+ if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallExpert(contract, toAddr, args, gas, value, &coinID, value2)
+
+ var bigVal2 = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
+ if !value2.IsZero() {
+ bigVal2 = value2.ToBig()
+ }
+
+ ret, returnGas, err := interpreter.evm.CallExpert(callContext.contract, toAddr, args, gas, bigVal, &coinID, bigVal2)
+
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
- if err == nil || err == errExecutionReverted {
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ stack.push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-
-func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.Get(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value)
+
+ ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
- if err == nil || err == errExecutionReverted {
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ stack.push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.Get(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
- if err == nil || err == errExecutionReverted {
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ stack.push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.Get(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
- if err == nil || err == errExecutionReverted {
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ stack.push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opReturn(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset, size := stack.pop(), stack.pop()
- ret := memory.GetPtr(offset.Int64(), size.Int64())
+func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.pop()
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
-func opRevert(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset, size := stack.pop(), stack.pop()
- ret := memory.GetPtr(offset.Int64(), size.Int64())
+func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.pop()
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
-func opStop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
return nil, nil
}
-func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- balance := interpreter.evm.StateDB.GetBalance(contract.Address())
- interpreter.evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
-
- interpreter.evm.StateDB.Suicide(contract.Address())
+func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ beneficiary := callContext.stack.pop()
+ balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
+ interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
+ interpreter.evm.StateDB.Suicide(callContext.contract.Address())
return nil, nil
}
-func opEMC(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opEMC(pc *uint64, interpreter *EVMInterpreter, contract *Contract, callContext *callCtx) ([]byte, error) {
return nil, interpreter.evm.StateDB.EnableMultiCoin(contract.Address())
}
@@ -930,16 +883,18 @@ func opEMC(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
// make log instruction function
func makeLog(size int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
topics := make([]common.Hash, size)
+ stack := callContext.stack
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
- topics[i] = common.BigToHash(stack.pop())
+ addr := stack.pop()
+ topics[i] = common.Hash(addr.Bytes32())
}
- d := memory.Get(mStart.Int64(), mSize.Int64())
+ d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
interpreter.evm.StateDB.AddLog(&types.Log{
- Address: contract.Address(),
+ Address: callContext.contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
@@ -947,30 +902,29 @@ func makeLog(size int) executionFunc {
BlockNumber: interpreter.evm.BlockNumber.Uint64(),
})
- interpreter.intPool.put(mStart, mSize)
return nil, nil
}
}
// opPush1 is a specialized version of pushN
-func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- codeLen = uint64(len(contract.Code))
- integer = interpreter.intPool.get()
+ codeLen = uint64(len(callContext.contract.Code))
+ integer = new(uint256.Int)
)
*pc += 1
if *pc < codeLen {
- stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
+ callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
} else {
- stack.push(integer.SetUint64(0))
+ callContext.stack.push(integer.Clear())
}
return nil, nil
}
// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- codeLen := len(contract.Code)
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ codeLen := len(callContext.contract.Code)
startMin := codeLen
if int(*pc+1) < startMin {
@@ -982,8 +936,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
endMin = startMin + pushByteSize
}
- integer := interpreter.intPool.get()
- stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize)))
+ integer := new(uint256.Int)
+ callContext.stack.push(integer.SetBytes(common.RightPadBytes(
+ callContext.contract.Code[startMin:endMin], pushByteSize)))
*pc += size
return nil, nil
@@ -992,8 +947,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.dup(interpreter.intPool, int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.dup(int(size))
return nil, nil
}
}
@@ -1002,8 +957,8 @@ func makeDup(size int64) executionFunc {
func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size++
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.swap(int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.swap(int(size))
return nil, nil
}
}
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 7e2d324..4f95423 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -20,7 +20,7 @@ import (
"math/big"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
)
// StateDB is an EVM database for full state querying.
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index e23896a..a114a8d 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -17,13 +17,12 @@
package vm
import (
- "fmt"
"hash"
"sync/atomic"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
- "github.com/ava-labs/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/log"
)
var (
@@ -40,7 +39,7 @@ type Config struct {
NoRecursion bool // Disables call, callcode, delegate call and create
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
- JumpTable [256]operation // EVM instruction table, automatically populated if unset
+ JumpTable [256]*operation // EVM instruction table, automatically populated if unset
EWASMInterpreter string // External EWASM interpreter options
EVMInterpreter string // External EVM interpreter options
@@ -70,6 +69,15 @@ type Interpreter interface {
CanRun([]byte) bool
}
+// callCtx contains the things that are per-call, such as stack and memory,
+// but not transients like pc and gas
+type callCtx struct {
+ memory *Memory
+ stack *Stack
+ rstack *ReturnStack
+ contract *Contract
+}
+
// 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.
@@ -83,8 +91,6 @@ type EVMInterpreter struct {
evm *EVM
cfg Config
- intPool *intPool
-
hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
@@ -97,9 +103,11 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// We use the STOP instruction whether to see
// the jump table was initialised. If it was not
// we'll set the default jump table.
- if !cfg.JumpTable[STOP].valid {
+ if cfg.JumpTable[STOP] == nil {
var jt JumpTable
switch {
+ case evm.chainRules.IsYoloV1:
+ jt = yoloV1InstructionSet
case evm.chainRules.IsIstanbul:
jt = istanbulInstructionSet
case evm.chainRules.IsConstantinople:
@@ -136,7 +144,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
//
// 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.
+// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if contract.Address() == BuiltinAddr {
self := AccountRef(contract.Caller())
@@ -145,13 +153,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
contract.self = self
}
- if in.intPool == nil {
- in.intPool = poolOfIntPools.get()
- defer func() {
- poolOfIntPools.put(in.intPool)
- in.intPool = nil
- }()
- }
// Increment the call depth which is restricted to 1024
in.evm.depth++
@@ -174,9 +175,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
var (
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
+ returns = newReturnStack() // local returns stack
+ callContext = &callCtx{
+ memory: mem,
+ stack: stack,
+ rstack: returns,
+ contract: contract,
+ }
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
// to be uint256. Practically much less so feasible.
@@ -188,18 +196,22 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged bool // deferred Tracer should ignore already logged steps
res []byte // result of the opcode execution function
)
+ // Don't move this deferrred function, it's placed before the capturestate-deferred method,
+ // so that it get's executed _after_: the capturestate needs the stacks before
+ // they are returned to the pools
+ defer func() {
+ returnStack(stack)
+ returnRStack(returns)
+ }()
contract.Input = input
- // Reclaim the stack as an int pool when the execution stops
- defer func() { in.intPool.put(stack.data...) }()
-
if in.cfg.Debug {
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
}
}
}()
@@ -208,7 +220,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
- for atomic.LoadInt32(&in.evm.abort) == 0 {
+ steps := 0
+ for {
+ steps++
+ if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 {
+ break
+ }
if in.cfg.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
@@ -218,14 +235,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
- if !operation.valid {
- return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
+ if operation == nil {
+ return nil, &ErrInvalidOpCode{opcode: op}
}
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
- return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
+ return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
- return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
+ return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
// If the operation is valid, enforce and write restrictions
if in.readOnly && in.evm.chainRules.IsByzantium {
@@ -235,7 +252,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || ((op == CALL || op == CALLEX) && stack.Back(2).Sign() != 0) {
- return nil, errWriteProtection
+ return nil, ErrWriteProtection
}
}
// Static portion of gas
@@ -252,12 +269,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
- return nil, errGasUintOverflow
+ return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
- return nil, errGasUintOverflow
+ return nil, ErrGasUintOverflow
}
}
// Dynamic portion of gas
@@ -276,28 +293,23 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
if in.cfg.Debug {
- in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
logged = true
}
// execute the operation
- res, err = operation.execute(&pc, in, contract, mem, stack)
- // verifyPool is a build flag. Pool verification makes sure the integrity
- // of the integer pool by comparing values to a default value.
- if verifyPool {
- verifyIntegerPool(in.intPool)
- }
+ res, err = operation.execute(&pc, in, callContext)
// if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation.
if operation.returns {
- in.returnData = res
+ in.returnData = common.CopyBytes(res)
}
switch {
case err != nil:
return nil, err
case operation.reverts:
- return res, errExecutionReverted
+ return res, ErrExecutionReverted
case operation.halts:
return res, nil
case !operation.jumps:
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 737dd14..7e3e354 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -17,20 +17,16 @@
package vm
import (
- "errors"
-
"github.com/ava-labs/coreth/params"
)
type (
- executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool)
)
-var errGasUintOverflow = errors.New("gas uint64 overflow")
-
type operation struct {
// execute is the operation function
execute executionFunc
@@ -48,7 +44,6 @@ type operation struct {
halts bool // indicates whether the operation should halt further execution
jumps bool // indicates whether the program counter should not increment
writes bool // determines whether this a state modifying operation
- valid bool // indication whether the retrieved operation is valid and known
reverts bool // determines whether the operation reverts state (implicitly halts)
returns bool // determines whether the operations sets the return data content
}
@@ -61,10 +56,19 @@ var (
byzantiumInstructionSet = newByzantiumInstructionSet()
constantinopleInstructionSet = newConstantinopleInstructionSet()
istanbulInstructionSet = newIstanbulInstructionSet()
+ yoloV1InstructionSet = newYoloV1InstructionSet()
)
// JumpTable contains the EVM opcodes supported at a given fork.
-type JumpTable [256]operation
+type JumpTable [256]*operation
+
+func newYoloV1InstructionSet() JumpTable {
+ instructionSet := newIstanbulInstructionSet()
+
+ enable2315(&instructionSet) // Subroutines - https://eips.ethereum.org/EIPS/eip-2315
+
+ return instructionSet
+}
// newIstanbulInstructionSet returns the frontier, homestead
// byzantium, contantinople and petersburg instructions.
@@ -82,42 +86,37 @@ func newIstanbulInstructionSet() JumpTable {
// byzantium and contantinople instructions.
func newConstantinopleInstructionSet() JumpTable {
instructionSet := newByzantiumInstructionSet()
- instructionSet[SHL] = operation{
+ instructionSet[SHL] = &operation{
execute: opSHL,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SHR] = operation{
+ instructionSet[SHR] = &operation{
execute: opSHR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SAR] = operation{
+ instructionSet[SAR] = &operation{
execute: opSAR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[EXTCODEHASH] = operation{
+ instructionSet[EXTCODEHASH] = &operation{
execute: opExtCodeHash,
constantGas: params.ExtcodeHashGasConstantinople,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
}
- instructionSet[CREATE2] = operation{
+ instructionSet[CREATE2] = &operation{
execute: opCreate2,
constantGas: params.Create2Gas,
dynamicGas: gasCreate2,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
- valid: true,
writes: true,
returns: true,
}
@@ -128,39 +127,35 @@ func newConstantinopleInstructionSet() JumpTable {
// byzantium instructions.
func newByzantiumInstructionSet() JumpTable {
instructionSet := newSpuriousDragonInstructionSet()
- instructionSet[STATICCALL] = operation{
+ instructionSet[STATICCALL] = &operation{
execute: opStaticCall,
constantGas: params.CallGasEIP150,
dynamicGas: gasStaticCall,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
- valid: true,
returns: true,
}
- instructionSet[RETURNDATASIZE] = operation{
+ instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
}
- instructionSet[RETURNDATACOPY] = operation{
+ instructionSet[RETURNDATACOPY] = &operation{
execute: opReturnDataCopy,
constantGas: GasFastestStep,
dynamicGas: gasReturnDataCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryReturnDataCopy,
- valid: true,
}
- instructionSet[REVERT] = operation{
+ instructionSet[REVERT] = &operation{
execute: opRevert,
dynamicGas: gasRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
- valid: true,
reverts: true,
returns: true,
}
@@ -193,14 +188,13 @@ func newTangerineWhistleInstructionSet() JumpTable {
// instructions that can be executed during the homestead phase.
func newHomesteadInstructionSet() JumpTable {
instructionSet := newFrontierInstructionSet()
- instructionSet[DELEGATECALL] = operation{
+ instructionSet[DELEGATECALL] = &operation{
execute: opDelegateCall,
dynamicGas: gasDelegateCall,
constantGas: params.CallGasFrontier,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
- valid: true,
returns: true,
}
return instructionSet
@@ -216,161 +210,138 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
halts: true,
- valid: true,
},
ADD: {
execute: opAdd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MUL: {
execute: opMul,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SUB: {
execute: opSub,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
DIV: {
execute: opDiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SDIV: {
execute: opSdiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MOD: {
execute: opMod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SMOD: {
execute: opSmod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ADDMOD: {
execute: opAddmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
MULMOD: {
execute: opMulmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
EXP: {
execute: opExp,
dynamicGas: gasExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SIGNEXTEND: {
execute: opSignExtend,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
LT: {
execute: opLt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
GT: {
execute: opGt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SLT: {
execute: opSlt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SGT: {
execute: opSgt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
EQ: {
execute: opEq,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ISZERO: {
execute: opIszero,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
AND: {
execute: opAnd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
XOR: {
execute: opXor,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
OR: {
execute: opOr,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
NOT: {
execute: opNot,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
BYTE: {
execute: opByte,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SHA3: {
execute: opSha3,
@@ -379,63 +350,54 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
memorySize: memorySha3,
- valid: true,
},
ADDRESS: {
execute: opAddress,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
BALANCE: {
execute: opBalance,
constantGas: params.BalanceGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
BALANCEMC: {
execute: opBalanceMultiCoin,
constantGas: params.BalanceGasFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ORIGIN: {
execute: opOrigin,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLER: {
execute: opCaller,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLVALUE: {
execute: opCallValue,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATALOAD: {
execute: opCallDataLoad,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
CALLDATASIZE: {
execute: opCallDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATACOPY: {
execute: opCallDataCopy,
@@ -444,14 +406,12 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCallDataCopy,
- valid: true,
},
CODESIZE: {
execute: opCodeSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CODECOPY: {
execute: opCodeCopy,
@@ -460,21 +420,18 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCodeCopy,
- valid: true,
},
GASPRICE: {
execute: opGasprice,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
EXTCODESIZE: {
execute: opExtCodeSize,
constantGas: params.ExtcodeSizeGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
EXTCODECOPY: {
execute: opExtCodeCopy,
@@ -483,56 +440,48 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryExtCodeCopy,
- valid: true,
},
BLOCKHASH: {
execute: opBlockhash,
constantGas: GasExtStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
COINBASE: {
execute: opCoinbase,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
TIMESTAMP: {
execute: opTimestamp,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
NUMBER: {
execute: opNumber,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
DIFFICULTY: {
execute: opDifficulty,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
GASLIMIT: {
execute: opGasLimit,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
POP: {
execute: opPop,
constantGas: GasQuickStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
- valid: true,
},
MLOAD: {
execute: opMload,
@@ -541,7 +490,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
memorySize: memoryMLoad,
- valid: true,
},
MSTORE: {
execute: opMstore,
@@ -550,7 +498,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryMStore,
- valid: true,
},
MSTORE8: {
execute: opMstore8,
@@ -559,22 +506,18 @@ func newFrontierInstructionSet() JumpTable {
memorySize: memoryMStore8,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
-
- valid: true,
},
SLOAD: {
execute: opSload,
constantGas: params.SloadGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
SSTORE: {
execute: opSstore,
dynamicGas: gasSStore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
- valid: true,
writes: true,
},
JUMP: {
@@ -583,7 +526,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
jumps: true,
- valid: true,
},
JUMPI: {
execute: opJumpi,
@@ -591,490 +533,420 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
jumps: true,
- valid: true,
},
PC: {
execute: opPc,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
MSIZE: {
execute: opMsize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
GAS: {
execute: opGas,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
JUMPDEST: {
execute: opJumpdest,
constantGas: params.JumpdestGas,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
- valid: true,
},
EMC: {
execute: opEMC,
constantGas: params.EMCGas,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
- valid: true,
},
PUSH1: {
execute: opPush1,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH2: {
execute: makePush(2, 2),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH3: {
execute: makePush(3, 3),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH4: {
execute: makePush(4, 4),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH5: {
execute: makePush(5, 5),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH6: {
execute: makePush(6, 6),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH7: {
execute: makePush(7, 7),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH8: {
execute: makePush(8, 8),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH9: {
execute: makePush(9, 9),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH10: {
execute: makePush(10, 10),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH11: {
execute: makePush(11, 11),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH12: {
execute: makePush(12, 12),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH13: {
execute: makePush(13, 13),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH14: {
execute: makePush(14, 14),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH15: {
execute: makePush(15, 15),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH16: {
execute: makePush(16, 16),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH17: {
execute: makePush(17, 17),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH18: {
execute: makePush(18, 18),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH19: {
execute: makePush(19, 19),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH20: {
execute: makePush(20, 20),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH21: {
execute: makePush(21, 21),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH22: {
execute: makePush(22, 22),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH23: {
execute: makePush(23, 23),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH24: {
execute: makePush(24, 24),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH25: {
execute: makePush(25, 25),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH26: {
execute: makePush(26, 26),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH27: {
execute: makePush(27, 27),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH28: {
execute: makePush(28, 28),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH29: {
execute: makePush(29, 29),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH30: {
execute: makePush(30, 30),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH31: {
execute: makePush(31, 31),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH32: {
execute: makePush(32, 32),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
DUP1: {
execute: makeDup(1),
constantGas: GasFastestStep,
minStack: minDupStack(1),
maxStack: maxDupStack(1),
- valid: true,
},
DUP2: {
execute: makeDup(2),
constantGas: GasFastestStep,
minStack: minDupStack(2),
maxStack: maxDupStack(2),
- valid: true,
},
DUP3: {
execute: makeDup(3),
constantGas: GasFastestStep,
minStack: minDupStack(3),
maxStack: maxDupStack(3),
- valid: true,
},
DUP4: {
execute: makeDup(4),
constantGas: GasFastestStep,
minStack: minDupStack(4),
maxStack: maxDupStack(4),
- valid: true,
},
DUP5: {
execute: makeDup(5),
constantGas: GasFastestStep,
minStack: minDupStack(5),
maxStack: maxDupStack(5),
- valid: true,
},
DUP6: {
execute: makeDup(6),
constantGas: GasFastestStep,
minStack: minDupStack(6),
maxStack: maxDupStack(6),
- valid: true,
},
DUP7: {
execute: makeDup(7),
constantGas: GasFastestStep,
minStack: minDupStack(7),
maxStack: maxDupStack(7),
- valid: true,
},
DUP8: {
execute: makeDup(8),
constantGas: GasFastestStep,
minStack: minDupStack(8),
maxStack: maxDupStack(8),
- valid: true,
},
DUP9: {
execute: makeDup(9),
constantGas: GasFastestStep,
minStack: minDupStack(9),
maxStack: maxDupStack(9),
- valid: true,
},
DUP10: {
execute: makeDup(10),
constantGas: GasFastestStep,
minStack: minDupStack(10),
maxStack: maxDupStack(10),
- valid: true,
},
DUP11: {
execute: makeDup(11),
constantGas: GasFastestStep,
minStack: minDupStack(11),
maxStack: maxDupStack(11),
- valid: true,
},
DUP12: {
execute: makeDup(12),
constantGas: GasFastestStep,
minStack: minDupStack(12),
maxStack: maxDupStack(12),
- valid: true,
},
DUP13: {
execute: makeDup(13),
constantGas: GasFastestStep,
minStack: minDupStack(13),
maxStack: maxDupStack(13),
- valid: true,
},
DUP14: {
execute: makeDup(14),
constantGas: GasFastestStep,
minStack: minDupStack(14),
maxStack: maxDupStack(14),
- valid: true,
},
DUP15: {
execute: makeDup(15),
constantGas: GasFastestStep,
minStack: minDupStack(15),
maxStack: maxDupStack(15),
- valid: true,
},
DUP16: {
execute: makeDup(16),
constantGas: GasFastestStep,
minStack: minDupStack(16),
maxStack: maxDupStack(16),
- valid: true,
},
SWAP1: {
execute: makeSwap(1),
constantGas: GasFastestStep,
minStack: minSwapStack(2),
maxStack: maxSwapStack(2),
- valid: true,
},
SWAP2: {
execute: makeSwap(2),
constantGas: GasFastestStep,
minStack: minSwapStack(3),
maxStack: maxSwapStack(3),
- valid: true,
},
SWAP3: {
execute: makeSwap(3),
constantGas: GasFastestStep,
minStack: minSwapStack(4),
maxStack: maxSwapStack(4),
- valid: true,
},
SWAP4: {
execute: makeSwap(4),
constantGas: GasFastestStep,
minStack: minSwapStack(5),
maxStack: maxSwapStack(5),
- valid: true,
},
SWAP5: {
execute: makeSwap(5),
constantGas: GasFastestStep,
minStack: minSwapStack(6),
maxStack: maxSwapStack(6),
- valid: true,
},
SWAP6: {
execute: makeSwap(6),
constantGas: GasFastestStep,
minStack: minSwapStack(7),
maxStack: maxSwapStack(7),
- valid: true,
},
SWAP7: {
execute: makeSwap(7),
constantGas: GasFastestStep,
minStack: minSwapStack(8),
maxStack: maxSwapStack(8),
- valid: true,
},
SWAP8: {
execute: makeSwap(8),
constantGas: GasFastestStep,
minStack: minSwapStack(9),
maxStack: maxSwapStack(9),
- valid: true,
},
SWAP9: {
execute: makeSwap(9),
constantGas: GasFastestStep,
minStack: minSwapStack(10),
maxStack: maxSwapStack(10),
- valid: true,
},
SWAP10: {
execute: makeSwap(10),
constantGas: GasFastestStep,
minStack: minSwapStack(11),
maxStack: maxSwapStack(11),
- valid: true,
},
SWAP11: {
execute: makeSwap(11),
constantGas: GasFastestStep,
minStack: minSwapStack(12),
maxStack: maxSwapStack(12),
- valid: true,
},
SWAP12: {
execute: makeSwap(12),
constantGas: GasFastestStep,
minStack: minSwapStack(13),
maxStack: maxSwapStack(13),
- valid: true,
},
SWAP13: {
execute: makeSwap(13),
constantGas: GasFastestStep,
minStack: minSwapStack(14),
maxStack: maxSwapStack(14),
- valid: true,
},
SWAP14: {
execute: makeSwap(14),
constantGas: GasFastestStep,
minStack: minSwapStack(15),
maxStack: maxSwapStack(15),
- valid: true,
},
SWAP15: {
execute: makeSwap(15),
constantGas: GasFastestStep,
minStack: minSwapStack(16),
maxStack: maxSwapStack(16),
- valid: true,
},
SWAP16: {
execute: makeSwap(16),
constantGas: GasFastestStep,
minStack: minSwapStack(17),
maxStack: maxSwapStack(17),
- valid: true,
},
LOG0: {
execute: makeLog(0),
@@ -1082,7 +954,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG1: {
@@ -1091,7 +962,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG2: {
@@ -1100,7 +970,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG3: {
@@ -1109,7 +978,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG4: {
@@ -1118,7 +986,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
CREATE: {
@@ -1128,7 +995,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
- valid: true,
writes: true,
returns: true,
},
@@ -1139,7 +1005,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
returns: true,
},
CALLEX: {
@@ -1149,7 +1014,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(9, 1),
maxStack: maxStack(9, 1),
memorySize: memoryCallExpert,
- valid: true,
returns: true,
},
CALLCODE: {
@@ -1159,7 +1023,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
returns: true,
},
RETURN: {
@@ -1169,7 +1032,6 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(2, 0),
memorySize: memoryReturn,
halts: true,
- valid: true,
},
SELFDESTRUCT: {
execute: opSuicide,
@@ -1177,7 +1039,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
halts: true,
- valid: true,
writes: true,
},
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 1a66ef8..99688b1 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -70,7 +70,7 @@ const (
SHR
SAR
- SHA3 = 0x20
+ SHA3 OpCode = 0x20
)
// 0x30 range - closure state.
@@ -101,26 +101,27 @@ const (
NUMBER
DIFFICULTY
GASLIMIT
- CHAINID = 0x46
- SELFBALANCE = 0x47
+ CHAINID OpCode = 0x46
+ SELFBALANCE OpCode = 0x47
)
// 0x50 range - 'storage' and execution.
const (
- POP OpCode = 0x50 + iota
- MLOAD
- MSTORE
- MSTORE8
- SLOAD
- SSTORE
- JUMP
- JUMPI
- PC
- MSIZE
- GAS
- JUMPDEST
- EMC = 0x5c
- BALANCEMC = 0x5d
+ POP OpCode = 0x50
+ MLOAD OpCode = 0x51
+ MSTORE OpCode = 0x52
+ MSTORE8 OpCode = 0x53
+ SLOAD OpCode = 0x54
+ SSTORE OpCode = 0x55
+ JUMP OpCode = 0x56
+ JUMPI OpCode = 0x57
+ PC OpCode = 0x58
+ MSIZE OpCode = 0x59
+ GAS OpCode = 0x5a
+ JUMPDEST OpCode = 0x5b
+ BEGINSUB OpCode = 0x5c
+ RETURNSUB OpCode = 0x5d
+ JUMPSUB OpCode = 0x5e
)
// 0x60 range.
@@ -207,6 +208,12 @@ const (
SWAP
)
+const (
+ BALANCEMC = 0xcd
+ EMC = 0xce
+ CALLEX = 0xcf
+)
+
// 0xf0 range - closures.
const (
CREATE OpCode = 0xf0 + iota
@@ -215,11 +222,9 @@ const (
RETURN
DELEGATECALL
CREATE2
- CALLEX = 0xf6
- STATICCALL = 0xfa
-
- REVERT = 0xfd
- SELFDESTRUCT = 0xff
+ STATICCALL OpCode = 0xfa
+ REVERT OpCode = 0xfd
+ SELFDESTRUCT OpCode = 0xff
)
// Since the opcodes aren't all in order we can't use a regular slice.
@@ -301,7 +306,10 @@ var opCodeToString = map[OpCode]string{
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
- EMC: "EMC",
+
+ BEGINSUB: "BEGINSUB",
+ JUMPSUB: "JUMPSUB",
+ RETURNSUB: "RETURNSUB",
// 0x60 range - push.
PUSH1: "PUSH1",
@@ -379,6 +387,7 @@ var opCodeToString = map[OpCode]string{
// 0xf0 range.
CREATE: "CREATE",
CALL: "CALL",
+ EMC: "EMC",
CALLEX: "CALLEX",
RETURN: "RETURN",
CALLCODE: "CALLCODE",
@@ -396,7 +405,7 @@ var opCodeToString = map[OpCode]string{
func (op OpCode) String() string {
str := opCodeToString[op]
if len(str) == 0 {
- return fmt.Sprintf("Missing opcode 0x%x", int(op))
+ return fmt.Sprintf("opcode 0x%x not defined", int(op))
}
return str
@@ -469,7 +478,9 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
- "EMC": EMC,
+ "BEGINSUB": BEGINSUB,
+ "RETURNSUB": RETURNSUB,
+ "JUMPSUB": JUMPSUB,
"PUSH1": PUSH1,
"PUSH2": PUSH2,
"PUSH3": PUSH3,
@@ -542,6 +553,7 @@ var stringToOp = map[string]OpCode{
"CREATE": CREATE,
"CREATE2": CREATE2,
"CALL": CALL,
+ "EMC": EMC,
"CALLEX": CALLEX,
"RETURN": RETURN,
"CALLCODE": CALLCODE,
diff --git a/eth/api.go b/eth/api.go
index b64090e..92ac928 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -34,10 +34,10 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/internal/ethapi"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/rlp"
- "github.com/ava-labs/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
)
// PublicEthereumAPI provides an API to access Ethereum full node-related
@@ -166,8 +166,21 @@ func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI {
return &PrivateAdminAPI{eth: eth}
}
-// ExportChain exports the current blockchain into a local file.
-func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
+// ExportChain exports the current blockchain into a local file,
+// or a range of blocks if first and last are non-nil
+func (api *PrivateAdminAPI) ExportChain(file string, first *uint64, last *uint64) (bool, error) {
+ if first == nil && last != nil {
+ return false, errors.New("last cannot be specified without first")
+ }
+ if first != nil && last == nil {
+ head := api.eth.BlockChain().CurrentHeader().Number.Uint64()
+ last = &head
+ }
+ if _, err := os.Stat(file); err == nil {
+ // File already exists. Allowing overwrite could be a DoS vecotor,
+ // since the 'file' may point to arbitrary paths on the drive
+ return false, errors.New("location would overwrite an existing file")
+ }
// Make sure we can create the file to export into
out, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
if err != nil {
@@ -182,7 +195,11 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
}
// Export the blockchain
- if err := api.eth.BlockChain().Export(writer); err != nil {
+ if first != nil {
+ if err := api.eth.BlockChain().ExportN(writer, *first, *last); err != nil {
+ return false, err
+ }
+ } else if err := api.eth.BlockChain().Export(writer); err != nil {
return false, err
}
return true, nil
@@ -336,70 +353,52 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
return results, nil
}
-// AccountRangeResult returns a mapping from the hash of an account addresses
-// to its preimage. It will return the JSON null if no preimage is found.
-// Since a query can return a limited amount of results, a "next" field is
-// also present for paging.
-type AccountRangeResult struct {
- Accounts map[common.Hash]*common.Address `json:"accounts"`
- Next common.Hash `json:"next"`
-}
-
-func accountRange(st state.Trie, start *common.Hash, maxResults int) (AccountRangeResult, error) {
- if start == nil {
- start = &common.Hash{0}
- }
- it := trie.NewIterator(st.NodeIterator(start.Bytes()))
- result := AccountRangeResult{Accounts: make(map[common.Hash]*common.Address), Next: common.Hash{}}
-
- if maxResults > AccountRangeMaxResults {
- maxResults = AccountRangeMaxResults
- }
-
- for i := 0; i < maxResults && it.Next(); i++ {
- if preimage := st.GetKey(it.Key); preimage != nil {
- addr := &common.Address{}
- addr.SetBytes(preimage)
- result.Accounts[common.BytesToHash(it.Key)] = addr
- } else {
- result.Accounts[common.BytesToHash(it.Key)] = nil
- }
- }
-
- if it.Next() {
- result.Next = common.BytesToHash(it.Key)
- }
-
- return result, nil
-}
-
// AccountRangeMaxResults is the maximum number of results to be returned per call
const AccountRangeMaxResults = 256
-// AccountRange enumerates all accounts in the latest state
-func (api *PrivateDebugAPI) AccountRange(ctx context.Context, start *common.Hash, maxResults int) (AccountRangeResult, error) {
- var statedb *state.StateDB
+// AccountRange enumerates all accounts in the given block and start point in paging request
+func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start []byte, maxResults int, nocode, nostorage, incompletes bool) (state.IteratorDump, error) {
+ var stateDb *state.StateDB
var err error
- block := api.eth.blockchain.CurrentBlock()
- if len(block.Transactions()) == 0 {
- statedb, err = api.computeStateDB(block, defaultTraceReexec)
- if err != nil {
- return AccountRangeResult{}, err
+ if number, ok := blockNrOrHash.Number(); ok {
+ if number == rpc.PendingBlockNumber {
+ // If we're dumping the pending state, we need to request
+ // both the pending block as well as the pending state from
+ // the miner and operate on those
+ _, stateDb = api.eth.miner.Pending()
+ } else {
+ var block *types.Block
+ if number == rpc.LatestBlockNumber {
+ block = api.eth.blockchain.CurrentBlock()
+ } else if blockNr == rpc.AcceptedBlockNumber {
+ block = api.eth.AcceptedBlock()
+ } else {
+ block = api.eth.blockchain.GetBlockByNumber(uint64(number))
+ }
+ if block == nil {
+ return state.IteratorDump{}, fmt.Errorf("block #%d not found", number)
+ }
+ stateDb, err = api.eth.BlockChain().StateAt(block.Root())
+ if err != nil {
+ return state.IteratorDump{}, err
+ }
}
- } else {
- _, _, statedb, err = api.computeTxEnv(block.Hash(), len(block.Transactions())-1, 0)
+ } else if hash, ok := blockNrOrHash.Hash(); ok {
+ block := api.eth.blockchain.GetBlockByHash(hash)
+ if block == nil {
+ return state.IteratorDump{}, fmt.Errorf("block %s not found", hash.Hex())
+ }
+ stateDb, err = api.eth.BlockChain().StateAt(block.Root())
if err != nil {
- return AccountRangeResult{}, err
+ return state.IteratorDump{}, err
}
}
- trie, err := statedb.Database().OpenTrie(block.Header().Root)
- if err != nil {
- return AccountRangeResult{}, err
+ if maxResults > AccountRangeMaxResults || maxResults <= 0 {
+ maxResults = AccountRangeMaxResults
}
-
- return accountRange(trie, start, maxResults)
+ return stateDb.IteratorDump(nocode, nostorage, incompletes, start, maxResults), nil
}
// StorageRangeResult is the result of a debug_storageRangeAt API call.
@@ -416,8 +415,13 @@ type storageEntry struct {
}
// StorageRangeAt returns the storage at the given block height and transaction index.
-func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
- _, _, statedb, err := api.computeTxEnv(blockHash, txIndex, 0)
+func (api *PrivateDebugAPI) StorageRangeAt(blockHash common.Hash, txIndex int, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
+ // Retrieve the block
+ block := api.eth.blockchain.GetBlockByHash(blockHash)
+ if block == nil {
+ return StorageRangeResult{}, fmt.Errorf("block %#x not found", blockHash)
+ }
+ _, _, statedb, err := api.computeTxEnv(block, txIndex, 0)
if err != nil {
return StorageRangeResult{}, err
}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index d4061f8..65c3be4 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -22,20 +22,21 @@ import (
"math/big"
"github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/eth/gasprice"
+ "github.com/ava-labs/coreth/miner"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
- "github.com/ava-labs/go-ethereum/core/bloombits"
- "github.com/ava-labs/go-ethereum/eth/downloader"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/bloombits"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
)
// EthAPIBackend implements ethapi.Backend for full nodes
@@ -79,6 +80,23 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
}
+func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
+ if blockNr, ok := blockNrOrHash.Number(); ok {
+ return b.HeaderByNumber(ctx, blockNr)
+ }
+ if hash, ok := blockNrOrHash.Hash(); ok {
+ header := b.eth.blockchain.GetHeaderByHash(hash)
+ if header == nil {
+ return nil, errors.New("header for hash not found")
+ }
+ if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
+ return nil, errors.New("hash is not currently canonical")
+ }
+ return header, nil
+ }
+ return nil, errors.New("invalid arguments; neither block nor hash specified")
+}
+
func (b *EthAPIBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return b.eth.blockchain.GetHeaderByHash(hash), nil
}
@@ -103,6 +121,27 @@ func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*typ
return b.eth.blockchain.GetBlockByHash(hash), nil
}
+func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
+ if blockNr, ok := blockNrOrHash.Number(); ok {
+ return b.BlockByNumber(ctx, blockNr)
+ }
+ if hash, ok := blockNrOrHash.Hash(); ok {
+ header := b.eth.blockchain.GetHeaderByHash(hash)
+ if header == nil {
+ return nil, errors.New("header for hash not found")
+ }
+ if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
+ return nil, errors.New("hash is not currently canonical")
+ }
+ block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
+ if block == nil {
+ return nil, errors.New("header found, but block body is missing")
+ }
+ return block, nil
+ }
+ return nil, errors.New("invalid arguments; neither block nor hash specified")
+}
+
func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
// Pending state is only known by the miner
if number == rpc.PendingBlockNumber {
@@ -121,6 +160,27 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
return stateDb, header, err
}
+func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
+ if blockNr, ok := blockNrOrHash.Number(); ok {
+ return b.StateAndHeaderByNumber(ctx, blockNr)
+ }
+ if hash, ok := blockNrOrHash.Hash(); ok {
+ header, err := b.HeaderByHash(ctx, hash)
+ if err != nil {
+ return nil, nil, err
+ }
+ if header == nil {
+ return nil, nil, errors.New("header for hash not found")
+ }
+ if blockNrOrHash.RequireCanonical && b.eth.blockchain.GetCanonicalHash(header.Number.Uint64()) != hash {
+ return nil, nil, errors.New("hash is not currently canonical")
+ }
+ stateDb, err := b.eth.BlockChain().StateAt(header.Root)
+ return stateDb, header, err
+ }
+ return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
+}
+
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return b.eth.blockchain.GetReceiptsByHash(hash), nil
}
@@ -137,12 +197,11 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*typ
return logs, nil
}
-func (b *EthAPIBackend) GetTd(blockHash common.Hash) *big.Int {
- return b.eth.blockchain.GetTdByHash(blockHash)
+func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
+ return b.eth.blockchain.GetTdByHash(hash)
}
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
- state.SetBalance(msg.From(), math.MaxBig256)
vmError := func() error { return nil }
context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
@@ -153,6 +212,10 @@ func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEven
return b.eth.BlockChain().SubscribeRemovedLogsEvent(ch)
}
+func (b *EthAPIBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return b.eth.miner.SubscribePendingLogs(ch)
+}
+
func (b *EthAPIBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return b.eth.BlockChain().SubscribeChainEvent(ch)
}
@@ -211,6 +274,10 @@ func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.TxPool().Content()
}
+func (b *EthAPIBackend) TxPool() *core.TxPool {
+ return b.eth.TxPool()
+}
+
func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
}
@@ -243,10 +310,14 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool {
return b.extRPCEnabled
}
-func (b *EthAPIBackend) RPCGasCap() *big.Int {
+func (b *EthAPIBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap
}
+func (b *EthAPIBackend) RPCTxFeeCap() float64 {
+ return b.eth.config.RPCTxFeeCap
+}
+
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
sections, _, _ := b.eth.bloomIndexer.Sections()
return params.BloomBitsBlocks, sections
@@ -257,3 +328,19 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
}
}
+
+func (b *EthAPIBackend) Engine() consensus.Engine {
+ return b.eth.engine
+}
+
+func (b *EthAPIBackend) CurrentHeader() *types.Header {
+ return b.eth.blockchain.CurrentHeader()
+}
+
+func (b *EthAPIBackend) Miner() *miner.Miner {
+ return b.eth.Miner()
+}
+
+func (b *EthAPIBackend) StartMining(threads int) error {
+ return b.eth.StartMining(threads)
+}
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index c8a5307..c044dcc 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -36,11 +36,11 @@ import (
"github.com/ava-labs/coreth/eth/tracers"
"github.com/ava-labs/coreth/internal/ethapi"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/rlp"
- "github.com/ava-labs/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/trie"
)
const (
@@ -151,7 +151,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
// Ensure we have a valid starting state before doing any work
origin := start.NumberU64()
- database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16) // Chain tracing will probably start at genesis
+ database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16, "") // Chain tracing will probably start at genesis
if number := start.NumberU64(); number > 0 {
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
@@ -159,7 +159,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
return nil, fmt.Errorf("parent block #%d not found", number-1)
}
}
- statedb, err := state.New(start.Root(), database)
+ statedb, err := state.New(start.Root(), database, nil)
if err != nil {
// If the starting state is missing, allow some number of blocks to be reexecuted
reexec := defaultTraceReexec
@@ -172,7 +172,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
if start == nil {
break
}
- if statedb, err = state.New(start.Root(), database); err == nil {
+ if statedb, err = state.New(start.Root(), database, nil); err == nil {
break
}
}
@@ -407,7 +407,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(ctx context.Context, file string,
return api.TraceBlock(ctx, blob, config)
}
-// TraceBadBlockByHash returns the structured logs created during the execution of
+// TraceBadBlock returns the structured logs created during the execution of
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
// object.
func (api *PrivateDebugAPI) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
@@ -508,7 +508,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{})
- if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
+ if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
break
}
@@ -602,7 +602,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
}
// Execute the transaction and flush any traces to disk
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf)
- _, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
+ _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
}
@@ -647,14 +647,14 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
}
// Otherwise try to reexec blocks until we find a state or reach our limit
origin := block.NumberU64()
- database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16)
+ database := state.NewDatabaseWithCache(api.eth.ChainDb(), 16, "")
for i := uint64(0); i < reexec; i++ {
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if block == nil {
break
}
- if statedb, err = state.New(block.Root(), database); err == nil {
+ if statedb, err = state.New(block.Root(), database, nil); err == nil {
break
}
}
@@ -717,7 +717,12 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
if config != nil && config.Reexec != nil {
reexec = *config.Reexec
}
- msg, vmctx, statedb, err := api.computeTxEnv(blockHash, int(index), reexec)
+ // Retrieve the block
+ block := api.eth.blockchain.GetBlockByHash(blockHash)
+ if block == nil {
+ return nil, fmt.Errorf("block %#x not found", blockHash)
+ }
+ msg, vmctx, statedb, err := api.computeTxEnv(block, int(index), reexec)
if err != nil {
return nil, err
}
@@ -725,6 +730,40 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
return api.traceTx(ctx, msg, vmctx, statedb, config)
}
+// TraceCall lets you trace a given eth_call. It collects the structured logs created during the execution of EVM
+// if the given transaction was added on top of the provided block and returns them as a JSON object.
+// You can provide -2 as a block number to trace on top of the pending block.
+func (api *PrivateDebugAPI) TraceCall(ctx context.Context, args ethapi.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceConfig) (interface{}, error) {
+ // First try to retrieve the state
+ statedb, header, err := api.eth.APIBackend.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
+ if err != nil {
+ // Try to retrieve the specified block
+ var block *types.Block
+ if hash, ok := blockNrOrHash.Hash(); ok {
+ block = api.eth.blockchain.GetBlockByHash(hash)
+ } else if number, ok := blockNrOrHash.Number(); ok {
+ block = api.eth.blockchain.GetBlockByNumber(uint64(number))
+ }
+ if block == nil {
+ return nil, fmt.Errorf("block %v not found: %v", blockNrOrHash, err)
+ }
+ // try to recompute the state
+ reexec := defaultTraceReexec
+ if config != nil && config.Reexec != nil {
+ reexec = *config.Reexec
+ }
+ _, _, statedb, err = api.computeTxEnv(block, 0, reexec)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Execute the trace
+ msg := args.ToMessage(api.eth.APIBackend.RPCGasCap())
+ vmctx := core.NewEVMContext(msg, header, api.eth.blockchain, nil)
+ return api.traceTx(ctx, msg, vmctx, statedb, config)
+}
+
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
@@ -764,17 +803,22 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
// Run the transaction with tracing enabled.
vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer})
- ret, gas, failed, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
+ result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
return nil, fmt.Errorf("tracing failed: %v", err)
}
// Depending on the tracer type, format and return the output
switch tracer := tracer.(type) {
case *vm.StructLogger:
+ // If the result contains a revert reason, return it.
+ returnVal := fmt.Sprintf("%x", result.Return())
+ if len(result.Revert()) > 0 {
+ returnVal = fmt.Sprintf("%x", result.Revert())
+ }
return &ethapi.ExecutionResult{
- Gas: gas,
- Failed: failed,
- ReturnValue: fmt.Sprintf("%x", ret),
+ Gas: result.UsedGas,
+ Failed: result.Failed(),
+ ReturnValue: returnVal,
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
@@ -787,12 +831,8 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
}
// computeTxEnv returns the execution environment of a certain transaction.
-func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) {
+func (api *PrivateDebugAPI) computeTxEnv(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.StateDB, error) {
// Create the parent state database
- block := api.eth.blockchain.GetBlockByHash(blockHash)
- if block == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("block %#x not found", blockHash)
- }
parent := api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
@@ -818,12 +858,12 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
}
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, statedb, api.eth.blockchain.Config(), vm.Config{})
- if _, _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
+ if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
}
- return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash)
+ return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
diff --git a/eth/backend.go b/eth/backend.go
index 056f8cb..9222181 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -41,27 +41,18 @@ import (
"github.com/ava-labs/coreth/node"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/go-ethereum/accounts/abi/bind"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/core/bloombits"
- "github.com/ava-labs/go-ethereum/eth/downloader"
- "github.com/ava-labs/go-ethereum/ethdb"
- "github.com/ava-labs/go-ethereum/event"
- "github.com/ava-labs/go-ethereum/log"
- "github.com/ava-labs/go-ethereum/p2p"
- "github.com/ava-labs/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/bloombits"
+ "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/rlp"
)
-type LesServer interface {
- Start(srvr *p2p.Server)
- Stop()
- APIs() []rpc.API
- Protocols() []p2p.Protocol
- SetBloomBitsIndexer(bbIndexer *core.ChainIndexer)
- SetContractBackend(bind.ContractBackend)
-}
-
type BackendCallbacks struct {
OnQueryAcceptedBlock func() *types.Block
}
@@ -70,16 +61,11 @@ type BackendCallbacks struct {
type Ethereum struct {
config *Config
- // Channel for shutting down the service
- shutdownChan chan bool
-
- server *p2p.Server
-
// Handlers
txPool *core.TxPool
blockchain *core.BlockChain
//protocolManager *ProtocolManager
- lesServer LesServer
+ dialCandidates enode.Iterator
// DB interfaces
chainDb ethdb.Database // Block chain database
@@ -88,8 +74,9 @@ type Ethereum struct {
engine consensus.Engine
accountManager *accounts.Manager
- bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
- bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
+ bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests
+ bloomIndexer *core.ChainIndexer // Bloom indexer operating during block imports
+ closeBloomHandler chan struct{}
APIBackend *EthAPIBackend
@@ -100,28 +87,17 @@ type Ethereum struct {
networkID uint64
netRPCService *ethapi.PublicNetAPI
+ p2pServer *p2p.Server
+
lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase)
txSubmitChan chan struct{}
bcb *BackendCallbacks
}
-func (s *Ethereum) AddLesServer(ls LesServer) {
- s.lesServer = ls
- ls.SetBloomBitsIndexer(s.bloomIndexer)
-}
-
-// SetClient sets a rpc client which connecting to our local node.
-func (s *Ethereum) SetContractBackend(backend bind.ContractBackend) {
- // Pass the rpc client to les server if it is enabled.
- if s.lesServer != nil {
- s.lesServer.SetContractBackend(backend)
- }
-}
-
// New creates a new Ethereum object (including the
// initialisation of the common Ethereum object)
-func New(ctx *node.ServiceContext, config *Config,
+func New(stack *node.Node, config *Config,
cb *dummy.ConsensusCallbacks,
mcb *miner.MinerCallbacks,
bcb *BackendCallbacks,
@@ -138,7 +114,12 @@ func New(ctx *node.ServiceContext, config *Config,
config.Miner.GasPrice = new(big.Int).Set(DefaultConfig.Miner.GasPrice)
}
if config.NoPruning && config.TrieDirtyCache > 0 {
- config.TrieCleanCache += config.TrieDirtyCache
+ if config.SnapshotCache > 0 {
+ config.TrieCleanCache += config.TrieDirtyCache * 3 / 5
+ config.SnapshotCache += config.TrieDirtyCache * 2 / 5
+ } else {
+ config.TrieCleanCache += config.TrieDirtyCache
+ }
config.TrieDirtyCache = 0
}
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
@@ -151,26 +132,27 @@ func New(ctx *node.ServiceContext, config *Config,
return nil, err
}
}
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideIstanbul)
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis)
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
log.Info("Initialised chain configuration", "config", chainConfig)
eth := &Ethereum{
- config: config,
- chainDb: chainDb,
- eventMux: ctx.EventMux,
- accountManager: ctx.AccountManager,
- engine: CreateConsensusEngine(ctx, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb, cb),
- shutdownChan: make(chan bool),
- networkID: config.NetworkId,
- gasPrice: config.Miner.GasPrice,
- etherbase: config.Miner.Etherbase,
- bloomRequests: make(chan chan *bloombits.Retrieval),
- bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
- txSubmitChan: make(chan struct{}, 1),
- bcb: bcb,
+ config: config,
+ chainDb: chainDb,
+ eventMux: stack.EventMux(),
+ accountManager: stack.AccountManager(),
+ engine: CreateConsensusEngine(stack, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb, cb),
+ closeBloomHandler: make(chan struct{}),
+ networkID: config.NetworkId,
+ gasPrice: config.Miner.GasPrice,
+ etherbase: config.Miner.Etherbase,
+ bloomRequests: make(chan chan *bloombits.Retrieval),
+ bloomIndexer: NewBloomIndexer(chainDb, params.BloomBitsBlocks, params.BloomConfirms),
+ p2pServer: stack.Server(),
+ txSubmitChan: make(chan struct{}, 1),
+ bcb: bcb,
}
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
@@ -196,13 +178,16 @@ func New(ctx *node.ServiceContext, config *Config,
}
cacheConfig = &core.CacheConfig{
TrieCleanLimit: config.TrieCleanCache,
+ TrieCleanJournal: stack.ResolvePath(config.TrieCleanCacheJournal),
+ TrieCleanRejournal: config.TrieCleanCacheRejournal,
TrieCleanNoPrefetch: config.NoPrefetch,
TrieDirtyLimit: config.TrieDirtyCache,
TrieDirtyDisabled: config.NoPruning,
TrieTimeLimit: config.TrieTimeout,
+ SnapshotLimit: config.SnapshotCache,
}
)
- eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, config.ManualCanonical)
+ eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, config.ManualCanonical)
if err != nil {
return nil, err
}
@@ -215,12 +200,12 @@ func New(ctx *node.ServiceContext, config *Config,
eth.bloomIndexer.Start(eth.blockchain)
if config.TxPool.Journal != "" {
- config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal)
+ config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
}
eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
//// Permit the downloader to use the trie cache allowance during fast sync
- //cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit
+ //cacheLimit := cacheConfig.TrieCleanLimit + cacheConfig.TrieDirtyLimit + cacheConfig.SnapshotLimit
//checkpoint := config.Checkpoint
//if checkpoint == nil {
// checkpoint = params.TrustedCheckpoints[genesisHash]
@@ -231,13 +216,25 @@ func New(ctx *node.ServiceContext, config *Config,
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock, mcb)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
- eth.APIBackend = &EthAPIBackend{ctx.ExtRPCEnabled(), eth, nil}
+ eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.Miner.GasPrice
}
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
+ eth.dialCandidates, err = eth.setupDiscovery(&stack.Config().P2P)
+ if err != nil {
+ return nil, err
+ }
+
+ // Start the RPC service
+ eth.netRPCService = ethapi.NewPublicNetAPI(eth.p2pServer, eth.NetVersion())
+
+ // Register the backend on the node
+ stack.RegisterAPIs(eth.APIs())
+ stack.RegisterProtocols(eth.Protocols())
+ stack.RegisterLifecycle(eth)
return eth, nil
}
@@ -259,7 +256,7 @@ func makeExtraData(extra []byte) []byte {
}
// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
-func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, cb *dummy.ConsensusCallbacks) consensus.Engine {
+func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, cb *dummy.ConsensusCallbacks) consensus.Engine {
return dummy.NewDummyEngine(cb)
}
@@ -268,18 +265,9 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo
func (s *Ethereum) APIs() []rpc.API {
apis := ethapi.GetAPIs(s.APIBackend)
- // Append any APIs exposed explicitly by the les server
- if s.lesServer != nil {
- apis = append(apis, s.lesServer.APIs()...)
- }
// Append any APIs exposed explicitly by the consensus engine
apis = append(apis, s.engine.APIs(s.BlockChain())...)
- // Append any APIs exposed explicitly by the les server
- if s.lesServer != nil {
- apis = append(apis, s.lesServer.APIs()...)
- }
-
// Append all the local APIs and return
return append(apis, []rpc.API{
{
@@ -487,77 +475,68 @@ func (s *Ethereum) NetVersion() uint64 { return s.networkID }
func (s *Ethereum) Downloader() *downloader.Downloader { return nil } // s.protocolManager.downloader }
func (s *Ethereum) Synced() bool { return true } // atomic.LoadUint32(&s.protocolManager.acceptTxs) == 1 }
func (s *Ethereum) ArchiveMode() bool { return s.config.NoPruning }
+func (s *Ethereum) BloomIndexer() *core.ChainIndexer { return s.bloomIndexer }
-// Protocols implements node.Service, returning all the currently configured
+// Protocols returns all the currently configured
// network protocols to start.
func (s *Ethereum) Protocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(ProtocolVersions))
//for i, vsn := range ProtocolVersions {
// protos[i] = s.protocolManager.makeProtocol(vsn)
// protos[i].Attributes = []enr.Entry{s.currentEthEntry()}
- //}
- //if s.lesServer != nil {
- // protos = append(protos, s.lesServer.Protocols()...)
+ // protos[i].DialCandidates = s.dialCandidates
//}
return protos
}
-// Start implements node.Service, starting all internal goroutines needed by the
+// Start implements node.Lifecycle, starting all internal goroutines needed by the
// Ethereum protocol implementation.
-func (s *Ethereum) Start(srvr *p2p.Server) error {
- //s.startEthEntryUpdate(srvr.LocalNode())
+func (s *Ethereum) Start() error {
+ //s.startEthEntryUpdate(s.p2pServer.LocalNode())
// Start the bloom bits servicing goroutines
s.startBloomHandlers(params.BloomBitsBlocks)
- // Start the RPC service
- s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion())
-
// Figure out a max peers count based on the server limits
- maxPeers := srvr.MaxPeers
+ maxPeers := s.p2pServer.MaxPeers
if s.config.LightServ > 0 {
- if s.config.LightPeers >= srvr.MaxPeers {
- return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)
+ if s.config.LightPeers >= s.p2pServer.MaxPeers {
+ return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, s.p2pServer.MaxPeers)
}
maxPeers -= s.config.LightPeers
}
// Start the networking layer and the light server if requested
//s.protocolManager.Start(maxPeers)
- //if s.lesServer != nil {
- // s.lesServer.Start(srvr)
- //}
return nil
}
-// Stop implements node.Service, terminating all internal goroutines used by the
+// Stop implements node.Lifecycle, terminating all internal goroutines used by the
// Ethereum protocol.
func (s *Ethereum) Stop() error {
- s.bloomIndexer.Close()
- s.blockchain.Stop()
- s.engine.Close()
+ // Stop all the peer-related stuff first.
//s.protocolManager.Stop()
- //if s.lesServer != nil {
- // s.lesServer.Stop()
- //}
+
+ // Then stop everything else.
+ s.bloomIndexer.Close()
+ close(s.closeBloomHandler)
s.txPool.Stop()
s.miner.Stop()
- s.eventMux.Stop()
-
+ s.blockchain.Stop()
+ s.engine.Close()
s.chainDb.Close()
- close(s.shutdownChan)
+ s.eventMux.Stop()
return nil
}
func (s *Ethereum) StopPart() error {
s.bloomIndexer.Close()
- s.blockchain.Stop()
- s.engine.Close()
+ close(s.closeBloomHandler)
s.txPool.Stop()
s.miner.Stop()
- s.eventMux.Stop()
-
+ s.blockchain.Stop()
+ s.engine.Close()
s.chainDb.Close()
- close(s.shutdownChan)
+ s.eventMux.Stop()
return nil
}
diff --git a/hacked-list.txt b/hacked-list.txt
index 92d0517..7fe011b 100644
--- a/hacked-list.txt
+++ b/hacked-list.txt
@@ -13,7 +13,6 @@
./core/state_processor.go
./core/state/statedb.go
./core/state/state_object.go
-
./core/tx_pool.go
./core/types/block.go
./core/vm/errors.go
@@ -28,6 +27,7 @@
./eth/api.go
./eth/api_tracer.go
./eth/backend.go
+
./eth/config.go
./eth/gen_config.go
./eth/tracers/internal/tracers/assets.go