aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/state/sync.go12
-rw-r--r--core/state/sync_test.go25
-rw-r--r--core/tx_pool.go105
-rw-r--r--core/tx_pool_test.go135
-rw-r--r--core/vm/gen_structlog.go26
-rw-r--r--core/vm/logger.go36
6 files changed, 259 insertions, 80 deletions
diff --git a/core/state/sync.go b/core/state/sync.go
index 8456a810b..2c29d706a 100644
--- a/core/state/sync.go
+++ b/core/state/sync.go
@@ -59,10 +59,16 @@ func (s *StateSync) Missing(max int) []common.Hash {
}
// Process injects a batch of retrieved trie nodes data, returning if something
-// was committed to the database and also the index of an entry if processing of
+// was committed to the memcache and also the index of an entry if processing of
// it failed.
-func (s *StateSync) Process(list []trie.SyncResult, dbw trie.DatabaseWriter) (bool, int, error) {
- return (*trie.TrieSync)(s).Process(list, dbw)
+func (s *StateSync) Process(list []trie.SyncResult) (bool, int, error) {
+ return (*trie.TrieSync)(s).Process(list)
+}
+
+// Commit flushes the data stored in the internal memcache out to persistent
+// storage, returning th enumber of items written and any occurred error.
+func (s *StateSync) Commit(dbw trie.DatabaseWriter) (int, error) {
+ return (*trie.TrieSync)(s).Commit(dbw)
}
// Pending returns the number of state entries currently pending for download.
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index 43d146e3a..108ebb320 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -138,9 +138,12 @@ func testIterativeStateSync(t *testing.T, batch int) {
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
- if _, index, err := sched.Process(results, dstDb); err != nil {
+ if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
+ if index, err := sched.Commit(dstDb); err != nil {
+ t.Fatalf("failed to commit data #%d: %v", index, err)
+ }
queue = append(queue[:0], sched.Missing(batch)...)
}
// Cross check that the two states are in sync
@@ -168,9 +171,12 @@ func TestIterativeDelayedStateSync(t *testing.T) {
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
- if _, index, err := sched.Process(results, dstDb); err != nil {
+ if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
+ if index, err := sched.Commit(dstDb); err != nil {
+ t.Fatalf("failed to commit data #%d: %v", index, err)
+ }
queue = append(queue[len(results):], sched.Missing(0)...)
}
// Cross check that the two states are in sync
@@ -206,9 +212,12 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
results = append(results, trie.SyncResult{Hash: hash, Data: data})
}
// Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results, dstDb); err != nil {
+ if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
+ if index, err := sched.Commit(dstDb); err != nil {
+ t.Fatalf("failed to commit data #%d: %v", index, err)
+ }
queue = make(map[common.Hash]struct{})
for _, hash := range sched.Missing(batch) {
queue[hash] = struct{}{}
@@ -249,9 +258,12 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
}
}
// Feed the retrieved results back and queue new tasks
- if _, index, err := sched.Process(results, dstDb); err != nil {
+ if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
+ if index, err := sched.Commit(dstDb); err != nil {
+ t.Fatalf("failed to commit data #%d: %v", index, err)
+ }
for _, hash := range sched.Missing(0) {
queue[hash] = struct{}{}
}
@@ -283,9 +295,12 @@ func TestIncompleteStateSync(t *testing.T) {
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
// Process each of the state nodes
- if _, index, err := sched.Process(results, dstDb); err != nil {
+ if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
}
+ if index, err := sched.Commit(dstDb); err != nil {
+ t.Fatalf("failed to commit data #%d: %v", index, err)
+ }
for _, result := range results {
added = append(added, result.Hash)
}
diff --git a/core/tx_pool.go b/core/tx_pool.go
index 04ffa8a98..2f3cd1e93 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -35,16 +35,41 @@ import (
)
var (
- // Transaction Pool Errors
- ErrInvalidSender = errors.New("invalid sender")
- ErrNonce = errors.New("nonce too low")
- ErrUnderpriced = errors.New("transaction underpriced")
+ // 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")
+
+ // ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
+ // with a different one without the required price bump.
ErrReplaceUnderpriced = errors.New("replacement transaction underpriced")
- ErrBalance = errors.New("insufficient balance")
- ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value")
- ErrIntrinsicGas = errors.New("intrinsic gas too low")
- ErrGasLimit = errors.New("exceeds block gas limit")
- ErrNegativeValue = errors.New("negative value")
+
+ // 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
+ // transaction with a negative value.
+ ErrNegativeValue = errors.New("negative value")
+
+ // ErrOversizedData is returned if the input data of a transaction is greater
+ // than some meaningful limit a user might use. This is not a consensus error
+ // making the transaction invalid, rather a DOS protection.
+ ErrOversizedData = errors.New("oversized data")
)
var (
@@ -54,16 +79,16 @@ var (
var (
// Metrics for the pending pool
- pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
- pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
- pendingRLCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
- pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
+ pendingDiscardCounter = metrics.NewCounter("txpool/pending/discard")
+ pendingReplaceCounter = metrics.NewCounter("txpool/pending/replace")
+ pendingRateLimitCounter = metrics.NewCounter("txpool/pending/ratelimit") // Dropped due to rate limiting
+ pendingNofundsCounter = metrics.NewCounter("txpool/pending/nofunds") // Dropped due to out-of-funds
// Metrics for the queued pool
- queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard")
- queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
- queuedRLCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
- queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
+ queuedDiscardCounter = metrics.NewCounter("txpool/queued/discard")
+ queuedReplaceCounter = metrics.NewCounter("txpool/queued/replace")
+ queuedRateLimitCounter = metrics.NewCounter("txpool/queued/ratelimit") // Dropped due to rate limiting
+ queuedNofundsCounter = metrics.NewCounter("txpool/queued/nofunds") // Dropped due to out-of-funds
// General tx metrics
invalidTxCounter = metrics.NewCounter("txpool/invalid")
@@ -374,7 +399,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
}
// Last but not least check for nonce errors
if currentState.GetNonce(from) > tx.Nonce() {
- return ErrNonce
+ return ErrNonceTooLow
}
// Check the transaction doesn't exceed the current
@@ -395,12 +420,15 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
if currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
return ErrInsufficientFunds
}
-
intrGas := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
if tx.Gas().Cmp(intrGas) < 0 {
return ErrIntrinsicGas
}
+ // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
+ if tx.Size() > 32*1024 {
+ return ErrOversizedData
+ }
return nil
}
@@ -638,8 +666,9 @@ func (pool *TxPool) removeTx(hash common.Hash) {
}
// Update the account nonce if needed
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
- pool.pendingState.SetNonce(addr, tx.Nonce())
+ pool.pendingState.SetNonce(addr, nonce)
}
+ return
}
}
// Transaction is in the future queue
@@ -696,10 +725,10 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
// Drop all transactions over the allowed limit
for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
hash := tx.Hash()
- log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
delete(pool.all, hash)
pool.priced.Removed()
- queuedRLCounter.Inc(1)
+ queuedRateLimitCounter.Inc(1)
+ log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
}
queued += uint64(list.Len())
@@ -745,7 +774,18 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
for i := 0; i < len(offenders)-1; i++ {
list := pool.pending[offenders[i]]
- list.Cap(list.Len() - 1)
+ for _, tx := range list.Cap(list.Len() - 1) {
+ // Drop the transaction from the global pools too
+ hash := tx.Hash()
+ delete(pool.all, hash)
+ pool.priced.Removed()
+
+ // Update the account nonce to the dropped transaction
+ if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
+ pool.pendingState.SetNonce(offenders[i], nonce)
+ }
+ log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
+ }
pending--
}
}
@@ -756,12 +796,23 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
for _, addr := range offenders {
list := pool.pending[addr]
- list.Cap(list.Len() - 1)
+ for _, tx := range list.Cap(list.Len() - 1) {
+ // Drop the transaction from the global pools too
+ hash := tx.Hash()
+ delete(pool.all, hash)
+ pool.priced.Removed()
+
+ // Update the account nonce to the dropped transaction
+ if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
+ pool.pendingState.SetNonce(addr, nonce)
+ }
+ log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
+ }
pending--
}
}
}
- pendingRLCounter.Inc(int64(pendingBeforeCap - pending))
+ pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
}
// If we've queued more transactions than the hard limit, drop oldest ones
if queued > pool.config.GlobalQueue {
@@ -785,7 +836,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
pool.removeTx(tx.Hash())
}
drop -= size
- queuedRLCounter.Inc(int64(size))
+ queuedRateLimitCounter.Inc(int64(size))
continue
}
// Otherwise drop only last few transactions
@@ -793,7 +844,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB, accounts []common.A
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
pool.removeTx(txs[i].Hash())
drop--
- queuedRLCounter.Inc(1)
+ queuedRateLimitCounter.Inc(1)
}
}
}
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index 94b07170d..4e28522e9 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -18,6 +18,7 @@ package core
import (
"crypto/ecdsa"
+ "fmt"
"math/big"
"math/rand"
"testing"
@@ -52,6 +53,35 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
return newPool, key
}
+// validateTxPoolInternals checks various consistency invariants within the pool.
+func validateTxPoolInternals(pool *TxPool) error {
+ pool.mu.RLock()
+ defer pool.mu.RUnlock()
+
+ // Ensure the total transaction set is consistent with pending + queued
+ pending, queued := pool.stats()
+ if total := len(pool.all); total != pending+queued {
+ return fmt.Errorf("total transaction count %d != %d pending + %d queued", total, pending, queued)
+ }
+ if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
+ return fmt.Errorf("total priced transaction count %d != %d pending + %d queued", priced, pending, queued)
+ }
+ // Ensure the next nonce to assign is the correct one
+ for addr, txs := range pool.pending {
+ // Find the last transaction
+ var last uint64
+ for nonce, _ := range txs.txs.items {
+ if last < nonce {
+ last = nonce
+ }
+ }
+ if nonce := pool.pendingState.GetNonce(addr); nonce != last+1 {
+ return fmt.Errorf("pending nonce mismatch: have %v, want %v", nonce, last+1)
+ }
+ }
+ return nil
+}
+
func deriveSender(tx *types.Transaction) (common.Address, error) {
return types.Sender(types.HomesteadSigner{}, tx)
}
@@ -150,8 +180,8 @@ func TestInvalidTransactions(t *testing.T) {
currentState.SetNonce(from, 1)
currentState.AddBalance(from, big.NewInt(0xffffffffffffff))
tx = transaction(0, big.NewInt(100000), key)
- if err := pool.Add(tx); err != ErrNonce {
- t.Error("expected", ErrNonce)
+ if err := pool.Add(tx); err != ErrNonceTooLow {
+ t.Error("expected", ErrNonceTooLow)
}
tx = transaction(1, big.NewInt(100000), key)
@@ -218,20 +248,25 @@ func TestTransactionQueue(t *testing.T) {
func TestRemoveTx(t *testing.T) {
pool, key := setupTxPool()
- tx := transaction(0, big.NewInt(100), key)
- from, _ := deriveSender(tx)
+ addr := crypto.PubkeyToAddress(key.PublicKey)
currentState, _ := pool.currentState()
- currentState.AddBalance(from, big.NewInt(1))
+ currentState.AddBalance(addr, big.NewInt(1))
+
+ tx1 := transaction(0, big.NewInt(100), key)
+ tx2 := transaction(2, big.NewInt(100), key)
+
+ pool.promoteTx(addr, tx1.Hash(), tx1)
+ pool.enqueueTx(tx2.Hash(), tx2)
- pool.enqueueTx(tx.Hash(), tx)
- pool.promoteTx(from, tx.Hash(), tx)
if len(pool.queue) != 1 {
t.Error("expected queue to be 1, got", len(pool.queue))
}
if len(pool.pending) != 1 {
t.Error("expected pending to be 1, got", len(pool.pending))
}
- pool.Remove(tx.Hash())
+ pool.Remove(tx1.Hash())
+ pool.Remove(tx2.Hash())
+
if len(pool.queue) > 0 {
t.Error("expected queue to be 0, got", len(pool.queue))
}
@@ -404,10 +439,10 @@ func TestTransactionDropping(t *testing.T) {
)
pool.promoteTx(account, tx0.Hash(), tx0)
pool.promoteTx(account, tx1.Hash(), tx1)
- pool.promoteTx(account, tx1.Hash(), tx2)
+ pool.promoteTx(account, tx2.Hash(), tx2)
pool.enqueueTx(tx10.Hash(), tx10)
pool.enqueueTx(tx11.Hash(), tx11)
- pool.enqueueTx(tx11.Hash(), tx12)
+ pool.enqueueTx(tx12.Hash(), tx12)
// Check that pre and post validations leave the pool as is
if pool.pending[account].Len() != 3 {
@@ -416,8 +451,8 @@ func TestTransactionDropping(t *testing.T) {
if pool.queue[account].Len() != 3 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
}
- if len(pool.all) != 4 {
- t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
+ if len(pool.all) != 6 {
+ t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
}
pool.resetState()
if pool.pending[account].Len() != 3 {
@@ -426,8 +461,8 @@ func TestTransactionDropping(t *testing.T) {
if pool.queue[account].Len() != 3 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 3)
}
- if len(pool.all) != 4 {
- t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
+ if len(pool.all) != 6 {
+ t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 6)
}
// Reduce the balance of the account, and check that invalidated transactions are dropped
state.AddBalance(account, big.NewInt(-650))
@@ -730,6 +765,12 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
if len(pool1.all) != len(pool2.all) {
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", len(pool1.all), len(pool2.all))
}
+ if err := validateTxPoolInternals(pool1); err != nil {
+ t.Errorf("pool 1 internal state corrupted: %v", err)
+ }
+ if err := validateTxPoolInternals(pool2); err != nil {
+ t.Errorf("pool 2 internal state corrupted: %v", err)
+ }
}
// Tests that if the transaction count belonging to multiple accounts go above
@@ -776,6 +817,45 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
if pending > int(DefaultTxPoolConfig.GlobalSlots) {
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, DefaultTxPoolConfig.GlobalSlots)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
+}
+
+// Tests that if transactions start being capped, transasctions are also removed from 'all'
+func TestTransactionCapClearsFromAll(t *testing.T) {
+ // Reduce the queue limits to shorten test time
+ defer func(old uint64) { DefaultTxPoolConfig.AccountSlots = old }(DefaultTxPoolConfig.AccountSlots)
+ defer func(old uint64) { DefaultTxPoolConfig.AccountQueue = old }(DefaultTxPoolConfig.AccountQueue)
+ defer func(old uint64) { DefaultTxPoolConfig.GlobalSlots = old }(DefaultTxPoolConfig.GlobalSlots)
+
+ DefaultTxPoolConfig.AccountSlots = 2
+ DefaultTxPoolConfig.AccountQueue = 2
+ DefaultTxPoolConfig.GlobalSlots = 8
+
+ // Create the pool to test the limit enforcement with
+ db, _ := ethdb.NewMemDatabase()
+ statedb, _ := state.New(common.Hash{}, db)
+
+ pool := NewTxPool(DefaultTxPoolConfig, params.TestChainConfig, new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
+ pool.resetState()
+
+ // Create a number of test accounts and fund them
+ state, _ := pool.currentState()
+
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ state.AddBalance(addr, big.NewInt(1000000))
+
+ txs := types.Transactions{}
+ for j := 0; j < int(DefaultTxPoolConfig.GlobalSlots)*2; j++ {
+ txs = append(txs, transaction(uint64(j), big.NewInt(100000), key))
+ }
+ // Import the batch and verify that limits have been enforced
+ pool.AddBatch(txs)
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
}
// Tests that if the transaction count belonging to multiple accounts go above
@@ -820,6 +900,9 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), DefaultTxPoolConfig.AccountSlots)
}
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
}
// Tests that setting the transaction pool gas price to a higher value correctly
@@ -867,6 +950,9 @@ func TestTransactionPoolRepricing(t *testing.T) {
if queued != 3 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
// Reprice the pool and check that underpriced transactions get dropped
pool.SetGasPrice(big.NewInt(2))
@@ -877,6 +963,9 @@ func TestTransactionPoolRepricing(t *testing.T) {
if queued != 3 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
// Check that we can't add the old transactions back
if err := pool.Add(pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[0])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
@@ -884,6 +973,9 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := pool.Add(pricedTransaction(2, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
// However we can add local underpriced transactions
tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(1), keys[2])
@@ -894,6 +986,9 @@ func TestTransactionPoolRepricing(t *testing.T) {
if pending, _ = pool.stats(); pending != 3 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 3)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
}
// Tests that when the pool reaches its global transaction limit, underpriced
@@ -945,6 +1040,9 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if queued != 1 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
// Ensure that adding an underpriced transaction on block limit fails
if err := pool.Add(pricedTransaction(0, big.NewInt(100000), big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
@@ -966,6 +1064,9 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
// Ensure that adding local transactions can push out even higher priced ones
tx := pricedTransaction(1, big.NewInt(100000), big.NewInt(0), keys[2])
@@ -980,6 +1081,9 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
}
// Tests that the pool rejects replacement transactions that don't meet the minimum
@@ -1041,6 +1145,9 @@ func TestTransactionReplacement(t *testing.T) {
if err := pool.Add(pricedTransaction(2, big.NewInt(100000), big.NewInt(threshold+1), key)); err != nil {
t.Fatalf("failed to replace original queued transaction: %v", err)
}
+ if err := validateTxPoolInternals(pool); err != nil {
+ t.Fatalf("pool internal state corrupted: %v", err)
+ }
}
// Benchmarks the speed of validating the contents of the pending queue of the
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 1c86b2256..88df942dc 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -18,12 +18,12 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
Gas math.HexOrDecimal64 `json:"gas"`
GasCost math.HexOrDecimal64 `json:"gasCost"`
Memory hexutil.Bytes `json:"memory"`
+ MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
Err error `json:"error"`
OpName string `json:"opName"`
- MemorySize int `json:"memSize"`
}
var enc StructLog
enc.Pc = s.Pc
@@ -31,6 +31,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Gas = math.HexOrDecimal64(s.Gas)
enc.GasCost = math.HexOrDecimal64(s.GasCost)
enc.Memory = s.Memory
+ enc.MemorySize = s.MemorySize
if s.Stack != nil {
enc.Stack = make([]*math.HexOrDecimal256, len(s.Stack))
for k, v := range s.Stack {
@@ -41,21 +42,21 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Depth = s.Depth
enc.Err = s.Err
enc.OpName = s.OpName()
- enc.MemorySize = s.MemorySize()
return json.Marshal(&enc)
}
func (s *StructLog) UnmarshalJSON(input []byte) error {
type StructLog struct {
- Pc *uint64 `json:"pc"`
- Op *OpCode `json:"op"`
- Gas *math.HexOrDecimal64 `json:"gas"`
- GasCost *math.HexOrDecimal64 `json:"gasCost"`
- Memory hexutil.Bytes `json:"memory"`
- Stack []*math.HexOrDecimal256 `json:"stack"`
- Storage map[common.Hash]common.Hash `json:"-"`
- Depth *int `json:"depth"`
- Err *error `json:"error"`
+ Pc *uint64 `json:"pc"`
+ Op *OpCode `json:"op"`
+ Gas *math.HexOrDecimal64 `json:"gas"`
+ GasCost *math.HexOrDecimal64 `json:"gasCost"`
+ Memory hexutil.Bytes `json:"memory"`
+ MemorySize *int `json:"memSize"`
+ Stack []*math.HexOrDecimal256 `json:"stack"`
+ Storage map[common.Hash]common.Hash `json:"-"`
+ Depth *int `json:"depth"`
+ Err *error `json:"error"`
}
var dec StructLog
if err := json.Unmarshal(input, &dec); err != nil {
@@ -76,6 +77,9 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
if dec.Memory != nil {
s.Memory = dec.Memory
}
+ if dec.MemorySize != nil {
+ s.MemorySize = *dec.MemorySize
+ }
if dec.Stack != nil {
s.Stack = make([]*big.Int, len(dec.Stack))
for k, v := range dec.Stack {
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 405ab169c..17a9c9ec3 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -54,35 +54,31 @@ type LogConfig struct {
// StructLog is emitted to the EVM each cycle and lists information about the current internal state
// prior to the execution of the statement.
type StructLog struct {
- Pc uint64 `json:"pc"`
- Op OpCode `json:"op"`
- Gas uint64 `json:"gas"`
- GasCost uint64 `json:"gasCost"`
- Memory []byte `json:"memory"`
- Stack []*big.Int `json:"stack"`
- Storage map[common.Hash]common.Hash `json:"-"`
- Depth int `json:"depth"`
- Err error `json:"error"`
+ Pc uint64 `json:"pc"`
+ Op OpCode `json:"op"`
+ Gas uint64 `json:"gas"`
+ GasCost uint64 `json:"gasCost"`
+ Memory []byte `json:"memory"`
+ MemorySize int `json:"memSize"`
+ Stack []*big.Int `json:"stack"`
+ Storage map[common.Hash]common.Hash `json:"-"`
+ Depth int `json:"depth"`
+ Err error `json:"error"`
}
// overrides for gencodec
type structLogMarshaling struct {
- Stack []*math.HexOrDecimal256
- Gas math.HexOrDecimal64
- GasCost math.HexOrDecimal64
- Memory hexutil.Bytes
- OpName string `json:"opName"`
- MemorySize int `json:"memSize"`
+ Stack []*math.HexOrDecimal256
+ Gas math.HexOrDecimal64
+ GasCost math.HexOrDecimal64
+ Memory hexutil.Bytes
+ OpName string `json:"opName"`
}
func (s *StructLog) OpName() string {
return s.Op.String()
}
-func (s *StructLog) MemorySize() int {
- return len(s.Memory)
-}
-
// Tracer is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
@@ -181,7 +177,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
}
}
// create a new snaptshot of the EVM.
- log := StructLog{pc, op, gas, cost, mem, stck, storage, env.depth, err}
+ log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, err}
l.logs = append(l.logs, log)
return nil