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
8379 8380 8381 8382 8383 8384 8385 8386 8387 8388 8389 8390 8391 8392 8393 8394 8395 8396 8397 8398 8399 8400 8401 8402 8403 8404 8405 8406 8407 8408 8409 8410 8411 8412 8413 8414 8415 8416 8417 8418 8419 8420 8421 8422 8423 8424 8425 8426 8427 8428 8429 8430 8431 8432 8433 8434 8435 8436 8437 8438 8439 8440 8441 8442 8443 8444 8445 8446 8447 8448 8449 8450 8451 8452 8453 8454 8455 8456 8457 8458 8459 8460 8461 8462 8463 8464 8465 8466 8467 8468 8469 8470 8471 8472 8473 8474 8475 8476 8477 8478 8479 8480 8481 8482 8483 8484 8485 8486 8487 8488 8489 8490 8491 8492 8493 8494 8495 8496 8497 8498 8499 8500 8501 8502 8503 8504 8505 8506 8507 8508 8509 8510 8511 8512 8513 8514 8515 8516 8517 8518 8519 8520 8521 8522 8523 8524 8525 8526 8527 8528 8529 8530 8531 8532 8533 8534 8535 8536 8537 8538 8539 8540 8541 8542 8543 8544 8545 8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 8579 8580 8581 8582 8583 8584 8585 8586 8587 8588 8589 8590 8591 8592 8593 8594 8595 8596 8597 8598 8599 8600 8601 8602 8603 8604 8605 8606 8607 8608 8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 8623 8624 8625 8626 8627 8628 8629 8630 8631 8632 8633 8634 8635 8636 8637 8638 8639 8640 8641 8642 8643 8644 8645 8646 8647 8648 8649 8650 8651 8652 8653 8654 8655 8656 8657 8658 8659 8660 8661 8662 8663 8664 8665 8666 8667 8668 8669 8670 8671 8672 8673 8674 8675 8676 8677 8678 8679 8680 8681 8682 8683 8684 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 8726 8727 8728 8729 8730 8731 8732 8733 8734 8735 8736 8737 8738 8739 8740 8741 8742 8743 8744 8745 8746 8747 8748 8749 8750 8751 8752 8753 8754 8755 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 8784 8785 8786 8787 8788 8789 8790 8791 8792 8793 8794 8795 8796 8797 8798 8799 8800 8801 8802 8803 8804 8805 8806 8807 8808 8809 8810 8811 8812 8813 8814 8815 8816 8817 8818 8819 8820 8821 8822 8823 8824 8825 8826 8827 8828 8829 8830 8831 8832 8833 8834 8835 8836 8837 8838 8839 8840 8841 8842 8843 8844 8845 8846 8847 8848 8849 8850 8851 8852 8853 8854 8855 8856 8857 8858 8859 8860 8861 8862 8863 8864 8865 8866 8867 8868 8869 8870 8871 8872 8873 8874 8875 8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 8894 8895 8896 8897 8898 8899 8900 8901 8902 8903 8904 8905 8906 8907 8908 8909 8910 8911 8912 8913 8914 8915 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 8943 8944 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 9130 9131 9132 9133 9134 9135 9136 9137 9138 9139 9140 9141 9142 9143 9144 9145 9146 9147 9148 9149 9150 9151 9152 9153 9154 9155 9156 9157 9158 9159 9160 9161 9162 9163 9164 9165 9166 9167 9168 9169 9170 9171 9172 9173 9174 9175 9176 9177 9178 9179 9180 9181 9182 9183 9184 9185 9186 9187 9188 9189 9190 9191 9192 9193 9194 9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 9213 9214 9215 9216 9217 9218 9219 9220 9221 9222 9223 9224 9225 9226 9227 9228 9229 9230 9231 9232 9233 9234 9235 9236 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 9264 9265 9266 9267 9268 9269 9270 9271 9272 9273 9274 9275 9276 9277 9278 9279 9280 9281 9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 9333 9334 9335 9336 9337 9338 9339 9340 9341 9342 9343 9344 9345 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372