Rollup evm to geth v1.9.9 Muir Glacier (#3356)

* Rollup evm to geth v1.9.9 Muir Glacier

* fix go gen

* update intrinsic gas for istanbul

* Update statedb.Commit

* revert go gen result

* revert protobuf version

* update protobuf

* update go gen files

* set testnet epoch for evm upgrade
pull/3377/head
Rongjian Lan 4 years ago committed by GitHub
parent 1b66674f29
commit 4452b36ee4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      accounts/keystore/keystore_test.go
  2. 2
      api/proto/message/message.pb.go
  3. 2
      api/service/syncing/downloader/proto/downloader.pb.go
  4. 19
      core/rawdb/accessors_chain_test.go
  5. 7
      core/rawdb/accessors_indexes_test.go
  6. 5
      core/staking_verifier_test.go
  7. 104
      core/state/database.go
  8. 149
      core/state/dump.go
  9. 5
      core/state/managed_state_test.go
  10. 344
      core/state/state_object.go
  11. 78
      core/state/state_test.go
  12. 249
      core/state/statedb.go
  13. 337
      core/state/statedb_test.go
  14. 16
      core/state_transition.go
  15. 8
      core/tx_pool.go
  16. 33
      core/tx_pool_test.go
  17. 6
      core/types/tx_errorsink.go
  18. 30
      core/vm/common.go
  19. 197
      core/vm/contracts.go
  20. 185
      core/vm/contracts_test.go
  21. 92
      core/vm/eips.go
  22. 6
      core/vm/evm.go
  23. 18
      core/vm/gas.go
  24. 422
      core/vm/gas_table.go
  25. 96
      core/vm/gas_table_test.go
  26. 62
      core/vm/instructions.go
  27. 230
      core/vm/instructions_test.go
  28. 2
      core/vm/interface.go
  29. 101
      core/vm/interpreter.go
  30. 1438
      core/vm/jump_table.go
  31. 8
      core/vm/logger.go
  32. 4
      core/vm/memory.go
  33. 114
      core/vm/memory_table.go
  34. 18
      core/vm/opcodes.go
  35. 6
      core/vm/runtime/runtime.go
  36. 7
      core/vm/runtime/runtime_test.go
  37. 7
      core/vm/stack.go
  38. 34
      core/vm/stack_table.go
  39. 1
      core/vm/testdata/testcases_add.json
  40. 1
      core/vm/testdata/testcases_and.json
  41. 1
      core/vm/testdata/testcases_byte.json
  42. 1
      core/vm/testdata/testcases_div.json
  43. 1
      core/vm/testdata/testcases_eq.json
  44. 1
      core/vm/testdata/testcases_exp.json
  45. 1
      core/vm/testdata/testcases_gt.json
  46. 1
      core/vm/testdata/testcases_lt.json
  47. 1
      core/vm/testdata/testcases_mod.json
  48. 1
      core/vm/testdata/testcases_mul.json
  49. 1
      core/vm/testdata/testcases_or.json
  50. 1
      core/vm/testdata/testcases_sar.json
  51. 1
      core/vm/testdata/testcases_sdiv.json
  52. 1
      core/vm/testdata/testcases_sgt.json
  53. 1
      core/vm/testdata/testcases_shl.json
  54. 1
      core/vm/testdata/testcases_shr.json
  55. 1
      core/vm/testdata/testcases_signext.json
  56. 1
      core/vm/testdata/testcases_slt.json
  57. 1
      core/vm/testdata/testcases_smod.json
  58. 1
      core/vm/testdata/testcases_sub.json
  59. 1
      core/vm/testdata/testcases_xor.json
  60. 35
      go.mod
  61. 3
      hmy/bloombits.go
  62. 21
      internal/params/config.go
  63. 106
      internal/params/protocol_params.go
  64. 6
      internal/shardchain/dbfactory.go
  65. 1
      node/worker/worker.go
  66. 12
      node/worker/worker_test.go
  67. 5
      staking/slash/double-sign_test.go
  68. 2
      staking/types/messages.go
  69. 4
      test/chain/main.go
  70. 7
      test/chain/reward/main.go

@ -217,9 +217,9 @@ func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
new := NewPlaintextKeyStore newK := NewPlaintextKeyStore
if encrypted { if encrypted {
new = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) } newK = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
} }
return d, new(d) return d, newK(d)
} }

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.23.0 // protoc-gen-go v1.25.0
// protoc v3.12.3 // protoc v3.12.3
// source: message.proto // source: message.proto

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.23.0 // protoc-gen-go v1.25.0
// protoc v3.12.3 // protoc v3.12.3
// source: downloader.proto // source: downloader.proto

@ -21,8 +21,9 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
@ -33,7 +34,7 @@ import (
// Tests block header storage and retrieval operations. // Tests block header storage and retrieval operations.
func TestHeaderStorage(t *testing.T) { func TestHeaderStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
// Create a test header to move around the database and make sure it's really new // Create a test header to move around the database and make sure it's really new
header := blockfactory.NewTestHeader().With().Number(big.NewInt(42)).Extra([]byte("test header")).Header() header := blockfactory.NewTestHeader().With().Number(big.NewInt(42)).Extra([]byte("test header")).Header()
@ -66,7 +67,7 @@ func TestHeaderStorage(t *testing.T) {
// Tests block body storage and retrieval operations. // Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) { func TestBodyStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
// Create a test body to move around the database and make sure it's really new // Create a test body to move around the database and make sure it's really new
body := types.NewTestBody().With().Uncles([]*block.Header{blockfactory.NewTestHeader().With().Extra([]byte("test header")).Header()}).Body() body := types.NewTestBody().With().Uncles([]*block.Header{blockfactory.NewTestHeader().With().Extra([]byte("test header")).Header()}).Body()
@ -104,7 +105,7 @@ func TestBodyStorage(t *testing.T) {
// Tests block storage and retrieval operations. // Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) { func TestBlockStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
// Create a test block to move around the database and make sure it's really new // Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(blockfactory.NewTestHeader().With(). block := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().
@ -166,7 +167,7 @@ func TestBlockStorage(t *testing.T) {
// Tests that partial block contents don't get reassembled into full blocks. // Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) { func TestPartialBlockStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
block := types.NewBlockWithHeader(blockfactory.NewTestHeader().With(). block := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().
Extra([]byte("test block")). Extra([]byte("test block")).
TxHash(types.EmptyRootHash). TxHash(types.EmptyRootHash).
@ -199,7 +200,7 @@ func TestPartialBlockStorage(t *testing.T) {
// Tests block total difficulty storage and retrieval operations. // Tests block total difficulty storage and retrieval operations.
func TestTdStorage(t *testing.T) { func TestTdStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
// Create a test TD to move around the database and make sure it's really new // Create a test TD to move around the database and make sure it's really new
hash, td := common.Hash{}, big.NewInt(314) hash, td := common.Hash{}, big.NewInt(314)
@ -222,7 +223,7 @@ func TestTdStorage(t *testing.T) {
// Tests that canonical numbers can be mapped to hashes and retrieved. // Tests that canonical numbers can be mapped to hashes and retrieved.
func TestCanonicalMappingStorage(t *testing.T) { func TestCanonicalMappingStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
// Create a test canonical number and assinged hash to move around // Create a test canonical number and assinged hash to move around
hash, number := common.Hash{0: 0xff}, uint64(314) hash, number := common.Hash{0: 0xff}, uint64(314)
@ -245,7 +246,7 @@ func TestCanonicalMappingStorage(t *testing.T) {
// Tests that head headers and head blocks can be assigned, individually. // Tests that head headers and head blocks can be assigned, individually.
func TestHeadStorage(t *testing.T) { func TestHeadStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
blockHead := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().Extra([]byte("test block header")).Header()) blockHead := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().Extra([]byte("test block header")).Header())
blockFull := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().Extra([]byte("test block full")).Header()) blockFull := types.NewBlockWithHeader(blockfactory.NewTestHeader().With().Extra([]byte("test block full")).Header())
@ -280,7 +281,7 @@ func TestHeadStorage(t *testing.T) {
// Tests that receipts associated with a single block can be stored and retrieved. // Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(t *testing.T) { func TestBlockReceiptStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
receipt1 := &types.Receipt{ receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed, Status: types.ReceiptStatusFailed,

@ -20,11 +20,12 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
@ -41,7 +42,7 @@ var (
// Tests that positional lookup metadata can be stored and retrieved. // Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) { func TestLookupStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), 0, big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22}) tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), 0, big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
@ -116,7 +117,7 @@ func TestLookupStorage(t *testing.T) {
// Test that staking tx hash does not find a plain tx hash (and visa versa) within the same block // Test that staking tx hash does not find a plain tx hash (and visa versa) within the same block
func TestMixedLookupStorage(t *testing.T) { func TestMixedLookupStorage(t *testing.T) {
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
tx := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) tx := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
stx := sampleCreateValidatorStakingTxn() stx := sampleCreateValidatorStakingTxn()

@ -7,10 +7,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
consensus_engine "github.com/harmony-one/harmony/consensus/engine" consensus_engine "github.com/harmony-one/harmony/consensus/engine"
"github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/state"
@ -1559,7 +1560,7 @@ func makeVWrapperByIndex(index int) staking.ValidatorWrapper {
} }
func newTestStateDB() (*state.DB, error) { func newTestStateDB() (*state.DB, error) {
return state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) return state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
} }
// makeVWrappersForStake makes the default staking.ValidatorWrappers for // makeVWrappersForStake makes the default staking.ValidatorWrappers for

@ -18,7 +18,6 @@ package state
import ( import (
"fmt" "fmt"
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -59,28 +58,61 @@ type Database interface {
TrieDB() *trie.Database TrieDB() *trie.Database
} }
// Trie is a Ethereum Merkle Trie. // Trie is a Ethereum Merkle Patricia trie.
type Trie interface { type Trie interface {
// GetKey returns the sha3 preimage of a hashed key that was previously used
// to store a value.
//
// TODO(fjl): remove this when SecureTrie is removed
GetKey([]byte) []byte
// TryGet returns the value for key stored in the trie. The value bytes must
// not be modified by the caller. If a node was not found in the database, a
// trie.MissingNodeError is returned.
TryGet(key []byte) ([]byte, error) TryGet(key []byte) ([]byte, error)
// TryUpdate associates key with value in the trie. If value has length zero, any
// existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
TryUpdate(key, value []byte) error TryUpdate(key, value []byte) error
// TryDelete removes any existing value for key from the trie. If a node was not
// found in the database, a trie.MissingNodeError is returned.
TryDelete(key []byte) error TryDelete(key []byte) error
Commit(onleaf trie.LeafCallback) (common.Hash, error)
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
Hash() common.Hash Hash() common.Hash
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
Commit(onleaf trie.LeafCallback) (common.Hash, error)
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key.
NodeIterator(startKey []byte) trie.NodeIterator NodeIterator(startKey []byte) trie.NodeIterator
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error // Prove constructs a Merkle proof for key. The result contains all encoded nodes
// on the path to the value at key. The value itself is also included in the last
// node and can be retrieved by verifying the proof.
//
// If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root), ending
// with the node that proves the absence of the key.
Prove(key []byte, fromLevel uint, proofDb ethdb.KeyValueWriter) error
} }
// NewDatabase creates a backing store for state. The returned database is safe for // NewDatabase creates a backing store for state. The returned database is safe for
// concurrent use and retains a few recent expanded trie nodes in memory. To keep // concurrent use, but does not retain any recent trie nodes in memory. To keep some
// more historical state in memory, use the NewDatabaseWithCache constructor. // historical state in memory, use the NewDatabaseWithCache constructor.
func NewDatabase(db ethdb.Database) Database { func NewDatabase(db ethdb.Database) Database {
return NewDatabaseWithCache(db, 0) return NewDatabaseWithCache(db, 0)
} }
// NewDatabaseWithCache creates a backing store for state. The returned database is safe for // NewDatabaseWithCache creates a backing store for state. The returned database
// concurrent use and retains both a few recent expanded trie nodes in memory, as // is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
// well as a lot of collapsed RLP trie nodes in a large memory cache. // large memory cache.
func NewDatabaseWithCache(db ethdb.Database, cache int) Database { func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
csc, _ := lru.New(codeSizeCacheSize) csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{ return &cachingDB{
@ -91,50 +123,22 @@ func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
type cachingDB struct { type cachingDB struct {
db *trie.Database db *trie.Database
mu sync.Mutex
pastTries []*trie.SecureTrie
codeSizeCache *lru.Cache codeSizeCache *lru.Cache
} }
// OpenTrie opens the main account trie. // OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
db.mu.Lock() return trie.NewSecure(root, db.db)
defer db.mu.Unlock()
for i := len(db.pastTries) - 1; i >= 0; i-- {
if db.pastTries[i].Hash() == root {
return cachedTrie{db.pastTries[i].Copy(), db}, nil
}
}
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
if err != nil {
return nil, err
}
return cachedTrie{tr, db}, nil
}
func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
db.mu.Lock()
defer db.mu.Unlock()
if len(db.pastTries) >= maxPastTries {
copy(db.pastTries, db.pastTries[1:])
db.pastTries[len(db.pastTries)-1] = t
} else {
db.pastTries = append(db.pastTries, t)
}
} }
// OpenStorageTrie opens the storage trie of an account. // OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0) return trie.NewSecure(root, db.db)
} }
// CopyTrie returns an independent copy of the given trie. // CopyTrie returns an independent copy of the given trie.
func (db *cachingDB) CopyTrie(t Trie) Trie { func (db *cachingDB) CopyTrie(t Trie) Trie {
switch t := t.(type) { switch t := t.(type) {
case cachedTrie:
return cachedTrie{t.SecureTrie.Copy(), db}
case *trie.SecureTrie: case *trie.SecureTrie:
return t.Copy() return t.Copy()
default: default:
@ -164,21 +168,3 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
func (db *cachingDB) TrieDB() *trie.Database { func (db *cachingDB) TrieDB() *trie.Database {
return db.db return db.db
} }
// cachedTrie inserts its trie into a cachingDB on commit.
type cachedTrie struct {
*trie.SecureTrie
db *cachingDB
}
func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
root, err := m.SecureTrie.Commit(onleaf)
if err == nil {
m.db.pushTrie(m.SecureTrie)
}
return root, err
}
func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
return m.SecureTrie.Prove(key, fromLevel, proofDb)
}

@ -20,78 +20,143 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/harmony-one/harmony/internal/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
common2 "github.com/harmony-one/harmony/internal/common"
staking "github.com/harmony-one/harmony/staking/types"
) )
// DumpAccount ... // DumpAccount represents an account in the state
type DumpAccount struct { type DumpAccount struct {
Balance string `json:"balance"` Balance string `json:"balance"`
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
Root string `json:"root"` Root string `json:"root"`
CodeHash string `json:"codeHash"` CodeHash string `json:"codeHash"`
Code string `json:"code"` Code string `json:"code,omitempty"`
Storage map[string]string `json:"storage"` Storage map[common.Hash]string `json:"storage,omitempty"`
Address *common.Address `json:"address,omitempty"` // Address only present in iterative (line-by-line) mode
SecureKey hexutil.Bytes `json:"key,omitempty"` // If we don't have address, we can output the key
} }
// Dump ... // Dump represents the full dump in a collected format, as one large map
type Dump struct { type Dump struct {
Root string `json:"root"` Root string `json:"root"`
Accounts map[string]DumpAccount `json:"accounts"` Accounts map[common.Address]DumpAccount `json:"accounts"`
}
// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively
type iterativeDump struct {
*json.Encoder
} }
// RawDump ... // Collector interface which the state trie calls during iteration
func (db *DB) RawDump() Dump { type collector interface {
dump := Dump{ onRoot(common.Hash)
Root: fmt.Sprintf("%x", db.trie.Hash()), onAccount(common.Address, DumpAccount)
Accounts: make(map[string]DumpAccount), }
func (d *Dump) onRoot(root common.Hash) {
d.Root = fmt.Sprintf("%x", root)
}
func (d *Dump) onAccount(addr common.Address, account DumpAccount) {
d.Accounts[addr] = account
}
func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{
Balance: account.Balance,
Nonce: account.Nonce,
Root: account.Root,
CodeHash: account.CodeHash,
Code: account.Code,
Storage: account.Storage,
SecureKey: account.SecureKey,
Address: nil,
} }
if addr != (common.Address{}) {
dumpAccount.Address = &addr
}
d.Encode(dumpAccount)
}
func (d iterativeDump) onRoot(root common.Hash) {
d.Encode(struct {
Root common.Hash `json:"root"`
}{root})
}
it := trie.NewIterator(db.trie.NodeIterator(nil)) func (s *DB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
emptyAddress := (common.Address{})
missingPreimages := 0
c.onRoot(s.trie.Hash())
it := trie.NewIterator(s.trie.NodeIterator(nil))
for it.Next() { for it.Next() {
addr := db.trie.GetKey(it.Key)
var data Account var data Account
if err := rlp.DecodeBytes(it.Value, &data); err != nil { if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err) panic(err)
} }
obj := newObject(nil, common.BytesToAddress(addr), data) addr := common.BytesToAddress(s.trie.GetKey(it.Key))
var wrapper staking.ValidatorWrapper obj := newObject(nil, addr, data)
wrap := ""
if err := rlp.DecodeBytes(obj.Code(db.db), &wrapper); err != nil {
//
} else {
marsh, err := json.Marshal(wrapper)
if err == nil {
wrap = string(marsh)
}
}
account := DumpAccount{ account := DumpAccount{
Balance: data.Balance.String(), Balance: data.Balance.String(),
Nonce: data.Nonce, Nonce: data.Nonce,
Root: common.Bytes2Hex(data.Root[:]), Root: common.Bytes2Hex(data.Root[:]),
CodeHash: common.Bytes2Hex(data.CodeHash), CodeHash: common.Bytes2Hex(data.CodeHash),
Code: wrap,
Storage: make(map[string]string),
} }
storageIt := trie.NewIterator(obj.getTrie(db.db).NodeIterator(nil)) if emptyAddress == addr {
for storageIt.Next() { // Preimage missing
account.Storage[common.Bytes2Hex(db.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value) missingPreimages++
if excludeMissingPreimages {
continue
}
account.SecureKey = it.Key
}
if !excludeCode {
account.Code = common.Bytes2Hex(obj.Code(s.db))
} }
dump.Accounts[common2.MustAddressToBech32(common.BytesToAddress(addr))] = account if !excludeStorage {
account.Storage = make(map[common.Hash]string)
storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
for storageIt.Next() {
_, content, _, err := rlp.Split(storageIt.Value)
if err != nil {
utils.Logger().Err(err).Msg("Failed to decode the value returned by iterator")
continue
}
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
}
}
c.onAccount(addr, account)
}
if missingPreimages > 0 {
utils.Logger().Warn().Int("missing", missingPreimages).Msg("Dump incomplete due to missing preimages")
}
}
// RawDump returns the entire state an a single large object
func (s *DB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
dump := &Dump{
Accounts: make(map[common.Address]DumpAccount),
} }
return dump s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages)
return *dump
} }
// Dump ... // Dump returns a JSON string representing the entire state as a single json-object
func (db *DB) Dump() string { func (s *DB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
json, err := json.MarshalIndent(db.RawDump(), "", " ") dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
json, err := json.MarshalIndent(dump, "", " ")
if err != nil { if err != nil {
fmt.Println("dump err", err) fmt.Println("dump err", err)
} }
return json
}
return string(json) // IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
func (s *DB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages)
} }

@ -19,14 +19,15 @@ package state
import ( import (
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
) )
var addr = common.BytesToAddress([]byte("test")) var addr = common.BytesToAddress([]byte("test"))
func create() (*ManagedState, *account) { func create() (*ManagedState, *account) {
statedb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) statedb, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
ms := ManageState(statedb) ms := ManageState(statedb)
ms.DB.SetNonce(addr, 100) ms.DB.SetNonce(addr, 100)
ms.accounts[addr] = newAccount(ms.DB.getStateObject(addr)) ms.accounts[addr] = newAccount(ms.DB.getStateObject(addr))

@ -21,6 +21,9 @@ import (
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -34,15 +37,15 @@ var emptyCodeHash = crypto.Keccak256(nil)
// Code ... // Code ...
type Code []byte type Code []byte
func (code Code) String() string { func (c Code) String() string {
return string(code) //strings.Join(Disassemble(so), " ") return string(c) //strings.Join(Disassemble(c), " ")
} }
// Storage ... // Storage ...
type Storage map[common.Hash]common.Hash type Storage map[common.Hash]common.Hash
func (storage Storage) String() (str string) { func (s Storage) String() (str string) {
for key, value := range storage { for key, value := range s {
str += fmt.Sprintf("%X : %X\n", key, value) str += fmt.Sprintf("%X : %X\n", key, value)
} }
@ -50,9 +53,9 @@ func (storage Storage) String() (str string) {
} }
// Copy ... // Copy ...
func (storage Storage) Copy() Storage { func (s Storage) Copy() Storage {
cpy := make(Storage) cpy := make(Storage)
for key, value := range storage { for key, value := range s {
cpy[key] = value cpy[key] = value
} }
@ -75,15 +78,17 @@ type Object struct {
// State objects are used by the consensus core and VM which are // State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs // unable to deal with database-level errors. Any error that occurs
// during a database read is memoized here and will eventually be returned // during a database read is memoized here and will eventually be returned
// by DB.Commit. // by StateDB.Commit.
dbErr error dbErr error
// Write caches. // Write caches.
trie Trie // storage trie, which becomes non-nil on first access trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded code Code // contract bytecode, which gets set when code is loaded
originStorage Storage // Storage cache of original entries to dedup rewrites originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
dirtyStorage Storage // Storage entries that need to be flushed to disk pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.
// Cache flags. // Cache flags.
// When an object is marked suicided it will be delete from the trie // When an object is marked suicided it will be delete from the trie
@ -94,8 +99,8 @@ type Object struct {
} }
// empty returns whether the account is considered empty. // empty returns whether the account is considered empty.
func (so *Object) empty() bool { func (s *Object) empty() bool {
return so.data.Nonce == 0 && so.data.Balance.Sign() == 0 && bytes.Equal(so.data.CodeHash, emptyCodeHash) return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
} }
// Account is the Ethereum consensus representation of accounts. // Account is the Ethereum consensus representation of accounts.
@ -115,205 +120,276 @@ func newObject(db *DB, address common.Address, data Account) *Object {
if data.CodeHash == nil { if data.CodeHash == nil {
data.CodeHash = emptyCodeHash data.CodeHash = emptyCodeHash
} }
if data.Root == (common.Hash{}) {
data.Root = emptyRoot
}
return &Object{ return &Object{
db: db, db: db,
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]), addrHash: crypto.Keccak256Hash(address[:]),
data: data, data: data,
originStorage: make(Storage), originStorage: make(Storage),
dirtyStorage: make(Storage), pendingStorage: make(Storage),
dirtyStorage: make(Storage),
} }
} }
// EncodeRLP implements rlp.Encoder. // EncodeRLP implements rlp.Encoder.
func (so *Object) EncodeRLP(w io.Writer) error { func (s *Object) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, so.data) return rlp.Encode(w, s.data)
} }
// setError remembers the first non-nil error it is called with. // setError remembers the first non-nil error it is called with.
func (so *Object) setError(err error) { func (s *Object) setError(err error) {
if so.dbErr == nil { if s.dbErr == nil {
so.dbErr = err s.dbErr = err
} }
} }
func (so *Object) markSuicided() { func (s *Object) markSuicided() {
so.suicided = true s.suicided = true
} }
func (so *Object) touch() { func (s *Object) touch() {
so.db.journal.append(touchChange{ s.db.journal.append(touchChange{
account: &so.address, account: &s.address,
}) })
if so.address == ripemd { if s.address == ripemd {
// Explicitly put it in the dirty-cache, which is otherwise generated from // Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals. // flattened journals.
so.db.journal.dirty(so.address) s.db.journal.dirty(s.address)
} }
} }
func (so *Object) getTrie(db Database) Trie { func (s *Object) getTrie(db Database) Trie {
if so.trie == nil { if s.trie == nil {
var err error var err error
so.trie, err = db.OpenStorageTrie(so.addrHash, so.data.Root) s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
if err != nil { if err != nil {
so.trie, _ = db.OpenStorageTrie(so.addrHash, common.Hash{}) s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{})
so.setError(fmt.Errorf("can't create storage trie: %v", err)) s.setError(fmt.Errorf("can't create storage trie: %v", err))
} }
} }
return so.trie return s.trie
} }
// GetState retrieves a value from the account storage trie. // GetState retrieves a value from the account storage trie.
func (so *Object) GetState(db Database, key common.Hash) common.Hash { func (s *Object) GetState(db Database, key common.Hash) common.Hash {
// If the fake storage is set, only lookup the state here(in the debugging mode)
if s.fakeStorage != nil {
return s.fakeStorage[key]
}
// If we have a dirty value for this state entry, return it // If we have a dirty value for this state entry, return it
value, dirty := so.dirtyStorage[key] value, dirty := s.dirtyStorage[key]
if dirty { if dirty {
return value return value
} }
// Otherwise return the entry's original value // Otherwise return the entry's original value
return so.GetCommittedState(db, key) return s.GetCommittedState(db, key)
} }
// GetCommittedState retrieves a value from the committed account storage trie. // GetCommittedState retrieves a value from the committed account storage trie.
func (so *Object) GetCommittedState(db Database, key common.Hash) common.Hash { func (s *Object) GetCommittedState(db Database, key common.Hash) common.Hash {
// If we have the original value cached, return that // If the fake storage is set, only lookup the state here(in the debugging mode)
value, cached := so.originStorage[key] if s.fakeStorage != nil {
if cached { return s.fakeStorage[key]
}
// If we have a pending write or clean cached, return that
if value, pending := s.pendingStorage[key]; pending {
return value
}
if value, cached := s.originStorage[key]; cached {
return value return value
} }
// Track the amount of time wasted on reading the storage trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now())
}
// Otherwise load the value from the database // Otherwise load the value from the database
enc, err := so.getTrie(db).TryGet(key[:]) enc, err := s.getTrie(db).TryGet(key[:])
if err != nil { if err != nil {
so.setError(err) s.setError(err)
return common.Hash{} return common.Hash{}
} }
var value common.Hash
if len(enc) > 0 { if len(enc) > 0 {
_, content, _, err := rlp.Split(enc) _, content, _, err := rlp.Split(enc)
if err != nil { if err != nil {
so.setError(err) s.setError(err)
} }
value.SetBytes(content) value.SetBytes(content)
} }
so.originStorage[key] = value s.originStorage[key] = value
return value return value
} }
// SetState updates a value in account storage. // SetState updates a value in account storage.
// to remove, set value to common.Hash{} func (s *Object) SetState(db Database, key, value common.Hash) {
func (so *Object) SetState(db Database, key, value common.Hash) { // If the fake storage is set, put the temporary state update here.
if s.fakeStorage != nil {
s.fakeStorage[key] = value
return
}
// If the new value is the same as old, don't set // If the new value is the same as old, don't set
prev := so.GetState(db, key) prev := s.GetState(db, key)
if prev == value { if prev == value {
return return
} }
// New value is different, update and journal the change // New value is different, update and journal the change
so.db.journal.append(storageChange{ s.db.journal.append(storageChange{
account: &so.address, account: &s.address,
key: key, key: key,
prevalue: prev, prevalue: prev,
}) })
so.setState(key, value) s.setState(key, value)
} }
func (so *Object) setState(key, value common.Hash) { // SetStorage replaces the entire state storage with the given one.
so.dirtyStorage[key] = value //
// After this function is called, all original state will be ignored and state
// lookup only happens in the fake state storage.
//
// Note this function should only be used for debugging purpose.
func (s *Object) SetStorage(storage map[common.Hash]common.Hash) {
// Allocate fake storage if it's nil.
if s.fakeStorage == nil {
s.fakeStorage = make(Storage)
}
for key, value := range storage {
s.fakeStorage[key] = value
}
// Don't bother journal since this function should only be used for
// debugging and the `fake` storage won't be committed to database.
}
func (s *Object) setState(key, value common.Hash) {
s.dirtyStorage[key] = value
}
// finalise moves all dirty storage slots into the pending area to be hashed or
// committed later. It is invoked at the end of every transaction.
func (s *Object) finalise() {
for key, value := range s.dirtyStorage {
s.pendingStorage[key] = value
}
if len(s.dirtyStorage) > 0 {
s.dirtyStorage = make(Storage)
}
} }
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.
func (so *Object) updateTrie(db Database) Trie { func (s *Object) updateTrie(db Database) Trie {
tr := so.getTrie(db) // Make sure all dirty slots are finalized into the pending storage area
for key, value := range so.dirtyStorage { s.finalise()
delete(so.dirtyStorage, key)
// Track the amount of time wasted on updating the storge trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
}
// Insert all the pending updates into the trie
tr := s.getTrie(db)
for key, value := range s.pendingStorage {
// Skip noop changes, persist actual changes // Skip noop changes, persist actual changes
if value == so.originStorage[key] { if value == s.originStorage[key] {
continue continue
} }
so.originStorage[key] = value s.originStorage[key] = value
if (value == common.Hash{}) { if (value == common.Hash{}) {
so.setError(tr.TryDelete(key[:])) s.setError(tr.TryDelete(key[:]))
continue continue
} }
// Encoding []byte cannot fail, ok to ignore the error. // Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) v, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
so.setError(tr.TryUpdate(key[:], v)) s.setError(tr.TryUpdate(key[:], v))
}
if len(s.pendingStorage) > 0 {
s.pendingStorage = make(Storage)
} }
return tr return tr
} }
// UpdateRoot sets the trie root to the current root hash of // UpdateRoot sets the trie root to the current root hash of
func (so *Object) updateRoot(db Database) { func (s *Object) updateRoot(db Database) {
so.updateTrie(db) s.updateTrie(db)
so.data.Root = so.trie.Hash()
// Track the amount of time wasted on hashing the storge trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now())
}
s.data.Root = s.trie.Hash()
} }
// CommitTrie the storage trie of the object to db. // CommitTrie the storage trie of the object to db.
// This updates the trie root. // This updates the trie root.
func (so *Object) CommitTrie(db Database) error { func (s *Object) CommitTrie(db Database) error {
so.updateTrie(db) s.updateTrie(db)
if so.dbErr != nil { if s.dbErr != nil {
return so.dbErr return s.dbErr
}
// Track the amount of time wasted on committing the storge trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
} }
root, err := so.trie.Commit(nil) root, err := s.trie.Commit(nil)
if err == nil { if err == nil {
so.data.Root = root s.data.Root = root
} }
return err return err
} }
// AddBalance removes amount from c's balance. // AddBalance removes amount from c's balance.
// It is used to add funds to the destination account of a transfer. // It is used to add funds to the destination account of a transfer.
func (so *Object) AddBalance(amount *big.Int) { func (s *Object) AddBalance(amount *big.Int) {
// EIP158: We must check emptiness for the objects such that the account // EIP158: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect. // clearing (0,0,0 objects) can take effect.
if amount.Sign() == 0 { if amount.Sign() == 0 {
if so.empty() { if s.empty() {
so.touch() s.touch()
} }
return return
} }
so.SetBalance(new(big.Int).Add(so.Balance(), amount)) s.SetBalance(new(big.Int).Add(s.Balance(), amount))
} }
// SubBalance removes amount from c's balance. // SubBalance removes amount from c's balance.
// It is used to remove funds from the origin account of a transfer. // It is used to remove funds from the origin account of a transfer.
func (so *Object) SubBalance(amount *big.Int) { func (s *Object) SubBalance(amount *big.Int) {
if amount.Sign() == 0 { if amount.Sign() == 0 {
return return
} }
so.SetBalance(new(big.Int).Sub(so.Balance(), amount)) s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
} }
// SetBalance ... // SetBalance ...
func (so *Object) SetBalance(amount *big.Int) { func (s *Object) SetBalance(amount *big.Int) {
so.db.journal.append(balanceChange{ s.db.journal.append(balanceChange{
account: &so.address, account: &s.address,
prev: new(big.Int).Set(so.data.Balance), prev: new(big.Int).Set(s.data.Balance),
}) })
so.setBalance(amount) s.setBalance(amount)
} }
func (so *Object) setBalance(amount *big.Int) { func (s *Object) setBalance(amount *big.Int) {
so.data.Balance = amount s.data.Balance = amount
} }
// ReturnGas returns the gas back to the origin. Used by the Virtual machine or Closures // ReturnGas the gas back to the origin. Used by the Virtual machine or Closures
func (so *Object) ReturnGas(gas *big.Int) {} func (s *Object) ReturnGas(gas *big.Int) {}
func (so *Object) deepCopy(db *DB) *Object { func (s *Object) deepCopy(db *DB) *Object {
stateObject := newObject(db, so.address, so.data) stateObject := newObject(db, s.address, s.data)
if so.trie != nil { if s.trie != nil {
stateObject.trie = db.db.CopyTrie(so.trie) stateObject.trie = db.db.CopyTrie(s.trie)
} }
stateObject.code = so.code stateObject.code = s.code
stateObject.dirtyStorage = so.dirtyStorage.Copy() stateObject.dirtyStorage = s.dirtyStorage.Copy()
stateObject.originStorage = so.originStorage.Copy() stateObject.originStorage = s.originStorage.Copy()
stateObject.suicided = so.suicided stateObject.pendingStorage = s.pendingStorage.Copy()
stateObject.dirtyCode = so.dirtyCode stateObject.suicided = s.suicided
stateObject.deleted = so.deleted stateObject.dirtyCode = s.dirtyCode
stateObject.deleted = s.deleted
return stateObject return stateObject
} }
@ -322,80 +398,80 @@ func (so *Object) deepCopy(db *DB) *Object {
// //
// Address returns the address of the contract/account // Address returns the address of the contract/account
func (so *Object) Address() common.Address { func (s *Object) Address() common.Address {
return so.address return s.address
} }
// Code returns the contract code associated with this object, if any. // Code returns the contract code associated with this object, if any.
func (so *Object) Code(db Database) []byte { func (s *Object) Code(db Database) []byte {
if so.code != nil { if s.code != nil {
return so.code return s.code
} }
if bytes.Equal(so.CodeHash(), emptyCodeHash) { if bytes.Equal(s.CodeHash(), emptyCodeHash) {
return nil return nil
} }
code, err := db.ContractCode(so.addrHash, common.BytesToHash(so.CodeHash())) code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash()))
if err != nil { if err != nil {
so.setError(fmt.Errorf("can't load code hash %x: %v", so.CodeHash(), err)) s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err))
} }
so.code = code s.code = code
return code return code
} }
// SetCode ... // SetCode ...
func (so *Object) SetCode(codeHash common.Hash, code []byte) { func (s *Object) SetCode(codeHash common.Hash, code []byte) {
prevcode := so.Code(so.db.db) prevcode := s.Code(s.db.db)
so.db.journal.append(codeChange{ s.db.journal.append(codeChange{
account: &so.address, account: &s.address,
prevhash: so.CodeHash(), prevhash: s.CodeHash(),
prevcode: prevcode, prevcode: prevcode,
}) })
so.setCode(codeHash, code) s.setCode(codeHash, code)
} }
func (so *Object) setCode(codeHash common.Hash, code []byte) { func (s *Object) setCode(codeHash common.Hash, code []byte) {
so.code = code s.code = code
so.data.CodeHash = codeHash[:] s.data.CodeHash = codeHash[:]
so.dirtyCode = true s.dirtyCode = true
} }
// SetNonce ... // SetNonce ...
func (so *Object) SetNonce(nonce uint64) { func (s *Object) SetNonce(nonce uint64) {
so.db.journal.append(nonceChange{ s.db.journal.append(nonceChange{
account: &so.address, account: &s.address,
prev: so.data.Nonce, prev: s.data.Nonce,
}) })
so.setNonce(nonce) s.setNonce(nonce)
} }
func (so *Object) setNonce(nonce uint64) { func (s *Object) setNonce(nonce uint64) {
so.data.Nonce = nonce s.data.Nonce = nonce
} }
// CodeHash ... // CodeHash ...
func (so *Object) CodeHash() []byte { func (s *Object) CodeHash() []byte {
return so.data.CodeHash return s.data.CodeHash
} }
// Balance ... // Balance ...
func (so *Object) Balance() *big.Int { func (s *Object) Balance() *big.Int {
return so.data.Balance return s.data.Balance
} }
// Nonce ... // Nonce ...
func (so *Object) Nonce() uint64 { func (s *Object) Nonce() uint64 {
return so.data.Nonce return s.data.Nonce
} }
// Value never called, but must be present to allow Object to be used // Value Never called, but must be present to allow stateObject to be used
// as a vm.Account interface that also satisfies the vm.ContractRef // as a vm.Account interface that also satisfies the vm.ContractRef
// interface. Interfaces are awesome. // interface. Interfaces are awesome.
func (so *Object) Value() *big.Int { func (s *Object) Value() *big.Int {
panic("Value on Object should never be called") panic("Value on stateObject should never be called")
} }
// IsValidator checks whether it is a validator object // IsValidator checks whether it is a validator object
func (so *Object) IsValidator(db Database) bool { func (s *Object) IsValidator(db Database) bool {
value := so.GetState(db, staking.IsValidatorKey) value := s.GetState(db, staking.IsValidatorKey)
return value != (common.Hash{}) return value != (common.Hash{})
} }

@ -21,22 +21,29 @@ import (
"math/big" "math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
checker "gopkg.in/check.v1"
) )
type StateSuite struct { var toAddr = common.BytesToAddress
db *ethdb.MemDatabase
type stateTest struct {
db ethdb.Database
state *DB state *DB
} }
var _ = checker.Suite(&StateSuite{}) func newStateTest() *stateTest {
db := rawdb.NewMemoryDatabase()
sdb, _ := New(common.Hash{}, NewDatabase(db))
return &stateTest{db: db, state: sdb}
}
var toAddr = common.BytesToAddress func TestDump(t *testing.T) {
s := newStateTest()
func (s *StateSuite) TestDump(c *checker.C) {
// generate a few entries // generate a few entries
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01})) obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
obj1.AddBalance(big.NewInt(22)) obj1.AddBalance(big.NewInt(22))
@ -51,47 +58,38 @@ func (s *StateSuite) TestDump(c *checker.C) {
s.state.Commit(false) s.state.Commit(false)
// check that dump contains the state objects that are in trie // check that dump contains the state objects that are in trie
got := string(s.state.Dump()) got := string(s.state.Dump(false, false, true))
want := `{ want := `{
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
"accounts": { "accounts": {
"0000000000000000000000000000000000000001": { "0x0000000000000000000000000000000000000001": {
"balance": "22", "balance": "22",
"nonce": 0, "nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
"code": "",
"storage": {}
}, },
"0000000000000000000000000000000000000002": { "0x0000000000000000000000000000000000000002": {
"balance": "44", "balance": "44",
"nonce": 0, "nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
"code": "",
"storage": {}
}, },
"0000000000000000000000000000000000000102": { "0x0000000000000000000000000000000000000102": {
"balance": "0", "balance": "0",
"nonce": 0, "nonce": 0,
"root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"codeHash": "87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3", "codeHash": "87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
"code": "03030303030303", "code": "03030303030303"
"storage": {}
} }
} }
}` }`
if got != want { if got != want {
c.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want) t.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want)
} }
} }
func (s *StateSuite) SetUpTest(c *checker.C) { func TestNull(t *testing.T) {
s.db = ethdb.NewMemDatabase() s := newStateTest()
s.state, _ = New(common.Hash{}, NewDatabase(s.db))
}
func (s *StateSuite) TestNull(c *checker.C) {
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
s.state.CreateAccount(address) s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
@ -101,18 +99,19 @@ func (s *StateSuite) TestNull(c *checker.C) {
s.state.Commit(false) s.state.Commit(false)
if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty current value, got %x", value) t.Errorf("expected empty current value, got %x", value)
} }
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) { if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty committed value, got %x", value) t.Errorf("expected empty committed value, got %x", value)
} }
} }
func (s *StateSuite) TestSnapshot(c *checker.C) { func TestSnapshot(t *testing.T) {
stateobjaddr := toAddr([]byte("aa")) stateobjaddr := toAddr([]byte("aa"))
var storageaddr common.Hash var storageaddr common.Hash
data1 := common.BytesToHash([]byte{42}) data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43}) data2 := common.BytesToHash([]byte{43})
s := newStateTest()
// snapshot the genesis state // snapshot the genesis state
genesis := s.state.Snapshot() genesis := s.state.Snapshot()
@ -125,23 +124,30 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
s.state.SetState(stateobjaddr, storageaddr, data2) s.state.SetState(stateobjaddr, storageaddr, data2)
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1) if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 {
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{}) t.Errorf("wrong storage value %v, want %v", v, data1)
}
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
}
// revert up to the genesis state and ensure correct content // revert up to the genesis state and ensure correct content
s.state.RevertToSnapshot(genesis) s.state.RevertToSnapshot(genesis)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{}) if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) {
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{}) t.Errorf("wrong storage value %v, want %v", v, common.Hash{})
}
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
}
} }
func (s *StateSuite) TestSnapshotEmpty(c *checker.C) { func TestSnapshotEmpty(t *testing.T) {
s := newStateTest()
s.state.RevertToSnapshot(s.state.Snapshot()) s.state.RevertToSnapshot(s.state.Snapshot())
} }
// use testing instead of checker because checker does not support
// printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) { func TestSnapshot2(t *testing.T) {
state, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
stateobjaddr0 := toAddr([]byte("so0")) stateobjaddr0 := toAddr([]byte("so0"))
stateobjaddr1 := toAddr([]byte("so1")) stateobjaddr1 := toAddr([]byte("so1"))

@ -21,6 +21,9 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"sort" "sort"
"time"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -43,8 +46,8 @@ type revision struct {
} }
var ( var (
// emptyState is the known hash of an empty state trie entry. // emptyRoot is the known root hash of an empty trie.
emptyState = crypto.Keccak256Hash(nil) emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode. // emptyCode is the known hash of the empty EVM bytecode.
emptyCode = crypto.Keccak256Hash(nil) emptyCode = crypto.Keccak256Hash(nil)
@ -57,6 +60,10 @@ func (n *proofList) Put(key []byte, value []byte) error {
return nil return nil
} }
func (n *proofList) Delete(key []byte) error {
panic("not supported")
}
// DB within the ethereum protocol are used to store anything // DB within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing // within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve: // nested states. It's the general query interface to retrieve:
@ -67,9 +74,10 @@ type DB struct {
trie Trie trie Trie
// This map holds 'live' objects, which will get modified while processing a state transition. // This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects map[common.Address]*Object stateObjects map[common.Address]*Object
stateObjectsDirty map[common.Address]struct{} stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
stateValidators map[common.Address]*stk.ValidatorWrapper stateObjectsDirty map[common.Address]struct{}
stateValidators map[common.Address]*stk.ValidatorWrapper
// DB error. // DB error.
// State objects are used by the consensus core and VM which are // State objects are used by the consensus core and VM which are
@ -93,6 +101,16 @@ type DB struct {
journal *journal journal *journal
validRevisions []revision validRevisions []revision
nextRevisionID int nextRevisionID int
// Measurements gathered during execution for debugging purposes
AccountReads time.Duration
AccountHashes time.Duration
AccountUpdates time.Duration
AccountCommits time.Duration
StorageReads time.Duration
StorageHashes time.Duration
StorageUpdates time.Duration
StorageCommits time.Duration
} }
// New creates a new state from a given trie. // New creates a new state from a given trie.
@ -102,14 +120,15 @@ func New(root common.Hash, db Database) (*DB, error) {
return nil, err return nil, err
} }
return &DB{ return &DB{
db: db, db: db,
trie: tr, trie: tr,
stateObjects: make(map[common.Address]*Object), stateObjects: make(map[common.Address]*Object),
stateObjectsDirty: make(map[common.Address]struct{}), stateObjectsPending: make(map[common.Address]struct{}),
stateValidators: make(map[common.Address]*stk.ValidatorWrapper), stateObjectsDirty: make(map[common.Address]struct{}),
logs: make(map[common.Hash][]*types.Log), stateValidators: make(map[common.Address]*stk.ValidatorWrapper),
preimages: make(map[common.Hash][]byte), logs: make(map[common.Hash][]*types.Log),
journal: newJournal(), preimages: make(map[common.Hash][]byte),
journal: newJournal(),
}, nil }, nil
} }
@ -133,6 +152,7 @@ func (db *DB) Reset(root common.Hash) error {
} }
db.trie = tr db.trie = tr
db.stateObjects = make(map[common.Address]*Object) db.stateObjects = make(map[common.Address]*Object)
db.stateObjectsPending = make(map[common.Address]struct{})
db.stateObjectsDirty = make(map[common.Address]struct{}) db.stateObjectsDirty = make(map[common.Address]struct{})
db.stateValidators = make(map[common.Address]*stk.ValidatorWrapper) db.stateValidators = make(map[common.Address]*stk.ValidatorWrapper)
db.thash = common.Hash{} db.thash = common.Hash{}
@ -234,6 +254,16 @@ func (db *DB) GetNonce(addr common.Address) uint64 {
return 0 return 0
} }
// TxIndex returns the current transaction index set by Prepare.
func (db *DB) TxIndex() int {
return db.txIndex
}
// BlockHash returns the current block hash set by Prepare.
func (db *DB) BlockHash() common.Hash {
return db.bhash
}
// GetCode ... // GetCode ...
func (db *DB) GetCode(addr common.Address) []byte { func (db *DB) GetCode(addr common.Address) []byte {
stateObject := db.getStateObject(addr) stateObject := db.getStateObject(addr)
@ -409,9 +439,15 @@ func (db *DB) Suicide(addr common.Address) bool {
// //
// updateStateObject writes the given object to the trie. // updateStateObject writes the given object to the trie.
func (db *DB) updateStateObject(stateObject *Object) { func (db *DB) updateStateObject(obj *Object) {
addr := stateObject.Address() // Track the amount of time wasted on updating the account from the trie
data, err := rlp.EncodeToBytes(stateObject) if metrics.EnabledExpensive {
defer func(start time.Time) { db.AccountUpdates += time.Since(start) }(time.Now())
}
// Encode the account and update the account trie
addr := obj.Address()
data, err := rlp.EncodeToBytes(obj)
if err != nil { if err != nil {
panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err)) panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
} }
@ -419,23 +455,40 @@ func (db *DB) updateStateObject(stateObject *Object) {
} }
// deleteStateObject removes the given object from the state trie. // deleteStateObject removes the given object from the state trie.
func (db *DB) deleteStateObject(stateObject *Object) { func (db *DB) deleteStateObject(obj *Object) {
stateObject.deleted = true // Track the amount of time wasted on deleting the account from the trie
addr := stateObject.Address() if metrics.EnabledExpensive {
defer func(start time.Time) { db.AccountUpdates += time.Since(start) }(time.Now())
}
// Delete the account from the trie
addr := obj.Address()
db.setError(db.trie.TryDelete(addr[:])) db.setError(db.trie.TryDelete(addr[:]))
} }
// Retrieve a state object given by the address. Returns nil if not found. // getStateObject retrieves a state object given by the address, returning nil if
func (db *DB) getStateObject(addr common.Address) (stateObject *Object) { // the object is not found or was deleted in this execution context. If you need
// Prefer 'live' objects. // to differentiate between non-existent/just-deleted, use getDeletedStateObject.
if obj := db.stateObjects[addr]; obj != nil { func (db *DB) getStateObject(addr common.Address) *Object {
if obj.deleted { if obj := db.getDeletedStateObject(addr); obj != nil && !obj.deleted {
return nil
}
return obj return obj
} }
return nil
}
// Load the object from the database. // getDeletedStateObject is similar to getStateObject, but instead of returning
// nil for a deleted state object, it returns the actual object with the deleted
// flag set. This is needed by the state journal to revert to the correct s-
// destructed object instead of wiping all knowledge about the state object.
func (db *DB) getDeletedStateObject(addr common.Address) *Object {
// Prefer live objects if any is available
if obj := db.stateObjects[addr]; obj != nil {
return obj
}
// Track the amount of time wasted on loading the object from the database
if metrics.EnabledExpensive {
defer func(start time.Time) { db.AccountReads += time.Since(start) }(time.Now())
}
// Load the object from the database
enc, err := db.trie.TryGet(addr[:]) enc, err := db.trie.TryGet(addr[:])
if len(enc) == 0 { if len(enc) == 0 {
db.setError(err) db.setError(err)
@ -446,7 +499,7 @@ func (db *DB) getStateObject(addr common.Address) (stateObject *Object) {
log.Error("Failed to decode state object", "addr", addr, "err", err) log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil return nil
} }
// Insert into the live set. // Insert into the live set
obj := newObject(db, addr, data) obj := newObject(db, addr, data)
db.setStateObject(obj) db.setStateObject(obj)
return obj return obj
@ -459,7 +512,7 @@ func (db *DB) setStateObject(object *Object) {
// GetOrNewStateObject retrieves a state object or create a new state object if nil. // GetOrNewStateObject retrieves a state object or create a new state object if nil.
func (db *DB) GetOrNewStateObject(addr common.Address) *Object { func (db *DB) GetOrNewStateObject(addr common.Address) *Object {
stateObject := db.getStateObject(addr) stateObject := db.getStateObject(addr)
if stateObject == nil || stateObject.deleted { if stateObject == nil {
stateObject, _ = db.createObject(addr) stateObject, _ = db.createObject(addr)
} }
return stateObject return stateObject
@ -468,7 +521,8 @@ func (db *DB) GetOrNewStateObject(addr common.Address) *Object {
// createObject creates a new state object. If there is an existing account with // createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value. // the given address, it is overwritten and returned as the second return value.
func (db *DB) createObject(addr common.Address) (newobj, prev *Object) { func (db *DB) createObject(addr common.Address) (newobj, prev *Object) {
prev = db.getStateObject(addr) prev = db.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
newobj = newObject(db, addr, Account{}) newobj = newObject(db, addr, Account{})
newobj.setNonce(0) // sets the object to dirty newobj.setNonce(0) // sets the object to dirty
if prev == nil { if prev == nil {
@ -498,20 +552,33 @@ func (db *DB) CreateAccount(addr common.Address) {
} }
// ForEachStorage ... // ForEachStorage ...
func (db *DB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { func (db *DB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error {
so := db.getStateObject(addr) so := db.getStateObject(addr)
if so == nil { if so == nil {
return return nil
} }
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil)) it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() { for it.Next() {
key := common.BytesToHash(db.trie.GetKey(it.Key)) key := common.BytesToHash(db.trie.GetKey(it.Key))
if value, dirty := so.dirtyStorage[key]; dirty { if value, dirty := so.dirtyStorage[key]; dirty {
cb(key, value) if !cb(key, value) {
return nil
}
continue continue
} }
cb(key, common.BytesToHash(it.Value))
if len(it.Value) > 0 {
_, content, _, err := rlp.Split(it.Value)
if err != nil {
return err
}
if !cb(key, common.BytesToHash(content)) {
return nil
}
}
} }
return nil
} }
// Copy creates a deep, independent copy of the state. // Copy creates a deep, independent copy of the state.
@ -519,16 +586,17 @@ func (db *DB) ForEachStorage(addr common.Address, cb func(key, value common.Hash
func (db *DB) Copy() *DB { func (db *DB) Copy() *DB {
// Copy all the basic fields, initialize the memory ones // Copy all the basic fields, initialize the memory ones
state := &DB{ state := &DB{
db: db.db, db: db.db,
trie: db.db.CopyTrie(db.trie), trie: db.db.CopyTrie(db.trie),
stateObjects: make(map[common.Address]*Object, len(db.journal.dirties)), stateObjects: make(map[common.Address]*Object, len(db.journal.dirties)),
stateObjectsDirty: make(map[common.Address]struct{}, len(db.journal.dirties)), stateObjectsPending: make(map[common.Address]struct{}, len(db.stateObjectsPending)),
stateValidators: make(map[common.Address]*stk.ValidatorWrapper), stateObjectsDirty: make(map[common.Address]struct{}, len(db.journal.dirties)),
refund: db.refund, stateValidators: make(map[common.Address]*stk.ValidatorWrapper),
logs: make(map[common.Hash][]*types.Log, len(db.logs)), refund: db.refund,
logSize: db.logSize, logs: make(map[common.Hash][]*types.Log, len(db.logs)),
preimages: make(map[common.Hash][]byte), logSize: db.logSize,
journal: newJournal(), preimages: make(map[common.Hash][]byte),
journal: newJournal(),
} }
// Copy the dirty states, logs, and preimages // Copy the dirty states, logs, and preimages
for addr := range db.journal.dirties { for addr := range db.journal.dirties {
@ -537,20 +605,30 @@ func (db *DB) Copy() *DB {
// in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for // in the stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we need to check for
// nil // nil
if object, exist := db.stateObjects[addr]; exist { if object, exist := db.stateObjects[addr]; exist {
// Even though the original object is dirty, we are not copying the journal,
// so we need to make sure that anyside effect the journal would have caused
// during a commit (or similar op) is already applied to the copy.
state.stateObjects[addr] = object.deepCopy(state) state.stateObjects[addr] = object.deepCopy(state)
state.stateObjectsDirty[addr] = struct{}{}
state.stateObjectsDirty[addr] = struct{}{} // Mark the copy dirty to force internal (code/state) commits
state.stateObjectsPending[addr] = struct{}{} // Mark the copy pending to force external (account) commits
} }
} }
// Above, we don't copy the actual journal. This means that if the copy is copied, the // Above, we don't copy the actual journal. This means that if the copy is copied, the
// loop above will be a no-op, since the copy's journal is empty. // loop above will be a no-op, since the copy's journal is empty.
// Thus, here we iterate over stateObjects, to enable copies of copies // Thus, here we iterate over stateObjects, to enable copies of copies
for addr := range db.stateObjectsPending {
if _, exist := state.stateObjects[addr]; !exist {
state.stateObjects[addr] = db.stateObjects[addr].deepCopy(state)
}
state.stateObjectsPending[addr] = struct{}{}
}
for addr := range db.stateObjectsDirty { for addr := range db.stateObjectsDirty {
if _, exist := state.stateObjects[addr]; !exist { if _, exist := state.stateObjects[addr]; !exist {
state.stateObjects[addr] = db.stateObjects[addr].deepCopy(state) state.stateObjects[addr] = db.stateObjects[addr].deepCopy(state)
state.stateObjectsDirty[addr] = struct{}{}
} }
state.stateObjectsDirty[addr] = struct{}{}
} }
for hash, logs := range db.logs { for hash, logs := range db.logs {
cpy := make([]*types.Log, len(logs)) cpy := make([]*types.Log, len(logs))
for i, l := range logs { for i, l := range logs {
@ -598,28 +676,28 @@ func (db *DB) GetRefund() uint64 {
// and clears the journal as well as the refunds. // and clears the journal as well as the refunds.
func (db *DB) Finalise(deleteEmptyObjects bool) { func (db *DB) Finalise(deleteEmptyObjects bool) {
// Commit validator changes in cache to stateObjects // Commit validator changes in cache to stateObjects
// TODO: remove validator cache after commit
for addr, val := range db.stateValidators { for addr, val := range db.stateValidators {
db.UpdateValidatorWrapper(addr, val) db.UpdateValidatorWrapper(addr, val)
} }
for addr := range db.journal.dirties { for addr := range db.journal.dirties {
stateObject, exist := db.stateObjects[addr] obj, exist := db.stateObjects[addr]
if !exist { if !exist {
// ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2
// That tx goes out of gas, and although the notion of 'touched' does not exist there, the // That tx goes out of gas, and although the notion of 'touched' does not exist there, the
// touch-event will still be recorded in the journal. Since ripeMD is a special snowflake, // touch-event will still be recorded in the journal. Since ripeMD is a special snowflake,
// it will persist in the journal even though the journal is reverted. In this special circumstance, // it will persist in the journal even though the journal is reverted. In this special circumstance,
// it may exist in `db.journal.dirties` but not in `db.stateObjects`. // it may exist in `s.journal.dirties` but not in `s.stateObjects`.
// Thus, we can safely ignore it here // Thus, we can safely ignore it here
continue continue
} }
if obj.suicided || (deleteEmptyObjects && obj.empty()) {
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) { obj.deleted = true
db.deleteStateObject(stateObject)
} else { } else {
stateObject.updateRoot(db.db) obj.finalise()
db.updateStateObject(stateObject)
} }
db.stateObjectsPending[addr] = struct{}{}
db.stateObjectsDirty[addr] = struct{}{} db.stateObjectsDirty[addr] = struct{}{}
} }
// Invalidate journal because reverting across transactions is not allowed. // Invalidate journal because reverting across transactions is not allowed.
@ -630,7 +708,25 @@ func (db *DB) Finalise(deleteEmptyObjects bool) {
// It is called in between transactions to get the root hash that // It is called in between transactions to get the root hash that
// goes into transaction receipts. // goes into transaction receipts.
func (db *DB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { func (db *DB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Finalise all the dirty storage states and write them into the tries
db.Finalise(deleteEmptyObjects) db.Finalise(deleteEmptyObjects)
for addr := range db.stateObjectsPending {
obj := db.stateObjects[addr]
if obj.deleted {
db.deleteStateObject(obj)
} else {
obj.updateRoot(db.db)
db.updateStateObject(obj)
}
}
if len(db.stateObjectsPending) > 0 {
db.stateObjectsPending = make(map[common.Address]struct{})
}
// Track the amount of time wasted on hashing the account trie
if metrics.EnabledExpensive {
defer func(start time.Time) { db.AccountHashes += time.Since(start) }(time.Now())
}
return db.trie.Hash() return db.trie.Hash()
} }
@ -650,41 +746,36 @@ func (db *DB) clearJournalAndRefund() {
// Commit writes the state to the underlying in-memory trie database. // Commit writes the state to the underlying in-memory trie database.
func (db *DB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) { func (db *DB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer db.clearJournalAndRefund() // Finalize any pending changes and merge everything into the tries
db.IntermediateRoot(deleteEmptyObjects)
for addr := range db.journal.dirties { // Commit objects to the trie, measuring the elapsed time
db.stateObjectsDirty[addr] = struct{}{} for addr := range db.stateObjectsDirty {
} if obj := db.stateObjects[addr]; !obj.deleted {
// Commit objects to the trie.
for addr, stateObject := range db.stateObjects {
_, isDirty := db.stateObjectsDirty[addr]
switch {
case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
db.deleteStateObject(stateObject)
case isDirty:
// Write any contract code associated with the state object // Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode { if obj.code != nil && obj.dirtyCode {
db.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code) db.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code)
stateObject.dirtyCode = false obj.dirtyCode = false
} }
// Write any storage changes in the state object to its storage trie. // Write any storage changes in the state object to its storage trie
if err := stateObject.CommitTrie(db.db); err != nil { if err := obj.CommitTrie(db.db); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
// Update the object in the main account trie.
db.updateStateObject(stateObject)
} }
delete(db.stateObjectsDirty, addr)
} }
// Write trie changes. if len(db.stateObjectsDirty) > 0 {
root, err = db.trie.Commit(func(leaf []byte, parent common.Hash) error { db.stateObjectsDirty = make(map[common.Address]struct{})
}
// Write the account trie changes, measuing the amount of wasted time
if metrics.EnabledExpensive {
defer func(start time.Time) { db.AccountCommits += time.Since(start) }(time.Now())
}
return db.trie.Commit(func(leaf []byte, parent common.Hash) error {
var account Account var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil { if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil return nil
} }
if account.Root != emptyState { if account.Root != emptyRoot {
db.db.TrieDB().Reference(account.Root, parent) db.db.TrieDB().Reference(account.Root, parent)
} }
code := common.BytesToHash(account.CodeHash) code := common.BytesToHash(account.CodeHash)
@ -693,8 +784,6 @@ func (db *DB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
} }
return nil return nil
}) })
//log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
} }
var ( var (

@ -25,22 +25,21 @@ import (
"math/rand" "math/rand"
"reflect" "reflect"
"strings" "strings"
"sync"
"testing" "testing"
"testing/quick" "testing/quick"
check "gopkg.in/check.v1" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
common2 "github.com/harmony-one/harmony/internal/common"
) )
// Tests that updating a state trie does not leak any database writes prior to // Tests that updating a state trie does not leak any database writes prior to
// actually committing the state. // actually committing the state.
func TestUpdateLeaks(t *testing.T) { func TestUpdateLeaks(t *testing.T) {
// Create an empty state database // Create an empty state database
db := ethdb.NewMemDatabase() db := rawdb.NewMemoryDatabase()
state, _ := New(common.Hash{}, NewDatabase(db)) state, _ := New(common.Hash{}, NewDatabase(db))
// Update it with some accounts // Update it with some accounts
@ -54,21 +53,27 @@ func TestUpdateLeaks(t *testing.T) {
if i%3 == 0 { if i%3 == 0 {
state.SetCode(addr, []byte{i, i, i, i, i}) state.SetCode(addr, []byte{i, i, i, i, i})
} }
state.IntermediateRoot(false)
} }
root := state.IntermediateRoot(false)
if err := state.Database().TrieDB().Commit(root, false); err != nil {
t.Errorf("can not commit trie %v to persistent database", root.Hex())
}
// Ensure that no data was leaked into the database // Ensure that no data was leaked into the database
for _, key := range db.Keys() { it := db.NewIterator()
value, _ := db.Get(key) for it.Next() {
t.Errorf("State leaked into database: %x -> %x", key, value) t.Errorf("State leaked into database: %x -> %x", it.Key(), it.Value())
} }
it.Release()
} }
// Tests that no intermediate state of an object is stored into the database, // Tests that no intermediate state of an object is stored into the database,
// only the one right before the commit. // only the one right before the commit.
func TestIntermediateLeaks(t *testing.T) { func TestIntermediateLeaks(t *testing.T) {
// Create two state databases, one transitioning to the final state, the other final from the beginning // Create two state databases, one transitioning to the final state, the other final from the beginning
transDb := ethdb.NewMemDatabase() transDb := rawdb.NewMemoryDatabase()
finalDb := ethdb.NewMemDatabase() finalDb := rawdb.NewMemoryDatabase()
transState, _ := New(common.Hash{}, NewDatabase(transDb)) transState, _ := New(common.Hash{}, NewDatabase(transDb))
finalState, _ := New(common.Hash{}, NewDatabase(finalDb)) finalState, _ := New(common.Hash{}, NewDatabase(finalDb))
@ -86,34 +91,56 @@ func TestIntermediateLeaks(t *testing.T) {
// Modify the transient state. // Modify the transient state.
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
modify(transState, common.Address{byte(i)}, i, 0) modify(transState, common.Address{i}, i, 0)
} }
// Write modifications to trie. // Write modifications to trie.
transState.IntermediateRoot(false) transState.IntermediateRoot(false)
// Overwrite all the data with new values in the transient database. // Overwrite all the data with new values in the transient database.
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
modify(transState, common.Address{byte(i)}, i, 99) modify(transState, common.Address{i}, i, 99)
modify(finalState, common.Address{byte(i)}, i, 99) modify(finalState, common.Address{i}, i, 99)
} }
// Commit and cross check the databases. // Commit and cross check the databases.
if _, err := transState.Commit(false); err != nil { transRoot, err := transState.Commit(false)
if err != nil {
t.Fatalf("failed to commit transition state: %v", err) t.Fatalf("failed to commit transition state: %v", err)
} }
if _, err := finalState.Commit(false); err != nil { if err = transState.Database().TrieDB().Commit(transRoot, false); err != nil {
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
}
finalRoot, err := finalState.Commit(false)
if err != nil {
t.Fatalf("failed to commit final state: %v", err) t.Fatalf("failed to commit final state: %v", err)
} }
for _, key := range finalDb.Keys() { if err = finalState.Database().TrieDB().Commit(finalRoot, false); err != nil {
if _, err := transDb.Get(key); err != nil { t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
val, _ := finalDb.Get(key) }
t.Errorf("entry missing from the transition database: %x -> %x", key, val)
it := finalDb.NewIterator()
for it.Next() {
key, fvalue := it.Key(), it.Value()
tvalue, err := transDb.Get(key)
if err != nil {
t.Errorf("entry missing from the transition database: %x -> %x", key, fvalue)
}
if !bytes.Equal(fvalue, tvalue) {
t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue)
} }
} }
for _, key := range transDb.Keys() { it.Release()
if _, err := finalDb.Get(key); err != nil {
val, _ := transDb.Get(key) it = transDb.NewIterator()
t.Errorf("extra entry in the transition database: %x -> %x", key, val) for it.Next() {
key, tvalue := it.Key(), it.Value()
fvalue, err := finalDb.Get(key)
if err != nil {
t.Errorf("extra entry in the transition database: %x -> %x", key, it.Value())
}
if !bytes.Equal(fvalue, tvalue) {
t.Errorf("the value associate key %x is mismatch,: %x in transition database ,%x in final database", key, tvalue, fvalue)
} }
} }
} }
@ -123,7 +150,7 @@ func TestIntermediateLeaks(t *testing.T) {
// https://github.com/ethereum/go-ethereum/pull/15549. // https://github.com/ethereum/go-ethereum/pull/15549.
func TestCopy(t *testing.T) { func TestCopy(t *testing.T) {
// Create a random state test to copy and modify "independently" // Create a random state test to copy and modify "independently"
orig, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) orig, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
@ -132,32 +159,45 @@ func TestCopy(t *testing.T) {
} }
orig.Finalise(false) orig.Finalise(false)
// Copy the state, modify both in-memory // Copy the state
copy := orig.Copy() copy := orig.Copy()
// Copy the copy state
ccopy := copy.Copy()
// modify all in memory
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
origObj.AddBalance(big.NewInt(2 * int64(i))) origObj.AddBalance(big.NewInt(2 * int64(i)))
copyObj.AddBalance(big.NewInt(3 * int64(i))) copyObj.AddBalance(big.NewInt(3 * int64(i)))
ccopyObj.AddBalance(big.NewInt(4 * int64(i)))
orig.updateStateObject(origObj) orig.updateStateObject(origObj)
copy.updateStateObject(copyObj) copy.updateStateObject(copyObj)
ccopy.updateStateObject(copyObj)
} }
// Finalise the changes on both concurrently
done := make(chan struct{})
go func() {
orig.Finalise(true)
close(done)
}()
copy.Finalise(true)
<-done
// Verify that the two states have been updated independently // Finalise the changes on all concurrently
finalise := func(wg *sync.WaitGroup, db *DB) {
defer wg.Done()
db.Finalise(true)
}
var wg sync.WaitGroup
wg.Add(3)
go finalise(&wg, orig)
go finalise(&wg, copy)
go finalise(&wg, ccopy)
wg.Wait()
// Verify that the three states have been updated independently
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i})) copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 { if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 {
t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want) t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want)
@ -165,6 +205,9 @@ func TestCopy(t *testing.T) {
if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 { if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want) t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want)
} }
if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want)
}
} }
} }
@ -179,7 +222,7 @@ func TestSnapshotRandom(t *testing.T) {
} }
} }
// A snapshotTest checks that reverting DB snapshots properly undoes all changes // A snapshotTest checks that reverting StateDB snapshots properly undoes all changes
// captured by the snapshot. Instances of this test with pseudorandom content are created // captured by the snapshot. Instances of this test with pseudorandom content are created
// by Generate. // by Generate.
// //
@ -277,13 +320,22 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
}, },
args: make([]int64, 1), args: make([]int64, 1),
}, },
{
name: "AddPreimage",
fn: func(a testAction, s *DB) {
preimage := []byte{1}
hash := common.BytesToHash(preimage)
s.AddPreimage(hash, preimage)
},
args: make([]int64, 1),
},
} }
action := actions[r.Intn(len(actions))] action := actions[r.Intn(len(actions))]
var nameargs []string var nameargs []string
if !action.noAddr { if !action.noAddr {
nameargs = append(nameargs, common2.MustAddressToBech32(addr)) nameargs = append(nameargs, addr.Hex())
} }
for _, i := range action.args { for i := range action.args {
action.args[i] = rand.Int63n(100) action.args[i] = rand.Int63n(100)
nameargs = append(nameargs, fmt.Sprint(action.args[i])) nameargs = append(nameargs, fmt.Sprint(action.args[i]))
} }
@ -334,7 +386,7 @@ func (test *snapshotTest) String() string {
func (test *snapshotTest) run() bool { func (test *snapshotTest) run() bool {
// Run all actions and create snapshots. // Run all actions and create snapshots.
var ( var (
state, _ = New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) state, _ = New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
snapshotRevs = make([]int, len(test.snapshots)) snapshotRevs = make([]int, len(test.snapshots))
sindex = 0 sindex = 0
) )
@ -367,7 +419,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *DB) error {
var err error var err error
checkeq := func(op string, a, b interface{}) bool { checkeq := func(op string, a, b interface{}) bool {
if err == nil && !reflect.DeepEqual(a, b) { if err == nil && !reflect.DeepEqual(a, b) {
err = fmt.Errorf("got %s(%s) == %v, want %v", op, common2.MustAddressToBech32(addr), a, b) err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b)
return false return false
} }
return true return true
@ -405,7 +457,8 @@ func (test *snapshotTest) checkEqual(state, checkstate *DB) error {
return nil return nil
} }
func (s *StateSuite) TestTouchDelete(c *check.C) { func TestTouchDelete(t *testing.T) {
s := newStateTest()
s.state.GetOrNewStateObject(common.Address{}) s.state.GetOrNewStateObject(common.Address{})
root, _ := s.state.Commit(false) root, _ := s.state.Commit(false)
s.state.Reset(root) s.state.Reset(root)
@ -414,25 +467,217 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
s.state.AddBalance(common.Address{}, new(big.Int)) s.state.AddBalance(common.Address{}, new(big.Int))
if len(s.state.journal.dirties) != 1 { if len(s.state.journal.dirties) != 1 {
c.Fatal("expected one dirty state object") t.Fatal("expected one dirty state object")
} }
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
if len(s.state.journal.dirties) != 0 { if len(s.state.journal.dirties) != 0 {
c.Fatal("expected no dirty state object") t.Fatal("expected no dirty state object")
} }
} }
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy. // TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512 // See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
func TestCopyOfCopy(t *testing.T) { func TestCopyOfCopy(t *testing.T) {
sdb, _ := New(common.Hash{}, NewDatabase(ethdb.NewMemDatabase())) state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
addr := common.HexToAddress("aaaa") addr := common.HexToAddress("aaaa")
sdb.SetBalance(addr, big.NewInt(42)) state.SetBalance(addr, big.NewInt(42))
if got := sdb.Copy().GetBalance(addr).Uint64(); got != 42 { if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("1st copy fail, expected 42, got %v", got) t.Fatalf("1st copy fail, expected 42, got %v", got)
} }
if got := sdb.Copy().Copy().GetBalance(addr).Uint64(); got != 42 { if got := state.Copy().Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("2nd copy fail, expected 42, got %v", got) t.Fatalf("2nd copy fail, expected 42, got %v", got)
} }
} }
// Tests a regression where committing a copy lost some internal meta information,
// leading to corrupted subsequent copies.
//
// See https://github.com/ethereum/go-ethereum/issues/20106.
func TestCopyCommitCopy(t *testing.T) {
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
// Create an account and check if the retrieved balance is correct
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata
state.SetState(addr, skey, sval) // Change the storage trie
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := state.GetState(addr, skey); val != sval {
t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) {
t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
}
// Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy()
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("first copy pre-commit code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyOne.GetState(addr, skey); val != sval {
t.Fatalf("first copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) {
t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
}
copyOne.Commit(false)
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyOne.GetState(addr, skey); val != sval {
t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyOne.GetCommittedState(addr, skey); val != sval {
t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
}
// Copy the copy and check the balance once more
copyTwo := copyOne.Copy()
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("second copy code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyTwo.GetState(addr, skey); val != sval {
t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyTwo.GetCommittedState(addr, skey); val != sval {
t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
}
}
// Tests a regression where committing a copy lost some internal meta information,
// leading to corrupted subsequent copies.
//
// See https://github.com/ethereum/go-ethereum/issues/20106.
func TestCopyCopyCommitCopy(t *testing.T) {
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
// Create an account and check if the retrieved balance is correct
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
state.SetBalance(addr, big.NewInt(42)) // Change the account trie
state.SetCode(addr, []byte("hello")) // Change an external metadata
state.SetState(addr, skey, sval) // Change the storage trie
if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := state.GetState(addr, skey); val != sval {
t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) {
t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{})
}
// Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy()
if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("first copy code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyOne.GetState(addr, skey); val != sval {
t.Fatalf("first copy non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) {
t.Fatalf("first copy committed storage slot mismatch: have %x, want %x", val, common.Hash{})
}
// Copy the copy and check the balance once more
copyTwo := copyOne.Copy()
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("second copy pre-commit code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyTwo.GetState(addr, skey); val != sval {
t.Fatalf("second copy pre-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) {
t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{})
}
copyTwo.Commit(false)
if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyTwo.GetState(addr, skey); val != sval {
t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyTwo.GetCommittedState(addr, skey); val != sval {
t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval)
}
// Copy the copy-copy and check the balance once more
copyThree := copyTwo.Copy()
if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
t.Fatalf("third copy code mismatch: have %x, want %x", code, []byte("hello"))
}
if val := copyThree.GetState(addr, skey); val != sval {
t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval)
}
if val := copyThree.GetCommittedState(addr, skey); val != sval {
t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval)
}
}
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
// while changing the internals of statedb. The workflow is that a contract is
// self destructed, then in a followup transaction (but same block) it's created
// again and the transaction reverted.
//
// The original statedb implementation flushed dirty objects to the tries after
// each transaction, so this works ok. The rework accumulated writes in memory
// first, but the journal wiped the entire state object on create-revert.
func TestDeleteCreateRevert(t *testing.T) {
// Create an initial state with a single contract
state, _ := New(common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
addr := toAddr([]byte("so"))
state.SetBalance(addr, big.NewInt(1))
root, _ := state.Commit(false)
state.Reset(root)
// Simulate self-destructing in one transaction, then create-reverting in another
state.Suicide(addr)
state.Finalise(true)
id := state.Snapshot()
state.SetBalance(addr, big.NewInt(2))
state.RevertToSnapshot(id)
// Commit the entire state and make sure we don't crash and have the correct state
root, _ = state.Commit(true)
state.Reset(root)
if state.getStateObject(addr) != nil {
t.Fatalf("self-destructed contract came alive")
}
}

@ -96,7 +96,7 @@ type Message interface {
} }
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. // IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
func IntrinsicGas(data []byte, contractCreation, homestead, isValidatorCreation bool) (uint64, error) { func IntrinsicGas(data []byte, contractCreation, homestead, istanbul, isValidatorCreation bool) (uint64, error) {
// Set the starting gas for the raw transaction // Set the starting gas for the raw transaction
var gas uint64 var gas uint64
if contractCreation && homestead { if contractCreation && homestead {
@ -116,10 +116,14 @@ func IntrinsicGas(data []byte, contractCreation, homestead, isValidatorCreation
} }
} }
// Make sure we don't exceed uint64 for all data combinations // Make sure we don't exceed uint64 for all data combinations
if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz { nonZeroGas := params.TxDataNonZeroGasFrontier
if istanbul {
nonZeroGas = params.TxDataNonZeroGasEIP2028
}
if (math.MaxUint64-gas)/nonZeroGas < nz {
return 0, vm.ErrOutOfGas return 0, vm.ErrOutOfGas
} }
gas += nz * params.TxDataNonZeroGas gas += nz * nonZeroGas
z := uint64(len(data)) - nz z := uint64(len(data)) - nz
if (math.MaxUint64-gas)/params.TxDataZeroGas < z { if (math.MaxUint64-gas)/params.TxDataZeroGas < z {
@ -219,10 +223,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
msg := st.msg msg := st.msg
sender := vm.AccountRef(msg.From()) sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.EpochNumber)
contractCreation := msg.To() == nil contractCreation := msg.To() == nil
// Pay intrinsic gas // Pay intrinsic gas
gas, err := IntrinsicGas(st.data, contractCreation, homestead, false) gas, err := IntrinsicGas(st.data, contractCreation, homestead, istanbul, false)
if err != nil { if err != nil {
return nil, 0, false, err return nil, 0, false, err
} }
@ -298,9 +303,10 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
sender := vm.AccountRef(msg.From()) sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.EpochNumber)
// Pay intrinsic gas // Pay intrinsic gas
gas, err := IntrinsicGas(st.data, false, homestead, msg.Type() == types.StakeCreateVal) gas, err := IntrinsicGas(st.data, false, homestead, istanbul, msg.Type() == types.StakeCreateVal)
if err != nil { if err != nil {
return 0, err return 0, err

@ -252,6 +252,7 @@ type TxPool struct {
txErrorSink *types.TransactionErrorSink // All failed txs gets reported here txErrorSink *types.TransactionErrorSink // All failed txs gets reported here
homestead bool homestead bool
istanbul bool
} }
// NewTxPool creates a new transaction pool to gather, sort and filter inbound // NewTxPool creates a new transaction pool to gather, sort and filter inbound
@ -335,6 +336,9 @@ func (pool *TxPool) loop() {
if pool.chainconfig.IsS3(ev.Block.Epoch()) { if pool.chainconfig.IsS3(ev.Block.Epoch()) {
pool.homestead = true pool.homestead = true
} }
if pool.chainconfig.IsIstanbul(ev.Block.Epoch()) {
pool.istanbul = true
}
pool.reset(head.Header(), ev.Block.Header()) pool.reset(head.Header(), ev.Block.Header())
head = ev.Block head = ev.Block
pool.mu.Unlock() pool.mu.Unlock()
@ -740,9 +744,9 @@ func (pool *TxPool) validateTx(tx types.PoolTransaction, local bool) error {
} }
intrGas := uint64(0) intrGas := uint64(0)
if isStakingTx { if isStakingTx {
intrGas, err = IntrinsicGas(tx.Data(), false, pool.homestead, stakingTx.StakingType() == staking.DirectiveCreateValidator) intrGas, err = IntrinsicGas(tx.Data(), false, pool.homestead, pool.istanbul, stakingTx.StakingType() == staking.DirectiveCreateValidator)
} else { } else {
intrGas, err = IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead, false) intrGas, err = IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead, pool.istanbul, false)
} }
if err != nil { if err != nil {
return err return err

@ -26,11 +26,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
@ -153,7 +154,7 @@ func createBlockChain() *BlockChain {
GasLimit: 1e18, GasLimit: 1e18,
ShardID: 0, ShardID: 0,
} }
database := ethdb.NewMemDatabase() database := rawdb.NewMemoryDatabase()
genesis := gspec.MustCommit(database) genesis := gspec.MustCommit(database)
_ = genesis _ = genesis
blockchain, _ := NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) blockchain, _ := NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil)
@ -161,7 +162,7 @@ func createBlockChain() *BlockChain {
} }
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1e18, new(event.Feed)} blockchain := &testBlockChain{statedb, 1e18, new(event.Feed)}
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -218,7 +219,7 @@ func (c *testChain) State() (*state.DB, error) {
// a state change between those fetches. // a state change between those fetches.
stdb := c.statedb stdb := c.statedb
if *c.trigger { if *c.trigger {
c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) c.statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
// simulate that the new head block included tx0 and tx1 // simulate that the new head block included tx0 and tx1
c.statedb.SetNonce(c.address, 2) c.statedb.SetNonce(c.address, 2)
c.statedb.SetBalance(c.address, new(big.Int).SetUint64(denominations.One)) c.statedb.SetBalance(c.address, new(big.Int).SetUint64(denominations.One))
@ -236,7 +237,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
var ( var (
key, _ = crypto.GenerateKey() key, _ = crypto.GenerateKey()
address = crypto.PubkeyToAddress(key.PublicKey) address = crypto.PubkeyToAddress(key.PublicKey)
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
trigger = false trigger = false
) )
@ -574,7 +575,7 @@ func TestTransactionChainFork(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.AddBalance(addr, big.NewInt(100000000000000)) statedb.AddBalance(addr, big.NewInt(100000000000000))
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
@ -603,7 +604,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
addr := crypto.PubkeyToAddress(key.PublicKey) addr := crypto.PubkeyToAddress(key.PublicKey)
resetState := func() { resetState := func() {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.AddBalance(addr, big.NewInt(100000000000000)) statedb.AddBalance(addr, big.NewInt(100000000000000))
pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
@ -799,7 +800,7 @@ func TestTransactionPostponing(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the postponing with // Create the pool to test the postponing with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink) pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink)
@ -957,7 +958,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
t.Parallel() t.Parallel()
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1047,7 +1048,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
evictionInterval = time.Second evictionInterval = time.Second
// Create the pool to test the non-expiration enforcement // Create the pool to test the non-expiration enforcement
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1162,7 +1163,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1208,7 +1209,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1242,7 +1243,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the limit enforcement with // Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1287,7 +1288,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the pricing enforcement with // Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink) pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink)
@ -1361,7 +1362,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
os.Remove(journal) os.Remove(journal)
// Create the original pool to inject transaction into the journal // Create the original pool to inject transaction into the journal
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
@ -1459,7 +1460,7 @@ func TestTransactionStatusCheck(t *testing.T) {
t.Parallel() t.Parallel()
// Create the pool to test the status retrievals with // Create the pool to test the status retrievals with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink) pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, dummyErrorSink)

@ -90,19 +90,17 @@ func (sink *TransactionErrorSink) Contains(hash string) bool {
func (sink *TransactionErrorSink) Remove(tx PoolTransaction) { func (sink *TransactionErrorSink) Remove(tx PoolTransaction) {
if plainTx, ok := tx.(*Transaction); ok { if plainTx, ok := tx.(*Transaction); ok {
hash := plainTx.Hash().String() hash := plainTx.Hash().String()
present := sink.failedPlainTxs.Remove(hash) sink.failedPlainTxs.Remove(hash)
utils.Logger().Debug(). utils.Logger().Debug().
Str("tag", logTag). Str("tag", logTag).
Interface("tx-hash-id", hash). Interface("tx-hash-id", hash).
Bool("was-in-error-sink", present).
Msgf("Removed plain transaction error message") Msgf("Removed plain transaction error message")
} else if stakingTx, ok := tx.(*staking.StakingTransaction); ok { } else if stakingTx, ok := tx.(*staking.StakingTransaction); ok {
hash := stakingTx.Hash().String() hash := stakingTx.Hash().String()
present := sink.failedStakingTxs.Remove(hash) sink.failedStakingTxs.Remove(hash)
utils.Logger().Debug(). utils.Logger().Debug().
Str("tag", logTag). Str("tag", logTag).
Interface("tx-hash-id", hash). Interface("tx-hash-id", hash).
Bool("was-in-error-sink", present).
Msgf("Removed staking transaction error message") Msgf("Removed staking transaction error message")
} else { } else {
utils.Logger().Error(). utils.Logger().Error().

@ -23,13 +23,31 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
) )
// calculates the memory size required for a step // calcMemSize64 calculates the required memory size, and returns
func calcMemSize(off, l *big.Int) *big.Int { // the size and whether the result overflowed uint64
if l.Sign() == 0 { func calcMemSize64(off, l *big.Int) (uint64, bool) {
return common.Big0 if !l.IsUint64() {
return 0, true
} }
return calcMemSize64WithUint(off, l.Uint64())
}
return new(big.Int).Add(off, l) // calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64
func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if length64 == 0 {
return 0, false
}
// Check that offset doesn't overflow
if !off.IsUint64() {
return 0, true
}
offset64 := off.Uint64()
val := offset64 + length64
// if value < either of it's parts, then it overflowed
return val, val < offset64
} }
// getData returns a slice from the data based on the start and size and pads // getData returns a slice from the data based on the start and size and pads
@ -59,7 +77,7 @@ func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
// bigUint64 returns the integer casted to a uint64 and returns whether it // bigUint64 returns the integer casted to a uint64 and returns whether it
// overflowed in the process. // overflowed in the process.
func bigUint64(v *big.Int) (uint64, bool) { func bigUint64(v *big.Int) (uint64, bool) {
return v.Uint64(), v.BitLen() > 64 return v.Uint64(), !v.IsUint64()
} }
// toWordSize returns the ceiled word size required for memory expansion. // toWordSize returns the ceiled word size required for memory expansion.

@ -18,9 +18,12 @@ package vm
import ( import (
"crypto/sha256" "crypto/sha256"
"encoding/binary"
"errors" "errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -54,9 +57,23 @@ var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{3}): &ripemd160hash{}, common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{}, common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{}, common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256Add{}, common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
common.BytesToAddress([]byte{7}): &bn256ScalarMul{}, common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
common.BytesToAddress([]byte{8}): &bn256Pairing{}, common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
}
// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
// contracts used in the Istanbul release.
var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
} }
// RunPrecompiledContract runs and evaluates the output of a precompiled contract. // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
@ -90,8 +107,13 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil return nil, nil
} }
// We must make sure not to modify the 'input', so placing the 'v' along with
// the signature needs to be done on a new allocation
sig := make([]byte, 65)
copy(sig, input[64:128])
sig[64] = v
// v needs to be at the end for libsecp256k1 // v needs to be at the end for libsecp256k1
pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v)) pubKey, err := crypto.Ecrecover(input[:32], sig)
// make sure the public key is a valid one // make sure the public key is a valid one
if err != nil { if err != nil {
return nil, nil return nil, nil
@ -271,15 +293,9 @@ func newTwistPoint(blob []byte) (*bn256.G2, error) {
return p, nil return p, nil
} }
// bn256Add implements a native elliptic curve point addition. // runBn256Add implements the Bn256Add precompile, referenced by both
type bn256Add struct{} // Byzantium and Istanbul operations.
func runBn256Add(input []byte) ([]byte, error) {
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256Add) RequiredGas(input []byte) uint64 {
return params.Bn256AddGas
}
func (c *bn256Add) Run(input []byte) ([]byte, error) {
x, err := newCurvePoint(getData(input, 0, 64)) x, err := newCurvePoint(getData(input, 0, 64))
if err != nil { if err != nil {
return nil, err return nil, err
@ -293,15 +309,35 @@ func (c *bn256Add) Run(input []byte) ([]byte, error) {
return res.Marshal(), nil return res.Marshal(), nil
} }
// bn256ScalarMul implements a native elliptic curve scalar multiplication. // bn256Add implements a native elliptic curve point addition conforming to
type bn256ScalarMul struct{} // Istanbul consensus rules.
type bn256AddIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}
func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
return runBn256Add(input)
}
// bn256AddByzantium implements a native elliptic curve point addition
// conforming to Byzantium consensus rules.
type bn256AddByzantium struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract. // RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMul) RequiredGas(input []byte) uint64 { func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGas return params.Bn256AddGasByzantium
} }
func (c *bn256ScalarMul) Run(input []byte) ([]byte, error) { func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
return runBn256Add(input)
}
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
// both Byzantium and Istanbul operations.
func runBn256ScalarMul(input []byte) ([]byte, error) {
p, err := newCurvePoint(getData(input, 0, 64)) p, err := newCurvePoint(getData(input, 0, 64))
if err != nil { if err != nil {
return nil, err return nil, err
@ -311,6 +347,32 @@ func (c *bn256ScalarMul) Run(input []byte) ([]byte, error) {
return res.Marshal(), nil return res.Marshal(), nil
} }
// bn256ScalarMulIstanbul implements a native elliptic curve scalar
// multiplication conforming to Istanbul consensus rules.
type bn256ScalarMulIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasIstanbul
}
func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}
// bn256ScalarMulByzantium implements a native elliptic curve scalar
// multiplication conforming to Byzantium consensus rules.
type bn256ScalarMulByzantium struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasByzantium
}
func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}
var ( var (
// true32Byte is returned if the bn256 pairing check succeeds. // true32Byte is returned if the bn256 pairing check succeeds.
true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1} true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
@ -322,15 +384,9 @@ var (
errBadPairingInput = errors.New("bad elliptic curve pairing size") errBadPairingInput = errors.New("bad elliptic curve pairing size")
) )
// bn256Pairing implements a pairing pre-compile for the bn256 curve // runBn256Pairing implements the Bn256Pairing precompile, referenced by both
type bn256Pairing struct{} // Byzantium and Istanbul operations.
func runBn256Pairing(input []byte) ([]byte, error) {
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256Pairing) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGas + uint64(len(input)/192)*params.Bn256PairingPerPointGas
}
func (c *bn256Pairing) Run(input []byte) ([]byte, error) {
// Handle some corner cases cheaply // Handle some corner cases cheaply
if len(input)%192 > 0 { if len(input)%192 > 0 {
return nil, errBadPairingInput return nil, errBadPairingInput
@ -358,3 +414,90 @@ func (c *bn256Pairing) Run(input []byte) ([]byte, error) {
} }
return false32Byte, nil return false32Byte, nil
} }
// bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve
// conforming to Istanbul consensus rules.
type bn256PairingIstanbul struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
}
func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
return runBn256Pairing(input)
}
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
// conforming to Byzantium consensus rules.
type bn256PairingByzantium struct{}
// RequiredGas returns the gas required to execute the pre-compiled contract.
func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
}
func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
return runBn256Pairing(input)
}
type blake2F struct{}
func (c *blake2F) RequiredGas(input []byte) uint64 {
// If the input is malformed, we can't calculate the gas, return 0 and let the
// actual call choke and fault.
if len(input) != blake2FInputLength {
return 0
}
return uint64(binary.BigEndian.Uint32(input[0:4]))
}
const (
blake2FInputLength = 213
blake2FFinalBlockBytes = byte(1)
blake2FNonFinalBlockBytes = byte(0)
)
var (
errBlake2FInvalidInputLength = errors.New("invalid input length")
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
)
func (c *blake2F) Run(input []byte) ([]byte, error) {
// Make sure the input is valid (correct lenth and final flag)
if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
}
if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes {
return nil, errBlake2FInvalidFinalFlag
}
// Parse the input into the Blake2b call parameters
var (
rounds = binary.BigEndian.Uint32(input[0:4])
final = (input[212] == blake2FFinalBlockBytes)
h [8]uint64
m [16]uint64
t [2]uint64
)
for i := 0; i < 8; i++ {
offset := 4 + i*8
h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
}
for i := 0; i < 16; i++ {
offset := 68 + i*8
m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
}
t[0] = binary.LittleEndian.Uint64(input[196:204])
t[1] = binary.LittleEndian.Uint64(input[204:212])
// Execute the compression function, extract and return the result
blake2b.F(&h, m, t, final, rounds)
output := make([]byte, 64)
for i := 0; i < 8; i++ {
offset := i * 8
binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
}
return output, nil
}

@ -17,8 +17,10 @@
package vm package vm
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -31,6 +33,14 @@ type precompiledTest struct {
noBenchmark bool // Benchmark primarily the worst-cases noBenchmark bool // Benchmark primarily the worst-cases
} }
// precompiledFailureTest defines the input/error pairs for precompiled
// contract failure tests.
type precompiledFailureTest struct {
input string
expectedError error
name string
}
// modexpTests are the test and benchmark data for the modexp precompiled contract. // modexpTests are the test and benchmark data for the modexp precompiled contract.
var modexpTests = []precompiledTest{ var modexpTests = []precompiledTest{
{ {
@ -335,8 +345,61 @@ var bn256PairingTests = []precompiledTest{
}, },
} }
// EIP-152 test vectors
var blake2FMalformedInputTests = []precompiledFailureTest{
{
input: "",
expectedError: errBlake2FInvalidInputLength,
name: "vector 0: empty input",
},
{
input: "00000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expectedError: errBlake2FInvalidInputLength,
name: "vector 1: less than 213 bytes input",
},
{
input: "000000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expectedError: errBlake2FInvalidInputLength,
name: "vector 2: more than 213 bytes input",
},
{
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000002",
expectedError: errBlake2FInvalidFinalFlag,
name: "vector 3: malformed final block indicator flag",
},
}
// EIP-152 test vectors
var blake2FTests = []precompiledTest{
{
input: "0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expected: "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b",
name: "vector 4",
},
{ // https://tools.ietf.org/html/rfc7693#appendix-A
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expected: "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
name: "vector 5",
},
{
input: "0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000",
expected: "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
name: "vector 6",
},
{
input: "0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expected: "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
name: "vector 7",
},
{
input: "007A120048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001",
expected: "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189",
name: "vector 8",
},
}
func testPrecompiled(addr string, test precompiledTest, t *testing.T) { func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := PrecompiledContractsByzantium[common.HexToAddress(addr)] p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)) nil, new(big.Int), p.RequiredGas(in))
@ -346,6 +409,48 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
} else if common.Bytes2Hex(res) != test.expected { } else if common.Bytes2Hex(res) != test.expected {
t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res)) t.Errorf("Expected %v, got %v", test.expected, common.Bytes2Hex(res))
} }
// Verify that the precompile did not touch the input buffer
exp := common.Hex2Bytes(test.input)
if !bytes.Equal(in, exp) {
t.Errorf("Precompiled %v modified input data", addr)
}
})
}
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)-1)
t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
// Verify that the precompile did not touch the input buffer
exp := common.Hex2Bytes(test.input)
if !bytes.Equal(in, exp) {
t.Errorf("Precompiled %v modified input data", addr)
}
})
}
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input)
contract := NewContract(AccountRef(common.HexToAddress("31337")),
nil, new(big.Int), p.RequiredGas(in))
t.Run(test.name, func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract)
if !reflect.DeepEqual(err, test.expectedError) {
t.Errorf("Expected error [%v], got [%v]", test.expectedError, err)
}
// Verify that the precompile did not touch the input buffer
exp := common.Hex2Bytes(test.input)
if !bytes.Equal(in, exp) {
t.Errorf("Precompiled %v modified input data", addr)
}
}) })
} }
@ -353,7 +458,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
if test.noBenchmark { if test.noBenchmark {
return return
} }
p := PrecompiledContractsByzantium[common.HexToAddress(addr)] p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.input) in := common.Hex2Bytes(test.input)
reqGas := p.RequiredGas(in) reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")), contract := NewContract(AccountRef(common.HexToAddress("1337")),
@ -453,6 +558,13 @@ func BenchmarkPrecompiledBn256Add(bench *testing.B) {
} }
} }
// Tests OOG
func TestPrecompiledModExpOOG(t *testing.T) {
for _, test := range modexpTests {
testPrecompiledOOG("05", test, t)
}
}
// Tests the sample inputs from the elliptic curve scalar multiplication EIP 213. // Tests the sample inputs from the elliptic curve scalar multiplication EIP 213.
func TestPrecompiledBn256ScalarMul(t *testing.T) { func TestPrecompiledBn256ScalarMul(t *testing.T) {
for _, test := range bn256ScalarMulTests { for _, test := range bn256ScalarMulTests {
@ -480,3 +592,72 @@ func BenchmarkPrecompiledBn256Pairing(bench *testing.B) {
benchmarkPrecompiled("08", test, bench) benchmarkPrecompiled("08", test, bench)
} }
} }
func TestPrecompiledBlake2F(t *testing.T) {
for _, test := range blake2FTests {
testPrecompiled("09", test, t)
}
}
func BenchmarkPrecompiledBlake2F(bench *testing.B) {
for _, test := range blake2FTests {
benchmarkPrecompiled("09", test, bench)
}
}
func TestPrecompileBlake2FMalformedInput(t *testing.T) {
for _, test := range blake2FMalformedInputTests {
testPrecompiledFailure("09", test, t)
}
}
// EcRecover test vectors
var ecRecoverTests = []precompiledTest{
{
input: "a8b53bdf3306a35a7103ab5504a0c9b492295564b6202b1942a84ef300107281" +
"000000000000000000000000000000000000000000000000000000000000001b" +
"3078356531653033663533636531386237373263636230303933666637316633" +
"6635336635633735623734646362333161383561613862383839326234653862" +
"1122334455667788991011121314151617181920212223242526272829303132",
expected: "",
name: "CallEcrecoverUnrecoverableKey",
},
{
input: "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" +
"000000000000000000000000000000000000000000000000000000000000001c" +
"73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f" +
"eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549",
expected: "000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
name: "ValidKey",
},
{
input: "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" +
"100000000000000000000000000000000000000000000000000000000000001c" +
"73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f" +
"eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549",
expected: "",
name: "InvalidHighV-bits-1",
},
{
input: "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" +
"000000000000000000000000000000000000001000000000000000000000001c" +
"73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f" +
"eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549",
expected: "",
name: "InvalidHighV-bits-2",
},
{
input: "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" +
"000000000000000000000000000000000000001000000000000000000000011c" +
"73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f" +
"eeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549",
expected: "",
name: "InvalidHighV-bits-3",
},
}
func TestPrecompiledEcrecover(t *testing.T) {
for _, test := range ecRecoverTests {
testPrecompiled("01", test, t)
}
}

@ -0,0 +1,92 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"fmt"
"github.com/harmony-one/harmony/internal/params"
)
// EnableEIP enables the given EIP on the config.
// This operation writes in-place, and callers need to ensure that the globally
// defined jump tables are not polluted.
func EnableEIP(eipNum int, jt *JumpTable) error {
switch eipNum {
case 2200:
enable2200(jt)
case 1884:
enable1884(jt)
case 1344:
enable1344(jt)
default:
return fmt.Errorf("undefined eip %d", eipNum)
}
return nil
}
// enable1884 applies EIP-1884 to the given jump table:
// - Increase cost of BALANCE to 700
// - Increase cost of EXTCODEHASH to 700
// - Increase cost of SLOAD to 800
// - Define SELFBALANCE, with cost GasFastStep (5)
func enable1884(jt *JumpTable) {
// Gas cost changes
jt[BALANCE].constantGas = params.BalanceGasEIP1884
jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
jt[SLOAD].constantGas = params.SloadGasEIP1884
// New opcode
jt[SELFBALANCE] = operation{
execute: opSelfBalance,
constantGas: GasFastStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
valid: true,
}
}
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(contract.Address()))
stack.push(balance)
return nil, nil
}
// enable1344 applies EIP-1344 (ChainID Opcode)
// - Adds an opcode that returns the current chain’s EIP-155 unique identifier
func enable1344(jt *JumpTable) {
// New opcode
jt[CHAINID] = operation{
execute: opChainID,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
valid: true,
}
}
// opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
chainID := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
stack.push(chainID)
return nil, nil
}
// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE)
func enable2200(jt *JumpTable) {
jt[SSTORE].dynamicGas = gasSStoreEIP2200
}

@ -51,6 +51,9 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
if evm.ChainConfig().IsS3(evm.EpochNumber) { if evm.ChainConfig().IsS3(evm.EpochNumber) {
precompiles = PrecompiledContractsByzantium precompiles = PrecompiledContractsByzantium
} }
if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul
}
if p := precompiles[*contract.CodeAddr]; p != nil { if p := precompiles[*contract.CodeAddr]; p != nil {
return RunPrecompiledContract(p, input, contract) return RunPrecompiledContract(p, input, contract)
} }
@ -219,6 +222,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.ChainConfig().IsS3(evm.EpochNumber) { if evm.ChainConfig().IsS3(evm.EpochNumber) {
precompiles = PrecompiledContractsByzantium precompiles = PrecompiledContractsByzantium
} }
if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul
}
if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 { if precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer // Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 { if evm.vmConfig.Debug && evm.depth == 0 {

@ -18,8 +18,6 @@ package vm
import ( import (
"math/big" "math/big"
"github.com/harmony-one/harmony/internal/params"
) )
// Gas costs // Gas costs
@ -30,28 +28,24 @@ const (
GasMidStep uint64 = 8 GasMidStep uint64 = 8
GasSlowStep uint64 = 10 GasSlowStep uint64 = 10
GasExtStep uint64 = 20 GasExtStep uint64 = 20
GasReturn uint64 = 0
GasStop uint64 = 0
GasContractByte uint64 = 200
) )
// calcGas returns the actual gas cost of the call. // calcGas returns the actual gas cost of the call.
// //
// The cost of gas was changed during the homestead price change HF. To allow for EIP150 // The cost of gas was changed during the homestead price change HF.
// to be implemented. The returned gas is gas - base * 63 / 64. // As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) { func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
if gasTable.CreateBySuicide > 0 { if isEip150 {
availableGas = availableGas - base availableGas = availableGas - base
gas := availableGas - availableGas/64 gas := availableGas - availableGas/64
// If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150 // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
// is smaller than the requested amount. Therefor we return the new gas instead // is smaller than the requested amount. Therefor we return the new gas instead
// of returning an error. // of returning an error.
if callCost.BitLen() > 64 || gas < callCost.Uint64() { if !callCost.IsUint64() || gas < callCost.Uint64() {
return gas, nil return gas, nil
} }
} }
if callCost.BitLen() > 64 { if !callCost.IsUint64() {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }

@ -17,29 +17,27 @@
package vm package vm
import ( import (
"errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
) )
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so // memoryGasCost calculates the quadratic gas for memory expansion. It does so
// only for the memory region that is expanded, not the total memory. // only for the memory region that is expanded, not the total memory.
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) { func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
if newMemSize == 0 { if newMemSize == 0 {
return 0, nil return 0, nil
} }
// The maximum that will fit in a uint64 is max_word_count - 1 // The maximum that will fit in a uint64 is max_word_count - 1. Anything above
// anything above that will result in an overflow. // that will result in an overflow. Additionally, a newMemSize which results in
// Additionally, a newMemSize which results in a // a newMemSizeWords larger than 0xFFFFFFFF will cause the square operation to
// newMemSizeWords larger than 0x7ffffffff will cause the square operation // overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
// to overflow. // without overflowing the gas calculation.
// The constant 0xffffffffe0 is the highest number that can be used without if newMemSize > 0x1FFFFFFFE0 {
// overflowing the gas calculation
if newMemSize > 0xffffffffe0 {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
newMemSizeWords := toWordSize(newMemSize) newMemSizeWords := toWordSize(newMemSize)
newMemSize = newMemSizeWords * 32 newMemSize = newMemSizeWords * 32
@ -57,65 +55,45 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
return 0, nil return 0, nil
} }
func constGasFunc(gas uint64) gasFunc { // memoryCopierGas creates the gas functions for the following opcodes, and takes
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // the stack position of the operand which determines the size of the data to copy
return gas, nil // as argument:
} // CALLDATACOPY (stack position 2)
} // CODECOPY (stack position 2)
// EXTCODECOPY (stack poition 3)
func gasCallDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // RETURNDATACOPY (stack position 2)
gas, err := memoryGasCost(mem, memorySize) func memoryCopierGas(stackpos int) gasFunc {
if err != nil { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return 0, err // Gas for expanding the memory
} gas, err := memoryGasCost(mem, memorySize)
if err != nil {
var overflow bool return 0, err
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow { }
return 0, errGasUintOverflow // And gas for copying data, charged per word at param.CopyGas
} words, overflow := bigUint64(stack.Back(stackpos))
if overflow {
words, overflow := bigUint64(stack.Back(2)) return 0, errGasUintOverflow
if overflow { }
return 0, errGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow { if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
if gas, overflow = math.SafeAdd(gas, words); overflow { if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
}
return gas, nil
} }
return gas, nil
} }
func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var (
gas, err := memoryGasCost(mem, memorySize) gasCallDataCopy = memoryCopierGas(2)
if err != nil { gasCodeCopy = memoryCopierGas(2)
return 0, err gasExtCodeCopy = memoryCopierGas(3)
} gasReturnDataCopy = memoryCopierGas(2)
)
var overflow bool
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
words, overflow := bigUint64(stack.Back(2))
if overflow {
return 0, errGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
y, x = stack.Back(1), stack.Back(0) y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
@ -184,8 +162,63 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
return params.NetSstoreDirtyGas, nil return params.NetSstoreDirtyGas, nil
} }
// 0. If *gasleft* is less than or equal to 2300, fail the current call.
// 1. If current value equals new value (this is a no-op), SSTORE_NOOP_GAS gas is deducted.
// 2. If current value does not equal new value:
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
// 2.1.1. If original value is 0, SSTORE_INIT_GAS gas is deducted.
// 2.1.2. Otherwise, SSTORE_CLEAN_GAS gas is deducted. If new value is 0, add SSTORE_CLEAR_REFUND to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), SSTORE_DIRTY_GAS gas is deducted. Apply both of the following clauses:
// 2.2.1. If original value is not 0:
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEAR_REFUND gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEAR_REFUND gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset):
// 2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
// 2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
return 0, errors.New("not enough gas for reentrancy sentry")
}
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.Back(0)
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
)
value := common.BigToHash(y)
if current == value { // noop (1)
return params.SstoreNoopGasEIP2200, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreInitGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
}
return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.SstoreClearRefundEIP2200)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200)
}
}
return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc { func makeGasLog(n uint64) gasFunc {
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
requestedSize, overflow := bigUint64(stack.Back(1)) requestedSize, overflow := bigUint64(stack.Back(1))
if overflow { if overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
@ -214,17 +247,11 @@ func makeGasLog(n uint64) gasFunc {
} }
} }
func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(1)) wordGas, overflow := bigUint64(stack.Back(1))
if overflow { if overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
@ -238,117 +265,27 @@ func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
return gas, nil return gas, nil
} }
func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { // pureMemoryGascost is used by several operations, which aside from their
gas, err := memoryGasCost(mem, memorySize) // static cost have a dynamic cost which is solely based on the memory
if err != nil { // expansion
return 0, err func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
} return memoryGasCost(mem, memorySize)
var overflow bool
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(2))
if overflow {
return 0, errGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, err
}
var overflow bool
if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(3))
if overflow {
return 0, errGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasExtCodeHash(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gt.ExtcodeHash, nil
}
func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
return gas, nil
}
func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize)
if err != nil {
return 0, errGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
return 0, errGasUintOverflow
}
return gas, nil
} }
func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var (
var overflow bool gasReturn = pureMemoryGascost
gas, err := memoryGasCost(mem, memorySize) gasRevert = pureMemoryGascost
if err != nil { gasMLoad = pureMemoryGascost
return 0, err gasMStore8 = pureMemoryGascost
} gasMStore = pureMemoryGascost
if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow { gasCreate = pureMemoryGascost
return 0, errGasUintOverflow )
}
return gas, nil
}
func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var overflow bool
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if gas, overflow = math.SafeAdd(gas, params.Create2Gas); overflow {
return 0, errGasUintOverflow
}
wordGas, overflow := bigUint64(stack.Back(2)) wordGas, overflow := bigUint64(stack.Back(2))
if overflow { if overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
@ -359,43 +296,42 @@ func gasCreate2(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
if gas, overflow = math.SafeAdd(gas, wordGas); overflow { if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
return gas, nil return gas, nil
} }
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gt.Balance, nil expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
}
func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return gt.ExtcodeSize, nil
}
func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var (
return gt.SLoad, nil gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
overflow bool
)
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return 0, errGasUintOverflow
}
return gas, nil
} }
func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
var ( var (
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
overflow bool overflow bool
) )
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow { if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
return gas, nil return gas, nil
} }
func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
gas = gt.Calls gas uint64
transfersValue = stack.Back(2).Sign() != 0 transfersValue = stack.Back(2).Sign() != 0
address = common.BigToAddress(stack.Back(1)) address = common.BigToAddress(stack.Back(1))
eip158 = evm.ChainConfig().IsS3(evm.EpochNumber)
) )
if eip158 { if evm.chainRules.IsS3 {
if transfersValue && evm.StateDB.Empty(address) { if transfersValue && evm.StateDB.Empty(address) {
gas += params.CallNewAccountGas gas += params.CallNewAccountGas
} }
@ -414,7 +350,7 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0)) evm.callGasTemp, err = callGas(evm.chainRules.IsS3, contract.Gas, gas, stack.Back(0))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -424,21 +360,22 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
return gas, nil return gas, nil
} }
func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas := gt.Calls
if stack.Back(2).Sign() != 0 {
gas += params.CallValueTransferGas
}
memoryGas, err := memoryGasCost(mem, memorySize) memoryGas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
var overflow bool var (
gas uint64
overflow bool
)
if stack.Back(2).Sign() != 0 {
gas += params.CallValueTransferGas
}
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
evm.callGasTemp, err = callGas(evm.chainRules.IsS3, contract.Gas, gas, stack.Back(0))
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -448,88 +385,57 @@ func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack,
return gas, nil return gas, nil
} }
func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryGasCost(mem, memorySize)
}
func gasRevert(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return memoryGasCost(mem, memorySize)
}
func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var gas uint64
// EIP150 homestead gas reprice fork:
if evm.ChainConfig().IsS3(evm.EpochNumber) {
gas = gt.Suicide
var (
address = common.BigToAddress(stack.Back(0))
eip158 = evm.ChainConfig().IsS3(evm.EpochNumber)
)
if eip158 {
// if empty and transfers value
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += gt.CreateBySuicide
}
} else if !evm.StateDB.Exist(address) {
gas += gt.CreateBySuicide
}
}
if !evm.StateDB.HasSuicided(contract.Address()) {
evm.StateDB.AddRefund(params.SuicideRefundGas)
}
return gas, nil
}
func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
var overflow bool evm.callGasTemp, err = callGas(evm.chainRules.IsS3, contract.Gas, gas, stack.Back(0))
if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow {
return 0, errGasUintOverflow
}
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil { if err != nil {
return 0, err return 0, err
} }
var overflow bool
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
return gas, nil return gas, nil
} }
func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
gas, err := memoryGasCost(mem, memorySize) gas, err := memoryGasCost(mem, memorySize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
var overflow bool evm.callGasTemp, err = callGas(evm.chainRules.IsS3, contract.Gas, gas, stack.Back(0))
if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow {
return 0, errGasUintOverflow
}
evm.callGasTemp, err = callGas(gt, contract.Gas, gas, stack.Back(0))
if err != nil { if err != nil {
return 0, err return 0, err
} }
var overflow bool
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow { if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
return 0, errGasUintOverflow return 0, errGasUintOverflow
} }
return gas, nil return gas, nil
} }
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
return GasFastestStep, nil var gas uint64
} // EIP150 homestead gas reprice fork:
if evm.chainRules.IsS3 {
gas = params.SelfdestructGasEIP150
var address = common.BigToAddress(stack.Back(0))
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { if evm.chainRules.IsS3 {
return GasFastestStep, nil // if empty and transfers value
} if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
gas += params.CreateBySelfdestructGas
}
} else if !evm.StateDB.Exist(address) {
gas += params.CreateBySelfdestructGas
}
}
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { if !evm.StateDB.HasSuicided(contract.Address()) {
return GasFastestStep, nil evm.StateDB.AddRefund(params.SelfdestructRefundGas)
}
return gas, nil
} }

@ -16,21 +16,95 @@
package vm package vm
import "testing" import (
"math"
"math/big"
"testing"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/harmony-one/harmony/core/state"
)
func TestMemoryGasCost(t *testing.T) { func TestMemoryGasCost(t *testing.T) {
//size := uint64(math.MaxUint64 - 64) tests := []struct {
size := uint64(0xffffffffe0) size uint64
v, err := memoryGasCost(&Memory{}, size) cost uint64
if err != nil { overflow bool
t.Error("didn't expect error:", err) }{
{0x1fffffffe0, 36028809887088637, false},
{0x1fffffffe1, 0, true},
} }
if v != 36028899963961341 { for i, tt := range tests {
t.Errorf("Expected: 36028899963961341, got %d", v) v, err := memoryGasCost(&Memory{}, tt.size)
if (err == errGasUintOverflow) != tt.overflow {
t.Errorf("test %d: overflow mismatch: have %v, want %v", i, err == errGasUintOverflow, tt.overflow)
}
if v != tt.cost {
t.Errorf("test %d: gas cost mismatch: have %v, want %v", i, v, tt.cost)
}
} }
}
var eip2200Tests = []struct {
original byte
gaspool uint64
input string
used uint64
refund uint64
failure error
}{
{0, math.MaxUint64, "0x60006000556000600055", 1612, 0, nil}, // 0 -> 0 -> 0
{0, math.MaxUint64, "0x60006000556001600055", 20812, 0, nil}, // 0 -> 0 -> 1
{0, math.MaxUint64, "0x60016000556000600055", 20812, 19200, nil}, // 0 -> 1 -> 0
{0, math.MaxUint64, "0x60016000556002600055", 20812, 0, nil}, // 0 -> 1 -> 2
{0, math.MaxUint64, "0x60016000556001600055", 20812, 0, nil}, // 0 -> 1 -> 1
{1, math.MaxUint64, "0x60006000556000600055", 5812, 15000, nil}, // 1 -> 0 -> 0
{1, math.MaxUint64, "0x60006000556001600055", 5812, 4200, nil}, // 1 -> 0 -> 1
{1, math.MaxUint64, "0x60006000556002600055", 5812, 0, nil}, // 1 -> 0 -> 2
{1, math.MaxUint64, "0x60026000556000600055", 5812, 15000, nil}, // 1 -> 2 -> 0
{1, math.MaxUint64, "0x60026000556003600055", 5812, 0, nil}, // 1 -> 2 -> 3
{1, math.MaxUint64, "0x60026000556001600055", 5812, 4200, nil}, // 1 -> 2 -> 1
{1, math.MaxUint64, "0x60026000556002600055", 5812, 0, nil}, // 1 -> 2 -> 2
{1, math.MaxUint64, "0x60016000556000600055", 5812, 15000, nil}, // 1 -> 1 -> 0
{1, math.MaxUint64, "0x60016000556002600055", 5812, 0, nil}, // 1 -> 1 -> 2
{1, math.MaxUint64, "0x60016000556001600055", 1612, 0, nil}, // 1 -> 1 -> 1
{0, math.MaxUint64, "0x600160005560006000556001600055", 40818, 19200, nil}, // 0 -> 1 -> 0 -> 1
{1, math.MaxUint64, "0x600060005560016000556000600055", 10818, 19200, nil}, // 1 -> 0 -> 1 -> 0
{1, 2306, "0x6001600055", 2306, 0, ErrOutOfGas}, // 1 -> 1 (2300 sentry + 2xPUSH)
{1, 2307, "0x6001600055", 806, 0, nil}, // 1 -> 1 (2301 sentry + 2xPUSH)
}
func TestEIP2200(t *testing.T) {
for i, tt := range eip2200Tests {
address := common.BytesToAddress([]byte("contract"))
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
statedb.CreateAccount(address)
statedb.SetCode(address, hexutil.MustDecode(tt.input))
statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
statedb.Finalise(true) // Push the state into the "original" slot
vmctx := Context{
CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
Transfer: func(StateDB, common.Address, common.Address, *big.Int, types.TransactionType) {},
IsValidator: func(StateDB, common.Address) bool { return false },
}
vmenv := NewEVM(vmctx, statedb, params.AllProtocolChanges, Config{ExtraEips: []int{2200}})
_, err = memoryGasCost(&Memory{}, size+1) _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
if err == nil { if err != tt.failure {
t.Error("expected error") t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
}
if used := tt.gaspool - gas; used != tt.used {
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
}
if refund := vmenv.StateDB.GetRefund(); refund != tt.refund {
t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund)
}
} }
} }

@ -18,7 +18,6 @@ package vm
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -35,6 +34,7 @@ var (
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds") errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
errExecutionReverted = errors.New("evm: execution reverted") errExecutionReverted = errors.New("evm: execution reverted")
errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded") 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) { func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
@ -384,7 +384,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
offset, size := stack.pop(), stack.pop() offset, size := stack.pop(), stack.pop()
data := memory.Get(offset.Int64(), size.Int64()) data := memory.GetPtr(offset.Int64(), size.Int64())
if interpreter.hasher == nil { if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState) interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@ -405,7 +405,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
} }
func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(contract.Address().Big()) stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
return nil, nil return nil, nil
} }
@ -416,12 +416,12 @@ func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
} }
func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(interpreter.evm.Origin.Big()) stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil return nil, nil
} }
func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(contract.Caller().Big()) stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
return nil, nil return nil, nil
} }
@ -467,7 +467,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
) )
defer interpreter.intPool.put(memOffset, dataOffset, length, end) defer interpreter.intPool.put(memOffset, dataOffset, length, end)
if end.BitLen() > 64 || uint64(len(interpreter.returnData)) < end.Uint64() { if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
return nil, errReturnDataOutOfBounds return nil, errReturnDataOutOfBounds
} }
memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()]) memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
@ -572,7 +572,7 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, me
} }
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(interpreter.evm.Coinbase.Big()) stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil return nil, nil
} }
@ -587,7 +587,6 @@ func opNumber(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
} }
func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// TODO: remove this op (add other ops like rand)
stack.push(math.U256(interpreter.intPool.get().Set(big.NewInt(0)))) stack.push(math.U256(interpreter.intPool.get().Set(big.NewInt(0))))
return nil, nil return nil, nil
} }
@ -603,11 +602,9 @@ func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
} }
func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
offset := stack.pop() v := stack.peek()
val := interpreter.intPool.get().SetBytes(memory.Get(offset.Int64(), 32)) offset := v.Int64()
stack.push(val) v.SetBytes(memory.GetPtr(offset, 32))
interpreter.intPool.put(offset)
return nil, nil return nil, nil
} }
@ -646,8 +643,7 @@ func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
pos := stack.pop() pos := stack.pop()
if !contract.validJumpdest(pos) { if !contract.validJumpdest(pos) {
nop := contract.GetOp(pos.Uint64()) return nil, errInvalidJump
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
} }
*pc = pos.Uint64() *pc = pos.Uint64()
@ -659,8 +655,7 @@ func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
pos, cond := stack.pop(), stack.pop() pos, cond := stack.pop(), stack.pop()
if cond.Sign() != 0 { if cond.Sign() != 0 {
if !contract.validJumpdest(pos) { if !contract.validJumpdest(pos) {
nop := contract.GetOp(pos.Uint64()) return nil, errInvalidJump
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
} }
*pc = pos.Uint64() *pc = pos.Uint64()
} else { } else {
@ -694,7 +689,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
var ( var (
value = stack.pop() value = stack.pop()
offset, size = stack.pop(), stack.pop() offset, size = stack.pop(), stack.pop()
input = memory.Get(offset.Int64(), size.Int64()) input = memory.GetCopy(offset.Int64(), size.Int64())
gas = contract.Gas gas = contract.Gas
) )
if interpreter.evm.ChainConfig().IsS3(interpreter.evm.EpochNumber) { if interpreter.evm.ChainConfig().IsS3(interpreter.evm.EpochNumber) {
@ -712,7 +707,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas { } else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
stack.push(interpreter.intPool.getZero()) stack.push(interpreter.intPool.getZero())
} else { } else {
stack.push(addr.Big()) stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
} }
contract.Gas += returnGas contract.Gas += returnGas
interpreter.intPool.put(value, offset, size) interpreter.intPool.put(value, offset, size)
@ -728,7 +723,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
endowment = stack.pop() endowment = stack.pop()
offset, size = stack.pop(), stack.pop() offset, size = stack.pop(), stack.pop()
salt = stack.pop() salt = stack.pop()
input = memory.Get(offset.Int64(), size.Int64()) input = memory.GetCopy(offset.Int64(), size.Int64())
gas = contract.Gas gas = contract.Gas
) )
@ -740,7 +735,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
if suberr != nil { if suberr != nil {
stack.push(interpreter.intPool.getZero()) stack.push(interpreter.intPool.getZero())
} else { } else {
stack.push(addr.Big()) stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
} }
contract.Gas += returnGas contract.Gas += returnGas
interpreter.intPool.put(endowment, offset, size, salt) interpreter.intPool.put(endowment, offset, size, salt)
@ -760,7 +755,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
toAddr := common.BigToAddress(addr) toAddr := common.BigToAddress(addr)
value = math.U256(value) value = math.U256(value)
// Get the arguments from the memory. // Get the arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64()) args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 { if value.Sign() != 0 {
gas += params.CallStipend gas += params.CallStipend
@ -789,7 +784,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, mem
toAddr := common.BigToAddress(addr) toAddr := common.BigToAddress(addr)
value = math.U256(value) value = math.U256(value)
// Get arguments from the memory. // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64()) args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
if value.Sign() != 0 { if value.Sign() != 0 {
gas += params.CallStipend gas += params.CallStipend
@ -817,7 +812,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.BigToAddress(addr)
// Get arguments from the memory. // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64()) args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas) ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas)
if err != nil { if err != nil {
@ -842,7 +837,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, m
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.BigToAddress(addr) toAddr := common.BigToAddress(addr)
// Get arguments from the memory. // Get arguments from the memory.
args := memory.Get(inOffset.Int64(), inSize.Int64()) args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas) ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas)
if err != nil { if err != nil {
@ -898,7 +893,7 @@ func makeLog(size int) executionFunc {
topics[i] = common.BigToHash(stack.pop()) topics[i] = common.BigToHash(stack.pop())
} }
d := memory.Get(mStart.Int64(), mSize.Int64()) d := memory.GetCopy(mStart.Int64(), mSize.Int64())
interpreter.evm.StateDB.AddLog(&types.Log{ interpreter.evm.StateDB.AddLog(&types.Log{
Address: contract.Address(), Address: contract.Address(),
Topics: topics, Topics: topics,
@ -913,6 +908,21 @@ func makeLog(size int) executionFunc {
} }
} }
// opPush1 is a specialized version of pushN
func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
codeLen = uint64(len(contract.Code))
integer = interpreter.intPool.get()
)
*pc++
if *pc < codeLen {
stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
} else {
stack.push(integer.SetUint64(0))
}
return nil, nil
}
// make push instruction function // make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc { func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {

@ -18,6 +18,9 @@ package vm
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt"
"io/ioutil"
"math/big" "math/big"
"testing" "testing"
@ -26,32 +29,91 @@ import (
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
) )
type twoOperandTest struct { type TwoOperandTestcase struct {
x string X string
y string Y string
expected string Expected string
} }
func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)) { type twoOperandParams struct {
x string
y string
}
var commonParams []*twoOperandParams
var twoOpMethods map[string]executionFunc
func init() {
// Params is a list of common edgecases that should be used for some common tests
params := []string{
"0000000000000000000000000000000000000000000000000000000000000000", // 0
"0000000000000000000000000000000000000000000000000000000000000001", // +1
"0000000000000000000000000000000000000000000000000000000000000005", // +5
"7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", // + max -1
"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // + max
"8000000000000000000000000000000000000000000000000000000000000000", // - max
"8000000000000000000000000000000000000000000000000000000000000001", // - max+1
"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", // - 5
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // - 1
}
// Params are combined so each param is used on each 'side'
commonParams = make([]*twoOperandParams, len(params)*len(params))
for i, x := range params {
for j, y := range params {
commonParams[i*len(params)+j] = &twoOperandParams{x, y}
}
}
twoOpMethods = map[string]executionFunc{
"add": opAdd,
"sub": opSub,
"mul": opMul,
"div": opDiv,
"sdiv": opSdiv,
"mod": opMod,
"smod": opSmod,
"exp": opExp,
"signext": opSignExtend,
"lt": opLt,
"gt": opGt,
"slt": opSlt,
"sgt": opSgt,
"eq": opEq,
"and": opAnd,
"or": opOr,
"xor": opXor,
"byte": opByte,
"shl": opSHL,
"shr": opSHR,
"sar": opSAR,
}
}
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
var ( var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack() stack = newstack()
pc = uint64(0) pc = uint64(0)
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) evmInterpreter = env.interpreter.(*EVMInterpreter)
) )
// Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero
env.interpreter = evmInterpreter
evmInterpreter.intPool = poolOfIntPools.get() evmInterpreter.intPool = poolOfIntPools.get()
evmInterpreter.intPool.put(big.NewInt(-1337))
evmInterpreter.intPool.put(big.NewInt(-1337))
evmInterpreter.intPool.put(big.NewInt(-1337))
for i, test := range tests { for i, test := range tests {
x := new(big.Int).SetBytes(common.Hex2Bytes(test.x)) x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
shift := new(big.Int).SetBytes(common.Hex2Bytes(test.y)) y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
expected := new(big.Int).SetBytes(common.Hex2Bytes(test.expected)) expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x) stack.push(x)
stack.push(shift) stack.push(y)
opFn(&pc, evmInterpreter, nil, nil, stack) opFn(&pc, evmInterpreter, nil, nil, stack)
actual := stack.pop() actual := stack.pop()
if actual.Cmp(expected) != 0 { if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %d, expected %v, got %v", i, expected, actual) t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
} }
// Check pool usage // Check pool usage
// 1.pool is not allowed to contain anything on the stack // 1.pool is not allowed to contain anything on the stack
@ -64,7 +126,7 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
for evmInterpreter.intPool.pool.len() > 0 { for evmInterpreter.intPool.pool.len() > 0 {
key := evmInterpreter.intPool.get() key := evmInterpreter.intPool.get()
if _, exist := poolvals[key]; exist { if _, exist := poolvals[key]; exist {
t.Errorf("Testcase %d, pool contains double-entry", i) t.Errorf("Testcase %v %d, pool contains double-entry", name, i)
} }
poolvals[key] = struct{}{} poolvals[key] = struct{}{}
} }
@ -74,47 +136,22 @@ func testTwoOperandOp(t *testing.T, tests []twoOperandTest, opFn func(pc *uint64
} }
func TestByteOp(t *testing.T) { func TestByteOp(t *testing.T) {
var ( tests := []TwoOperandTestcase{
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) {"ABCDEF0908070605040302010000000000000000000000000000000000000000", "00", "AB"},
stack = newstack() {"ABCDEF0908070605040302010000000000000000000000000000000000000000", "01", "CD"},
evmInterpreter = NewEVMInterpreter(env, env.vmConfig) {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "00", "00"},
) {"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", "01", "CD"},
{"0000000000000000000000000000000000000000000000000000000000102030", "1F", "30"},
env.interpreter = evmInterpreter {"0000000000000000000000000000000000000000000000000000000000102030", "1E", "20"},
evmInterpreter.intPool = poolOfIntPools.get() {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "20", "00"},
tests := []struct { {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "FFFFFFFFFFFFFFFF", "00"},
v string
th uint64
expected *big.Int
}{
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 0, big.NewInt(0xAB)},
{"ABCDEF0908070605040302010000000000000000000000000000000000000000", 1, big.NewInt(0xCD)},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 0, big.NewInt(0x00)},
{"00CDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff", 1, big.NewInt(0xCD)},
{"0000000000000000000000000000000000000000000000000000000000102030", 31, big.NewInt(0x30)},
{"0000000000000000000000000000000000000000000000000000000000102030", 30, big.NewInt(0x20)},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 32, big.NewInt(0x0)},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0xFFFFFFFFFFFFFFFF, big.NewInt(0x0)},
} }
pc := uint64(0) testTwoOperandOp(t, tests, opByte, "byte")
for _, test := range tests {
val := new(big.Int).SetBytes(common.Hex2Bytes(test.v))
th := new(big.Int).SetUint64(test.th)
stack.push(val)
stack.push(th)
opByte(&pc, evmInterpreter, nil, nil, stack)
actual := stack.pop()
if actual.Cmp(test.expected) != 0 {
t.Fatalf("Expected [%v] %v:th byte to be %v, was %v.", test.v, test.th, test.expected, actual)
}
}
poolOfIntPools.put(evmInterpreter.intPool)
} }
func TestSHL(t *testing.T) { func TestSHL(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shl-shift-left
tests := []twoOperandTest{ tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"}, {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000002"},
{"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "ff", "8000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
@ -126,12 +163,12 @@ func TestSHL(t *testing.T) {
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "01", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"},
} }
testTwoOperandOp(t, tests, opSHL) testTwoOperandOp(t, tests, opSHL, "shl")
} }
func TestSHR(t *testing.T) { func TestSHR(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#shr-logical-shift-right
tests := []twoOperandTest{ tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000000", "01", "4000000000000000000000000000000000000000000000000000000000000000"},
@ -144,12 +181,12 @@ func TestSHR(t *testing.T) {
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
{"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000000", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
} }
testTwoOperandOp(t, tests, opSHR) testTwoOperandOp(t, tests, opSHR, "shr")
} }
func TestSAR(t *testing.T) { func TestSAR(t *testing.T) {
// Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right // Testcases from https://github.com/ethereum/EIPs/blob/master/EIPS/eip-145.md#sar-arithmetic-shift-right
tests := []twoOperandTest{ tests := []TwoOperandTestcase{
{"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"}, {"0000000000000000000000000000000000000000000000000000000000000001", "00", "0000000000000000000000000000000000000000000000000000000000000001"},
{"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "01", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000000", "01", "c000000000000000000000000000000000000000000000000000000000000000"},
@ -168,44 +205,59 @@ func TestSAR(t *testing.T) {
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0100", "0000000000000000000000000000000000000000000000000000000000000000"},
} }
testTwoOperandOp(t, tests, opSAR) testTwoOperandOp(t, tests, opSAR, "sar")
} }
func TestSGT(t *testing.T) { // getResult is a convenience function to generate the expected values
tests := []twoOperandTest{ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
stack = newstack()
pc = uint64(0)
interpreter = env.interpreter.(*EVMInterpreter)
)
interpreter.intPool = poolOfIntPools.get()
result := make([]TwoOperandTestcase, len(args))
for i, param := range args {
x := new(big.Int).SetBytes(common.Hex2Bytes(param.x))
y := new(big.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x)
stack.push(y)
opFn(&pc, interpreter, nil, nil, stack)
actual := stack.pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
}
return result
}
// utility function to fill the json-file with testcases
// Enable this test to generate the 'testcases_xx.json' files
func TestWriteExpectedValues(t *testing.T) {
t.Skip("Enable this test to create json test cases.")
{"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, for name, method := range twoOpMethods {
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, data, err := json.Marshal(getResult(commonParams, method))
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, if err != nil {
{"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, t.Fatal(err)
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, }
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, _ = ioutil.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0644)
{"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, if err != nil {
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, t.Fatal(err)
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, }
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000001"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000000"},
} }
testTwoOperandOp(t, tests, opSgt) }
}
// TestJsonTestcases runs through all the testcases defined as json-files
func TestSLT(t *testing.T) { func TestJsonTestcases(t *testing.T) {
tests := []twoOperandTest{ for name := range twoOpMethods {
{"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, data, err := ioutil.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name))
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, if err != nil {
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, t.Fatal("Failed to read file", err)
{"0000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, }
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, var testcases []TwoOperandTestcase
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, json.Unmarshal(data, &testcases)
{"0000000000000000000000000000000000000000000000000000000000000001", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, testTwoOperandOp(t, testcases, twoOpMethods[name], name)
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000000"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000001"},
} }
testTwoOperandOp(t, tests, opSlt)
} }
func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) { func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error), args ...string) {
@ -458,12 +510,12 @@ func TestOpMstore(t *testing.T) {
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700" v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0)) stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
opMstore(&pc, evmInterpreter, nil, mem, stack) opMstore(&pc, evmInterpreter, nil, mem, stack)
if got := common.Bytes2Hex(mem.Get(0, 32)); got != v { if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v) t.Fatalf("Mstore fail, got %v, expected %v", got, v)
} }
stack.pushN(big.NewInt(0x1), big.NewInt(0)) stack.pushN(big.NewInt(0x1), big.NewInt(0))
opMstore(&pc, evmInterpreter, nil, mem, stack) opMstore(&pc, evmInterpreter, nil, mem, stack)
if common.Bytes2Hex(mem.Get(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" { if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value") t.Fatalf("Mstore failed to overwrite previous value")
} }
poolOfIntPools.put(evmInterpreter.intPool) poolOfIntPools.put(evmInterpreter.intPool)

@ -73,7 +73,7 @@ type StateDB interface {
AddLog(*types.Log) AddLog(*types.Log)
AddPreimage(common.Hash, []byte) AddPreimage(common.Hash, []byte)
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
} }
// CallContext provides a basic interface for the EVM calling conventions. The EVM // CallContext provides a basic interface for the EVM calling conventions. The EVM

@ -21,9 +21,10 @@ import (
"hash" "hash"
"sync/atomic" "sync/atomic"
"github.com/harmony-one/harmony/internal/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/harmony-one/harmony/internal/params"
) )
// Config are the configuration options for the Interpreter // Config are the configuration options for the Interpreter
@ -46,6 +47,9 @@ type Config struct {
EWASMInterpreter string EWASMInterpreter string
// Type of the EVM interpreter // Type of the EVM interpreter
EVMInterpreter string EVMInterpreter string
// ExtraEips the additional EIPS that are to be enabled
ExtraEips []int
} }
// Interpreter is used to run Ethereum based contracts and will utilise the // Interpreter is used to run Ethereum based contracts and will utilise the
@ -80,9 +84,8 @@ type keccakState interface {
// EVMInterpreter represents an EVM interpreter // EVMInterpreter represents an EVM interpreter
type EVMInterpreter struct { type EVMInterpreter struct {
evm *EVM evm *EVM
cfg Config cfg Config
gasTable params.GasTable
intPool *intPool intPool *intPool
@ -99,40 +102,29 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// the jump table was initialised. If it was not // the jump table was initialised. If it was not
// we'll set the default jump table. // we'll set the default jump table.
if !cfg.JumpTable[STOP].valid { if !cfg.JumpTable[STOP].valid {
//switch { var jt JumpTable
//case evm.ChainConfig().IsConstantinople(evm.BlockNumber): switch {
// cfg.JumpTable = constantinopleInstructionSet case evm.chainRules.IsIstanbul:
//case evm.ChainConfig().IsByzantium(evm.BlockNumber): jt = istanbulInstructionSet
// cfg.JumpTable = byzantiumInstructionSet case evm.chainRules.IsS3:
//case evm.ChainConfig().IsHomestead(evm.BlockNumber): jt = constantinopleInstructionSet
// cfg.JumpTable = homesteadInstructionSet default:
//default: jt = frontierInstructionSet
// cfg.JumpTable = frontierInstructionSet }
//} for i, eip := range cfg.ExtraEips {
cfg.JumpTable = constantinopleInstructionSet if err := EnableEIP(eip, &jt); err != nil {
// Disable it, so caller can check if it's activated or not
cfg.ExtraEips = append(cfg.ExtraEips[:i], cfg.ExtraEips[i+1:]...)
utils.Logger().Error().Int("eip", eip).Err(err).Msg("EIP activation failed")
}
}
cfg.JumpTable = jt
} }
return &EVMInterpreter{ return &EVMInterpreter{
evm: evm, evm: evm,
cfg: cfg, cfg: cfg,
gasTable: evm.ChainConfig().GasTable(evm.EpochNumber),
}
}
func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
if in.evm.chainRules.IsS3 {
if in.readOnly {
// If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).BitLen() > 0) {
return errWriteProtection
}
}
} }
return nil
} }
// Run loops and evaluates the contract's code with the given input data and returns // Run loops and evaluates the contract's code with the given input data and returns
@ -183,6 +175,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
pcCopy uint64 // needed for the deferred Tracer pcCopy uint64 // needed for the deferred Tracer
gasCopy uint64 // for Tracer to log gas remaining before execution gasCopy uint64 // for Tracer to log gas remaining before execution
logged bool // deferred Tracer should ignore already logged steps logged bool // deferred Tracer should ignore already logged steps
res []byte // result of the opcode execution function
) )
contract.Input = input contract.Input = input
@ -217,19 +210,36 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
if !operation.valid { if !operation.valid {
return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
} }
if err := operation.validateStack(stack); err != nil { // Validate stack
return nil, err if sLen := stack.len(); sLen < operation.minStack {
return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
} else if sLen > operation.maxStack {
return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
} }
// If the operation is valid, enforce and write restrictions // If the operation is valid, enforce and write restrictions
if err := in.enforceRestrictions(op, operation, stack); err != nil { if in.readOnly && in.evm.chainRules.IsS3 {
return nil, err // If the interpreter is operating in readonly mode, make sure no
// state-modifying operation is performed. The 3rd stack item
// for a call operation is the value. Transferring value from one
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
return nil, errWriteProtection
}
}
// Static portion of gas
cost = operation.constantGas // For tracing
if !contract.UseGas(operation.constantGas) {
return nil, ErrOutOfGas
} }
var memorySize uint64 var memorySize uint64
// calculate the new memory size and expand the memory to fit // calculate the new memory size and expand the memory to fit
// the operation // the operation
// Memory check needs to be done prior to evaluating the dynamic gas portion,
// to detect calculation overflows
if operation.memorySize != nil { if operation.memorySize != nil {
memSize, overflow := bigUint64(operation.memorySize(stack)) memSize, overflow := operation.memorySize(stack)
if overflow { if overflow {
return nil, errGasUintOverflow return nil, errGasUintOverflow
} }
@ -239,11 +249,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
return nil, errGasUintOverflow return nil, errGasUintOverflow
} }
} }
// Dynamic portion of gas
// consume the gas and return an error if not enough gas is available. // consume the gas and return an error if not enough gas is available.
// cost is explicitly set so that the capture state defer method can get the proper cost // cost is explicitly set so that the capture state defer method can get the proper cost
cost, err = operation.gasCost(in.gasTable, in.evm, contract, stack, mem, memorySize) if operation.dynamicGas != nil {
if err != nil || !contract.UseGas(cost) { var dynamicCost uint64
return nil, ErrOutOfGas dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // total cost, for debug tracing
if err != nil || !contract.UseGas(dynamicCost) {
return nil, ErrOutOfGas
}
} }
if memorySize > 0 { if memorySize > 0 {
mem.Resize(memorySize) mem.Resize(memorySize)
@ -255,7 +270,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} }
// execute the operation // execute the operation
res, err := operation.execute(&pc, in, contract, mem, stack) res, err = operation.execute(&pc, in, contract, mem, stack)
// verifyPool is a build flag. Pool verification makes sure the integrity // verifyPool is a build flag. Pool verification makes sure the integrity
// of the integer pool by comparing values to a default value. // of the integer pool by comparing values to a default value.
if verifyPool { if verifyPool {

File diff suppressed because it is too large Load Diff

@ -51,7 +51,7 @@ type LogConfig struct {
Limit int // maximum length of output, but zero means unlimited Limit int // maximum length of output, but zero means unlimited
} }
// no go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
// StructLog is emitted to the EVM each cycle and lists information about the current internal state // StructLog is emitted to the EVM each cycle and lists information about the current internal state
// prior to the execution of the statement. // prior to the execution of the statement.
@ -98,7 +98,7 @@ func (s *StructLog) ErrorString() string {
// Note that reference types are actual VM data structures; make copies // Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call. // if you need to retain them beyond the current call.
type Tracer interface { type Tracer interface {
CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
@ -158,7 +158,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
) )
l.changedValues[contract.Address()][address] = value l.changedValues[contract.Address()][address] = value
} }
// Copy a snapstot of the current memory state to a new buffer // Copy a snapshot of the current memory state to a new buffer
var mem []byte var mem []byte
if !l.cfg.DisableMemory { if !l.cfg.DisableMemory {
mem = make([]byte, len(memory.Data())) mem = make([]byte, len(memory.Data()))
@ -177,7 +177,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
if !l.cfg.DisableStorage { if !l.cfg.DisableStorage {
storage = l.changedValues[contract.Address()].Copy() storage = l.changedValues[contract.Address()].Copy()
} }
// create a new snaptshot of the EVM. // create a new snapshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err} log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log) l.logs = append(l.logs, log)

@ -69,8 +69,8 @@ func (m *Memory) Resize(size uint64) {
} }
} }
// Get returns offset + size as a new slice // GetCopy returns offset + size as a new slice
func (m *Memory) Get(offset, size int64) (cpy []byte) { func (m *Memory) GetCopy(offset, size int64) (cpy []byte) {
if size == 0 { if size == 0 {
return nil return nil
} }

@ -16,82 +16,98 @@
package vm package vm
import ( func memorySha3(stack *Stack) (uint64, bool) {
"math/big" return calcMemSize64(stack.Back(0), stack.Back(1))
"github.com/ethereum/go-ethereum/common/math"
)
func memorySha3(stack *Stack) *big.Int {
return calcMemSize(stack.Back(0), stack.Back(1))
} }
func memoryCallDataCopy(stack *Stack) *big.Int { func memoryCallDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryReturnDataCopy(stack *Stack) *big.Int { func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryCodeCopy(stack *Stack) *big.Int { func memoryCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(2)) return calcMemSize64(stack.Back(0), stack.Back(2))
} }
func memoryExtCodeCopy(stack *Stack) *big.Int { func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(3)) return calcMemSize64(stack.Back(1), stack.Back(3))
} }
func memoryMLoad(stack *Stack) *big.Int { func memoryMLoad(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(32)) return calcMemSize64WithUint(stack.Back(0), 32)
} }
func memoryMStore8(stack *Stack) *big.Int { func memoryMStore8(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(1)) return calcMemSize64WithUint(stack.Back(0), 1)
} }
func memoryMStore(stack *Stack) *big.Int { func memoryMStore(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), big.NewInt(32)) return calcMemSize64WithUint(stack.Back(0), 32)
} }
func memoryCreate(stack *Stack) *big.Int { func memoryCreate(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(2)) return calcMemSize64(stack.Back(1), stack.Back(2))
} }
func memoryCreate2(stack *Stack) *big.Int { func memoryCreate2(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(1), stack.Back(2)) return calcMemSize64(stack.Back(1), stack.Back(2))
} }
func memoryCall(stack *Stack) *big.Int { func memoryCall(stack *Stack) (uint64, bool) {
x := calcMemSize(stack.Back(5), stack.Back(6)) x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
y := calcMemSize(stack.Back(3), stack.Back(4)) if overflow {
return 0, true
return math.BigMax(x, y) }
y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
} }
func memoryDelegateCall(stack *Stack) (uint64, bool) {
func memoryDelegateCall(stack *Stack) *big.Int { x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
x := calcMemSize(stack.Back(4), stack.Back(5)) if overflow {
y := calcMemSize(stack.Back(2), stack.Back(3)) return 0, true
}
return math.BigMax(x, y) y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
} }
func memoryStaticCall(stack *Stack) *big.Int { func memoryStaticCall(stack *Stack) (uint64, bool) {
x := calcMemSize(stack.Back(4), stack.Back(5)) x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
y := calcMemSize(stack.Back(2), stack.Back(3)) if overflow {
return 0, true
return math.BigMax(x, y) }
y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
if overflow {
return 0, true
}
if x > y {
return x, false
}
return y, false
} }
func memoryReturn(stack *Stack) *big.Int { func memoryReturn(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(1)) return calcMemSize64(stack.Back(0), stack.Back(1))
} }
func memoryRevert(stack *Stack) *big.Int { func memoryRevert(stack *Stack) (uint64, bool) {
return calcMemSize(stack.Back(0), stack.Back(1)) return calcMemSize64(stack.Back(0), stack.Back(1))
} }
func memoryLog(stack *Stack) *big.Int { func memoryLog(stack *Stack) (uint64, bool) {
mSize, mStart := stack.Back(1), stack.Back(0) return calcMemSize64(stack.Back(0), stack.Back(1))
return calcMemSize(mStart, mSize)
} }

@ -101,6 +101,8 @@ const (
NUMBER NUMBER
DIFFICULTY DIFFICULTY
GASLIMIT GASLIMIT
CHAINID = 0x46
SELFBALANCE = 0x47
) )
// 0x50 range - 'storage' and execution. // 0x50 range - 'storage' and execution.
@ -271,12 +273,14 @@ var opCodeToString = map[OpCode]string{
EXTCODEHASH: "EXTCODEHASH", EXTCODEHASH: "EXTCODEHASH",
// 0x40 range - block operations. // 0x40 range - block operations.
BLOCKHASH: "BLOCKHASH", BLOCKHASH: "BLOCKHASH",
COINBASE: "COINBASE", COINBASE: "COINBASE",
TIMESTAMP: "TIMESTAMP", TIMESTAMP: "TIMESTAMP",
NUMBER: "NUMBER", NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY", DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
CHAINID: "CHAINID",
SELFBALANCE: "SELFBALANCE",
// 0x50 range - 'storage' and execution. // 0x50 range - 'storage' and execution.
POP: "POP", POP: "POP",
@ -428,6 +432,7 @@ var stringToOp = map[string]OpCode{
"CALLDATALOAD": CALLDATALOAD, "CALLDATALOAD": CALLDATALOAD,
"CALLDATASIZE": CALLDATASIZE, "CALLDATASIZE": CALLDATASIZE,
"CALLDATACOPY": CALLDATACOPY, "CALLDATACOPY": CALLDATACOPY,
"CHAINID": CHAINID,
"DELEGATECALL": DELEGATECALL, "DELEGATECALL": DELEGATECALL,
"STATICCALL": STATICCALL, "STATICCALL": STATICCALL,
"CODESIZE": CODESIZE, "CODESIZE": CODESIZE,
@ -444,6 +449,7 @@ var stringToOp = map[string]OpCode{
"NUMBER": NUMBER, "NUMBER": NUMBER,
"DIFFICULTY": DIFFICULTY, "DIFFICULTY": DIFFICULTY,
"GASLIMIT": GASLIMIT, "GASLIMIT": GASLIMIT,
"SELFBALANCE": SELFBALANCE,
"POP": POP, "POP": POP,
"MLOAD": MLOAD, "MLOAD": MLOAD,
"MSTORE": MSTORE, "MSTORE": MSTORE,

@ -22,8 +22,8 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
@ -101,7 +101,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.DB, error) {
setDefaults(cfg) setDefaults(cfg)
if cfg.State == nil { if cfg.State == nil {
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
} }
var ( var (
address = common.BytesToAddress([]byte("contract")) address = common.BytesToAddress([]byte("contract"))
@ -131,7 +131,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
setDefaults(cfg) setDefaults(cfg)
if cfg.State == nil { if cfg.State == nil {
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
} }
var ( var (
vmenv = NewEnv(cfg) vmenv = NewEnv(cfg)

@ -21,9 +21,10 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/vm" "github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/params"
@ -95,7 +96,7 @@ func TestExecute(t *testing.T) {
} }
func TestCall(t *testing.T) { func TestCall(t *testing.T) {
state, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) state, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
address := common.HexToAddress("0x0a") address := common.HexToAddress("0x0a")
state.SetCode(address, []byte{ state.SetCode(address, []byte{
byte(vm.PUSH1), 10, byte(vm.PUSH1), 10,
@ -151,7 +152,7 @@ func BenchmarkCall(b *testing.B) {
} }
func benchmarkEVMCreate(bench *testing.B, code string) { func benchmarkEVMCreate(bench *testing.B, code string) {
var ( var (
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver")) receiver = common.BytesToAddress([]byte("receiver"))
) )

@ -74,13 +74,6 @@ func (st *Stack) Back(n int) *big.Int {
return st.data[st.len()-n-1] return st.data[st.len()-n-1]
} }
func (st *Stack) require(n int) error {
if st.len() < n {
return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
}
return nil
}
// Print dumps the content of the stack // Print dumps the content of the stack
func (st *Stack) Print() { func (st *Stack) Print() {
fmt.Println("### stack ###") fmt.Println("### stack ###")

@ -17,28 +17,26 @@
package vm package vm
import ( import (
"fmt" "github.com/ethereum/go-ethereum/params"
"github.com/harmony-one/harmony/internal/params"
) )
func makeStackFunc(pop, push int) stackValidationFunc { func minSwapStack(n int) int {
return func(stack *Stack) error { return minStack(n, n)
if err := stack.require(pop); err != nil { }
return err func maxSwapStack(n int) int {
} return maxStack(n, n)
if stack.len()+push-pop > int(params.StackLimit) {
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
}
return nil
}
} }
func makeDupStackFunc(n int) stackValidationFunc { func minDupStack(n int) int {
return makeStackFunc(n, n+1) return minStack(n, n+1)
}
func maxDupStack(n int) int {
return maxStack(n, n+1)
} }
func makeSwapStackFunc(n int) stackValidationFunc { func maxStack(pop, push int) int {
return makeStackFunc(n, n) return int(params.StackLimit) + pop - push
}
func minStack(pops, push int) int {
return pops
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -3,25 +3,31 @@ module github.com/harmony-one/harmony
go 1.14 go 1.14
require ( require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/VictoriaMetrics/fastcache v1.5.7 // indirect
github.com/Workiva/go-datastructures v1.0.50 github.com/Workiva/go-datastructures v1.0.50
github.com/allegro/bigcache v1.2.1 // indirect github.com/allegro/bigcache v1.2.1 // indirect
github.com/aristanetworks/goarista v0.0.0-20190607111240-52c2a7864a08 // indirect github.com/aristanetworks/goarista v0.0.0-20190607111240-52c2a7864a08 // indirect
github.com/aws/aws-sdk-go v1.30.1 github.com/aws/aws-sdk-go v1.30.1
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d github.com/btcsuite/btcutil v1.0.2
github.com/cespare/cp v1.1.1 github.com/cespare/cp v1.1.1
github.com/coinbase/rosetta-sdk-go v0.3.4 github.com/coinbase/rosetta-sdk-go v0.4.4
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect
github.com/deckarep/golang-set v1.7.1 github.com/deckarep/golang-set v1.7.1
github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.9.18 github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa // indirect
github.com/ethereum/go-ethereum v1.9.21
github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a // indirect github.com/fjl/memsize v0.0.0-20180929194037-2a09253e352a // indirect
github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c // indirect github.com/garslo/gogen v0.0.0-20170307003452-d6ebae628c7c // indirect
github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
github.com/golang/mock v1.4.0 github.com/golang/mock v1.4.0
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 // indirect
github.com/golangci/golangci-lint v1.22.2 github.com/golangci/golangci-lint v1.22.2
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
github.com/harmony-ek/gencodec v0.0.0-20190215044613-e6740dbdd846 github.com/harmony-ek/gencodec v0.0.0-20190215044613-e6740dbdd846
github.com/harmony-one/abool v1.0.1 github.com/harmony-one/abool v1.0.1
@ -29,6 +35,7 @@ require (
github.com/harmony-one/taggedrlp v0.1.4 github.com/harmony-one/taggedrlp v0.1.4
github.com/harmony-one/vdf v0.0.0-20190924175951-620379da8849 github.com/harmony-one/vdf v0.0.0-20190924175951-620379da8849
github.com/hashicorp/golang-lru v0.5.4 github.com/hashicorp/golang-lru v0.5.4
github.com/holiman/uint256 v1.1.1 // indirect
github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 // indirect github.com/iancoleman/strcase v0.0.0-20190422225806-e506e3ef7365 // indirect
github.com/ipfs/go-ds-badger v0.2.4 github.com/ipfs/go-ds-badger v0.2.4
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
@ -45,6 +52,7 @@ require (
github.com/libp2p/go-libp2p-peer v0.2.0 // indirect github.com/libp2p/go-libp2p-peer v0.2.0 // indirect
github.com/libp2p/go-libp2p-peerstore v0.2.6 // indirect github.com/libp2p/go-libp2p-peerstore v0.2.6 // indirect
github.com/libp2p/go-libp2p-pubsub v0.3.3 github.com/libp2p/go-libp2p-pubsub v0.3.3
github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/multiformats/go-multiaddr v0.2.2 github.com/multiformats/go-multiaddr v0.2.2
github.com/multiformats/go-multiaddr-net v0.1.5 github.com/multiformats/go-multiaddr-net v0.1.5
github.com/natefinch/lumberjack v2.0.0+incompatible github.com/natefinch/lumberjack v2.0.0+incompatible
@ -55,25 +63,28 @@ require (
github.com/prometheus/procfs v0.0.3 // indirect github.com/prometheus/procfs v0.0.3 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
github.com/rjeczalik/notify v0.9.2 github.com/rjeczalik/notify v0.9.2
github.com/robertkrimen/otto v0.0.0-20170205013659-6a77b7cbc37d // indirect
github.com/rs/cors v1.7.0 // indirect github.com/rs/cors v1.7.0 // indirect
github.com/rs/zerolog v1.18.0 github.com/rs/zerolog v1.18.0
github.com/shirou/gopsutil v2.18.12+incompatible // indirect github.com/shirou/gopsutil v2.20.5+incompatible // indirect
github.com/spf13/cobra v0.0.5 github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.6.1 github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/lint v0.0.0-20200302205851-738671d3881b golang.org/x/lint v0.0.0-20200302205851-738671d3881b
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
golang.org/x/tools v0.0.0-20200408032209-46bd65c8538f golang.org/x/tools v0.0.0-20200904185747-39188db58858
google.golang.org/grpc v1.28.1 google.golang.org/grpc v1.28.1
google.golang.org/protobuf v1.23.0 google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect
gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect
gopkg.in/yaml.v2 v2.2.7 gopkg.in/yaml.v2 v2.3.0
) )
replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.8.27 replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.9.9

@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/common/bitutil"
"github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/bloombits"
ethRawDB "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
"github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core"
@ -103,7 +104,7 @@ func NewBloomIndexer(db ethdb.Database, size, confirms uint64) *core.ChainIndexe
db: db, db: db,
size: size, size: size,
} }
table := ethdb.NewTable(db, string(rawdb.BloomBitsIndexPrefix)) table := ethRawDB.NewTable(db, string(rawdb.BloomBitsIndexPrefix))
return core.NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits") return core.NewChainIndexer(db, table, backend, size, confirms, bloomThrottling, "bloombits")
} }

@ -35,6 +35,7 @@ var (
RedelegationEpoch: big.NewInt(290), RedelegationEpoch: big.NewInt(290),
EIP155Epoch: big.NewInt(28), EIP155Epoch: big.NewInt(28),
S3Epoch: big.NewInt(28), S3Epoch: big.NewInt(28),
IstanbulEpoch: big.NewInt(10000000),
ReceiptLogEpoch: big.NewInt(101), ReceiptLogEpoch: big.NewInt(101),
} }
@ -50,6 +51,7 @@ var (
RedelegationEpoch: big.NewInt(36500), RedelegationEpoch: big.NewInt(36500),
EIP155Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0), S3Epoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(43800),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
} }
@ -66,6 +68,7 @@ var (
RedelegationEpoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0), S3Epoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
} }
@ -82,6 +85,7 @@ var (
RedelegationEpoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0), S3Epoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
} }
@ -98,6 +102,7 @@ var (
RedelegationEpoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0), S3Epoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
} }
@ -113,6 +118,7 @@ var (
RedelegationEpoch: big.NewInt(0), RedelegationEpoch: big.NewInt(0),
EIP155Epoch: big.NewInt(0), EIP155Epoch: big.NewInt(0),
S3Epoch: big.NewInt(0), S3Epoch: big.NewInt(0),
IstanbulEpoch: big.NewInt(0),
ReceiptLogEpoch: big.NewInt(0), ReceiptLogEpoch: big.NewInt(0),
} }
@ -130,6 +136,7 @@ var (
big.NewInt(0), // RedelegationEpoch big.NewInt(0), // RedelegationEpoch
big.NewInt(0), // EIP155Epoch big.NewInt(0), // EIP155Epoch
big.NewInt(0), // S3Epoch big.NewInt(0), // S3Epoch
big.NewInt(0), // IstanbulEpoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
} }
@ -147,6 +154,7 @@ var (
big.NewInt(0), // RedelegationEpoch big.NewInt(0), // RedelegationEpoch
big.NewInt(0), // EIP155Epoch big.NewInt(0), // EIP155Epoch
big.NewInt(0), // S3Epoch big.NewInt(0), // S3Epoch
big.NewInt(0), // IstanbulEpoch
big.NewInt(0), // ReceiptLogEpoch big.NewInt(0), // ReceiptLogEpoch
} }
@ -206,6 +214,9 @@ type ChainConfig struct {
// S3 epoch is the first epoch containing S3 mainnet and all ethereum update up to Constantinople // S3 epoch is the first epoch containing S3 mainnet and all ethereum update up to Constantinople
S3Epoch *big.Int `json:"s3-epoch,omitempty"` S3Epoch *big.Int `json:"s3-epoch,omitempty"`
// Istanbul epoch
IstanbulEpoch *big.Int `json:"istanbul-epoch,omitempty"`
// ReceiptLogEpoch is the first epoch support receiptlog // ReceiptLogEpoch is the first epoch support receiptlog
ReceiptLogEpoch *big.Int `json:"receipt-log-epoch,omitempty"` ReceiptLogEpoch *big.Int `json:"receipt-log-epoch,omitempty"`
} }
@ -282,6 +293,11 @@ func (c *ChainConfig) IsS3(epoch *big.Int) bool {
return isForked(c.S3Epoch, epoch) return isForked(c.S3Epoch, epoch)
} }
// IsIstanbul returns whether epoch is either equal to the Istanbul fork epoch or greater.
func (c *ChainConfig) IsIstanbul(epoch *big.Int) bool {
return isForked(c.IstanbulEpoch, epoch)
}
// IsReceiptLog returns whether epoch is either equal to the ReceiptLog fork epoch or greater. // IsReceiptLog returns whether epoch is either equal to the ReceiptLog fork epoch or greater.
func (c *ChainConfig) IsReceiptLog(epoch *big.Int) bool { func (c *ChainConfig) IsReceiptLog(epoch *big.Int) bool {
return isForked(c.ReceiptLogEpoch, epoch) return isForked(c.ReceiptLogEpoch, epoch)
@ -316,8 +332,8 @@ func isForked(s, epoch *big.Int) bool {
// Rules is a one time interface meaning that it shouldn't be used in between transition // Rules is a one time interface meaning that it shouldn't be used in between transition
// phases. // phases.
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
IsCrossLink, IsEIP155, IsS3, IsReceiptLog bool IsCrossLink, IsEIP155, IsS3, IsReceiptLog, IsIstanbul bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -332,5 +348,6 @@ func (c *ChainConfig) Rules(epoch *big.Int) Rules {
IsEIP155: c.IsEIP155(epoch), IsEIP155: c.IsEIP155(epoch),
IsS3: c.IsS3(epoch), IsS3: c.IsS3(epoch),
IsReceiptLog: c.IsReceiptLog(epoch), IsReceiptLog: c.IsReceiptLog(epoch),
IsIstanbul: c.IsIstanbul(epoch),
} }
} }

@ -1,5 +1,8 @@
package params package params
import "math/big"
// nolint
const ( const (
// GasLimitBoundDivisor ... // GasLimitBoundDivisor ...
GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations. GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
@ -64,6 +67,23 @@ const (
// NetSstoreResetClearRefund ... // NetSstoreResetClearRefund ...
NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
// SstoreSentryGasEIP2200 ...
SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed
// SstoreNoopGasEIP2200 ...
SstoreNoopGasEIP2200 uint64 = 800 // Once per SSTORE operation if the value doesn't change.
// SstoreDirtyGasEIP2200 ...
SstoreDirtyGasEIP2200 uint64 = 800 // Once per SSTORE operation if a dirty value is changed.
// SstoreInitGasEIP2200 ...
SstoreInitGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero
// SstoreInitRefundEIP2200 ...
SstoreInitRefundEIP2200 uint64 = 19200 // Once per SSTORE operation for resetting to the original zero value
// SstoreCleanGasEIP2200 ...
SstoreCleanGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else
// SstoreCleanRefundEIP2200 ...
SstoreCleanRefundEIP2200 uint64 = 4200 // Once per SSTORE operation for resetting to the original non-zero value
// SstoreClearRefundEIP2200 ...
SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
// JumpdestGas ... // JumpdestGas ...
JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
// EpochDuration ... // EpochDuration ...
@ -90,40 +110,72 @@ const (
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
// Create2Gas ... // Create2Gas ...
Create2Gas uint64 = 32000 // Once per CREATE2 operation Create2Gas uint64 = 32000 // Once per CREATE2 operation
// SuicideRefundGas ... // SelfdestructRefundGas ...
SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation. SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation.
// MemoryGas ... // MemoryGas ...
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
// TxDataNonZeroGas ... // TxDataNonZeroGas ...
TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
// TxDataNonZeroGasEIP2028 ...
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
// These have been changed during the course of the chain
CallGasFrontier uint64 = 40 // Once per CALL operation & message call transaction.
CallGasEIP150 uint64 = 700 // Static portion of gas for CALL-derivates after EIP 150 (Tangerine)
BalanceGasFrontier uint64 = 20 // The cost of a BALANCE operation
BalanceGasEIP150 uint64 = 400 // The cost of a BALANCE operation after Tangerine
BalanceGasEIP1884 uint64 = 700 // The cost of a BALANCE operation after EIP 1884 (part of Istanbul)
ExtcodeSizeGasFrontier uint64 = 20 // Cost of EXTCODESIZE before EIP 150 (Tangerine)
ExtcodeSizeGasEIP150 uint64 = 700 // Cost of EXTCODESIZE after EIP 150 (Tangerine)
SloadGasFrontier uint64 = 50
SloadGasEIP150 uint64 = 200
SloadGasEIP1884 uint64 = 800 // Cost of SLOAD after EIP 1884 (part of Istanbul)
ExtcodeHashGasConstantinople uint64 = 400 // Cost of EXTCODEHASH (introduced in Constantinople)
ExtcodeHashGasEIP1884 uint64 = 700 // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul)
SelfdestructGasEIP150 uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine)
// EXP has a dynamic portion depending on the size of the exponent
ExpByteFrontier uint64 = 10 // was set to 10 in Frontier
ExpByteEIP158 uint64 = 50 // was raised to 50 during Eip158 (Spurious Dragon)
// Extcodecopy has a dynamic AND a static cost. This represents only the
// static portion of the gas. It was changed during EIP 150 (Tangerine)
ExtcodeCopyBaseFrontier uint64 = 20
ExtcodeCopyBaseEIP150 uint64 = 700
// CreateBySelfdestructGas is used when the refunded account is one that does
// not exist. This logic is similar to call.
// Introduced in Tangerine Whistle (Eip 150)
CreateBySelfdestructGas uint64 = 25000
// MaxCodeSize ... // MaxCodeSize ...
MaxCodeSize = 24576 // Maximum bytecode to permit for a contract MaxCodeSize = 24576 // Maximum bytecode to permit for a contract
// Precompiled contract gas prices // Precompiled contract gas prices
// EcrecoverGas ... EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
// Sha256BaseGas ... Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
// Sha256PerWordGas ... Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation IdentityBaseGas uint64 = 15 // Base price for a data copy operation
// Ripemd160BaseGas ... IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation
// Ripemd160PerWordGas ...
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation Bn256AddGasByzantium uint64 = 500 // Byzantium gas needed for an elliptic curve addition
// IdentityBaseGas ... Bn256AddGasIstanbul uint64 = 150 // Gas needed for an elliptic curve addition
IdentityBaseGas uint64 = 15 // Base price for a data copy operation Bn256ScalarMulGasByzantium uint64 = 40000 // Byzantium gas needed for an elliptic curve scalar multiplication
// IdentityPerWordGas ... Bn256ScalarMulGasIstanbul uint64 = 6000 // Gas needed for an elliptic curve scalar multiplication
IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation Bn256PairingBaseGasByzantium uint64 = 100000 // Byzantium base price for an elliptic curve pairing check
// ModExpQuadCoeffDiv ... Bn256PairingBaseGasIstanbul uint64 = 45000 // Base price for an elliptic curve pairing check
ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation Bn256PairingPerPointGasByzantium uint64 = 80000 // Byzantium per-point price for an elliptic curve pairing check
// Bn256AddGas ... Bn256PairingPerPointGasIstanbul uint64 = 34000 // Per-point price for an elliptic curve pairing check
Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition )
// Bn256ScalarMulGas ...
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication // nolint
// Bn256PairingBaseGas ... var (
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
// Bn256PairingPerPointGas ... GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
) )

@ -4,6 +4,8 @@ import (
"fmt" "fmt"
"path" "path"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
) )
@ -22,7 +24,7 @@ type LDBFactory struct {
// NewChainDB returns a new LDB for the blockchain for given shard. // NewChainDB returns a new LDB for the blockchain for given shard.
func (f *LDBFactory) NewChainDB(shardID uint32) (ethdb.Database, error) { func (f *LDBFactory) NewChainDB(shardID uint32) (ethdb.Database, error) {
dir := path.Join(f.RootDir, fmt.Sprintf("harmony_db_%d", shardID)) dir := path.Join(f.RootDir, fmt.Sprintf("harmony_db_%d", shardID))
return ethdb.NewLDBDatabase(dir, 128, 64) return rawdb.NewLevelDBDatabase(dir, 128, 64, "")
} }
// MemDBFactory is a memory-backed blockchain database factory. // MemDBFactory is a memory-backed blockchain database factory.
@ -30,5 +32,5 @@ type MemDBFactory struct{}
// NewChainDB returns a new memDB for the blockchain for given shard. // NewChainDB returns a new memDB for the blockchain for given shard.
func (f *MemDBFactory) NewChainDB(shardID uint32) (ethdb.Database, error) { func (f *MemDBFactory) NewChainDB(shardID uint32) (ethdb.Database, error) {
return ethdb.NewMemDatabase(), nil return rawdb.NewMemoryDatabase(), nil
} }

@ -228,6 +228,7 @@ func (w *Worker) commitTransaction(
} }
w.current.txs = append(w.current.txs, tx) w.current.txs = append(w.current.txs, tx)
w.current.receipts = append(w.current.receipts, receipt) w.current.receipts = append(w.current.receipts, receipt)
if cx != nil { if cx != nil {
w.current.outcxs = append(w.current.outcxs, cx) w.current.outcxs = append(w.current.outcxs, cx)
} }

@ -5,9 +5,10 @@ import (
"math/rand" "math/rand"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
"github.com/harmony-one/harmony/common/denominations" "github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core"
@ -30,7 +31,7 @@ var (
func TestNewWorker(t *testing.T) { func TestNewWorker(t *testing.T) {
// Setup a new blockchain with genesis block containing test token on test address // Setup a new blockchain with genesis block containing test token on test address
var ( var (
database = ethdb.NewMemDatabase() database = rawdb.NewMemoryDatabase()
gspec = core.Genesis{ gspec = core.Genesis{
Config: chainConfig, Config: chainConfig,
Factory: blockFactory, Factory: blockFactory,
@ -41,8 +42,11 @@ func TestNewWorker(t *testing.T) {
genesis := gspec.MustCommit(database) genesis := gspec.MustCommit(database)
_ = genesis _ = genesis
chain, _ := core.NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil) chain, err := core.NewBlockChain(database, nil, gspec.Config, chain2.Engine, vm.Config{}, nil)
if err != nil {
t.Error(err)
}
// Create a new worker // Create a new worker
worker := New(params.TestChainConfig, chain, chain2.Engine) worker := New(params.TestChainConfig, chain, chain2.Engine)
@ -54,7 +58,7 @@ func TestNewWorker(t *testing.T) {
func TestCommitTransactions(t *testing.T) { func TestCommitTransactions(t *testing.T) {
// Setup a new blockchain with genesis block containing test token on test address // Setup a new blockchain with genesis block containing test token on test address
var ( var (
database = ethdb.NewMemDatabase() database = rawdb.NewMemoryDatabase()
gspec = core.Genesis{ gspec = core.Genesis{
Config: chainConfig, Config: chainConfig,
Factory: blockFactory, Factory: blockFactory,

@ -8,10 +8,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
blockfactory "github.com/harmony-one/harmony/block/factory" blockfactory "github.com/harmony-one/harmony/block/factory"
consensus_sig "github.com/harmony-one/harmony/consensus/signature" consensus_sig "github.com/harmony-one/harmony/consensus/signature"
@ -990,7 +991,7 @@ func defaultTestStateDB() *state.DB {
} }
func makeTestStateDB() *state.DB { func makeTestStateDB() *state.DB {
db := state.NewDatabase(ethdb.NewMemDatabase()) db := state.NewDatabase(rawdb.NewMemoryDatabase())
sdb, err := state.New(common.Hash{}, db) sdb, err := state.New(common.Hash{}, db)
if err != nil { if err != nil {
panic(err) panic(err)

@ -108,7 +108,7 @@ type EditValidator struct {
SlotKeyToRemove *bls.SerializedPublicKey `json:"slot-key-to_remove" rlp:"nil"` SlotKeyToRemove *bls.SerializedPublicKey `json:"slot-key-to_remove" rlp:"nil"`
SlotKeyToAdd *bls.SerializedPublicKey `json:"slot-key-to_add" rlp:"nil"` SlotKeyToAdd *bls.SerializedPublicKey `json:"slot-key-to_add" rlp:"nil"`
SlotKeyToAddSig *bls.SerializedSignature `json:"slot-key-to-add-sig" rlp:"nil"` SlotKeyToAddSig *bls.SerializedSignature `json:"slot-key-to-add-sig" rlp:"nil"`
EPOSStatus effective.Eligibility `json:"epos-eligibility-status" rlp:"nil"` EPOSStatus effective.Eligibility `json:"epos-eligibility-status"`
} }
// Type of EditValidator // Type of EditValidator

@ -6,6 +6,8 @@ import (
"log" "log"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
@ -37,7 +39,7 @@ var (
blockFactory = blockfactory.ForTest blockFactory = blockfactory.ForTest
// Test transactions // Test transactions
pendingTxs []*types.Transaction pendingTxs []*types.Transaction
database = ethdb.NewMemDatabase() database = rawdb.NewMemoryDatabase()
gspec = core.Genesis{ gspec = core.Genesis{
Config: chainConfig, Config: chainConfig,
Factory: blockFactory, Factory: blockFactory,

@ -6,6 +6,8 @@ import (
"math/rand" "math/rand"
"time" "time"
"github.com/ethereum/go-ethereum/core/rawdb"
msg_pb "github.com/harmony-one/harmony/api/proto/message" msg_pb "github.com/harmony-one/harmony/api/proto/message"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
@ -15,7 +17,6 @@ import (
common2 "github.com/ethereum/go-ethereum/common" common2 "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/core/state" "github.com/harmony-one/harmony/core/state"
@ -104,11 +105,11 @@ func main() {
GasLimit: 1e18, GasLimit: 1e18,
ShardID: 0, ShardID: 0,
} }
database := ethdb.NewMemDatabase() database := rawdb.NewMemoryDatabase()
genesis := gspec.MustCommit(database) genesis := gspec.MustCommit(database)
_ = genesis _ = genesis
bc, _ := core.NewBlockChain(database, nil, gspec.Config, chain.Engine, vm.Config{}, nil) bc, _ := core.NewBlockChain(database, nil, gspec.Config, chain.Engine, vm.Config{}, nil)
statedb, _ := state.New(common2.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common2.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
msg := createValidator() msg := createValidator()
statedb.AddBalance(msg.ValidatorAddress, new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000))) statedb.AddBalance(msg.ValidatorAddress, new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000)))
validator, err := core.VerifyAndCreateValidatorFromMsg( validator, err := core.VerifyAndCreateValidatorFromMsg(

Loading…
Cancel
Save