diff --git a/block/factory/factory.go b/block/factory/factory.go
index 1601c291a6..072b580fdb 100644
--- a/block/factory/factory.go
+++ b/block/factory/factory.go
@@ -9,6 +9,7 @@ import (
v1 "github.com/harmony-one/harmony/block/v1"
v2 "github.com/harmony-one/harmony/block/v2"
v3 "github.com/harmony-one/harmony/block/v3"
+ v4 "github.com/harmony-one/harmony/block/v4"
"github.com/harmony-one/harmony/internal/params"
)
@@ -30,6 +31,8 @@ func NewFactory(chainConfig *params.ChainConfig) Factory {
func (f *factory) NewHeader(epoch *big.Int) *block.Header {
var impl blockif.Header
switch {
+ case f.chainConfig.IsLondon(epoch):
+ impl = v4.NewHeader()
case f.chainConfig.IsPreStaking(epoch) || f.chainConfig.IsStaking(epoch):
impl = v3.NewHeader()
case f.chainConfig.IsCrossLink(epoch):
diff --git a/block/interface/header.go b/block/interface/header.go
index f75810c68a..4656b30c38 100644
--- a/block/interface/header.go
+++ b/block/interface/header.go
@@ -236,4 +236,8 @@ type Header interface {
// SetSlashes sets the RLP-encoded form of slashes
// It stores a copy; the caller may freely modify the original.
SetSlashes(newSlashes []byte)
+
+ BaseFee() *big.Int
+
+ SetBaseFee(newBaseFee *big.Int)
}
diff --git a/block/v0/header.go b/block/v0/header.go
index 194132863e..7975080599 100644
--- a/block/v0/header.go
+++ b/block/v0/header.go
@@ -445,3 +445,15 @@ func (h *Header) Copy() blockif.Header {
cpy := *h
return &cpy
}
+
+// BaseFee returns the base fee of the block.
+func (h *Header) BaseFee() *big.Int {
+ return nil
+}
+
+// SetBaseFee sets the base fee of the block.
+func (h *Header) SetBaseFee(newBaseFee *big.Int) {
+ h.Logger(utils.Logger()).Warn().
+ Str("baseFee", newBaseFee.String()).
+ Msg("cannot store base fee in V0 header")
+}
diff --git a/block/v1/header.go b/block/v1/header.go
index ab153d4e17..ae7e92092f 100644
--- a/block/v1/header.go
+++ b/block/v1/header.go
@@ -429,3 +429,14 @@ func (h *Header) Copy() blockif.Header {
cpy := *h
return &cpy
}
+
+// BaseFee returns the base fee of the header.
+func (h *Header) BaseFee() *big.Int {
+ return nil
+}
+
+// SetBaseFee sets the base fee of the header.
+func (h *Header) SetBaseFee(newBaseFee *big.Int) {
+ h.Logger(utils.Logger()).Error().
+ Msg("cannot store base fee in V1 header")
+}
diff --git a/block/v2/header.go b/block/v2/header.go
index 7e2736ab50..80b2e4cfd9 100644
--- a/block/v2/header.go
+++ b/block/v2/header.go
@@ -428,3 +428,14 @@ func (h *Header) Copy() blockif.Header {
cpy := *h
return &cpy
}
+
+// BaseFee returns the base fee of the block.
+func (h *Header) BaseFee() *big.Int {
+ return nil
+}
+
+// SetBaseFee sets the base fee of the block.
+func (h *Header) SetBaseFee(newBaseFee *big.Int) {
+ h.Logger(utils.Logger()).Error().
+ Msg("cannot store base fee in V2 header")
+}
diff --git a/block/v3/header.go b/block/v3/header.go
index ea62204a9f..b207a5be02 100644
--- a/block/v3/header.go
+++ b/block/v3/header.go
@@ -422,3 +422,15 @@ func (h *Header) Copy() blockif.Header {
cpy := *h
return &cpy
}
+
+// BaseFee returns the base fee of the header.
+func (h *Header) BaseFee() *big.Int {
+ return nil
+}
+
+// SetBaseFee sets the base fee of the header.
+func (h *Header) SetBaseFee(newBaseFee *big.Int) {
+ h.Logger(utils.Logger()).Warn().
+ Str("baseFee", newBaseFee.String()).
+ Msg("cannot store BaseFee in V3 header")
+}
diff --git a/block/v4/header.go b/block/v4/header.go
new file mode 100644
index 0000000000..a9df258056
--- /dev/null
+++ b/block/v4/header.go
@@ -0,0 +1,442 @@
+package v4
+
+import (
+ "io"
+ "math/big"
+ "unsafe"
+
+ "github.com/ethereum/go-ethereum/common"
+ ethtypes "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/rs/zerolog"
+
+ blockif "github.com/harmony-one/harmony/block/interface"
+ "github.com/harmony-one/harmony/crypto/hash"
+ "github.com/harmony-one/harmony/internal/utils"
+ "github.com/harmony-one/harmony/shard"
+)
+
+// Header is the V3 block header.
+// V3 block header is exactly the same
+// we copy the code instead of embedded v2 header into v3
+// when we do type checking in NewBodyForMatchingHeader
+// the embedded structure will return v2 header type instead of v3 type
+type Header struct {
+ fields headerFields
+}
+
+// EncodeRLP encodes the header fields into RLP format.
+func (h *Header) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, &h.fields)
+}
+
+// DecodeRLP decodes the given RLP decode stream into the header fields.
+func (h *Header) DecodeRLP(s *rlp.Stream) error {
+ return s.Decode(&h.fields)
+}
+
+// NewHeader creates a new header object.
+func NewHeader() *Header {
+ return &Header{headerFields{
+ Number: new(big.Int),
+ Time: new(big.Int),
+ ViewID: new(big.Int),
+ Epoch: new(big.Int),
+ }}
+}
+
+type headerFields struct {
+ ParentHash common.Hash `json:"parentHash" gencodec:"required"`
+ Coinbase common.Address `json:"miner" gencodec:"required"`
+ Root common.Hash `json:"stateRoot" gencodec:"required"`
+ TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
+ ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
+ OutgoingReceiptHash common.Hash `json:"outgoingReceiptsRoot" gencodec:"required"`
+ IncomingReceiptHash common.Hash `json:"incomingReceiptsRoot" gencodec:"required"`
+ Bloom ethtypes.Bloom `json:"logsBloom" gencodec:"required"`
+ Number *big.Int `json:"number" gencodec:"required"`
+ GasLimit uint64 `json:"gasLimit" gencodec:"required"`
+ GasUsed uint64 `json:"gasUsed" gencodec:"required"`
+ Time *big.Int `json:"timestamp" gencodec:"required"`
+ Extra []byte `json:"extraData" gencodec:"required"`
+ MixDigest common.Hash `json:"mixHash" gencodec:"required"`
+ // Additional Fields
+ ViewID *big.Int `json:"viewID" gencodec:"required"`
+ Epoch *big.Int `json:"epoch" gencodec:"required"`
+ ShardID uint32 `json:"shardID" gencodec:"required"`
+ LastCommitSignature [96]byte `json:"lastCommitSignature" gencodec:"required"`
+ LastCommitBitmap []byte `json:"lastCommitBitmap" gencodec:"required"` // Contains which validator signed
+ Vrf []byte `json:"vrf"`
+ Vdf []byte `json:"vdf"`
+ ShardState []byte `json:"shardState"`
+ CrossLinks []byte `json:"crossLink"`
+ Slashes []byte `json:"slashes"`
+ // BaseFee was added by EIP-1559 and is ignored in legacy headers.
+ BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
+}
+
+// ParentHash is the header hash of the parent block. For the genesis block
+// which has no parent by definition, this field is zeroed out.
+func (h *Header) ParentHash() common.Hash {
+ return h.fields.ParentHash
+}
+
+// SetParentHash sets the parent hash field.
+func (h *Header) SetParentHash(newParentHash common.Hash) {
+ h.fields.ParentHash = newParentHash
+}
+
+// Coinbase is now the first 20 bytes of the SHA256 hash of the leader's
+// public BLS key. This is required for EVM compatibility.
+func (h *Header) Coinbase() common.Address {
+ return h.fields.Coinbase
+}
+
+// SetCoinbase sets the coinbase address field.
+func (h *Header) SetCoinbase(newCoinbase common.Address) {
+ h.fields.Coinbase = newCoinbase
+}
+
+// Root is the state (account) trie root hash.
+func (h *Header) Root() common.Hash {
+ return h.fields.Root
+}
+
+// SetRoot sets the state trie root hash field.
+func (h *Header) SetRoot(newRoot common.Hash) {
+ h.fields.Root = newRoot
+}
+
+// TxHash is the transaction trie root hash.
+func (h *Header) TxHash() common.Hash {
+ return h.fields.TxHash
+}
+
+// SetTxHash sets the transaction trie root hash field.
+func (h *Header) SetTxHash(newTxHash common.Hash) {
+ h.fields.TxHash = newTxHash
+}
+
+// ReceiptHash is the same-shard transaction receipt trie hash.
+func (h *Header) ReceiptHash() common.Hash {
+ return h.fields.ReceiptHash
+}
+
+// SetReceiptHash sets the same-shard transaction receipt trie hash.
+func (h *Header) SetReceiptHash(newReceiptHash common.Hash) {
+ h.fields.ReceiptHash = newReceiptHash
+}
+
+// OutgoingReceiptHash is the egress transaction receipt trie hash.
+func (h *Header) OutgoingReceiptHash() common.Hash {
+ return h.fields.OutgoingReceiptHash
+}
+
+// SetOutgoingReceiptHash sets the egress transaction receipt trie hash.
+func (h *Header) SetOutgoingReceiptHash(newOutgoingReceiptHash common.Hash) {
+ h.fields.OutgoingReceiptHash = newOutgoingReceiptHash
+}
+
+// IncomingReceiptHash is the ingress transaction receipt trie hash.
+func (h *Header) IncomingReceiptHash() common.Hash {
+ return h.fields.IncomingReceiptHash
+}
+
+// SetIncomingReceiptHash sets the ingress transaction receipt trie hash.
+func (h *Header) SetIncomingReceiptHash(newIncomingReceiptHash common.Hash) {
+ h.fields.IncomingReceiptHash = newIncomingReceiptHash
+}
+
+// Bloom is the Bloom filter that indexes accounts and topics logged by smart
+// contract transactions (executions) in this block.
+func (h *Header) Bloom() ethtypes.Bloom {
+ return h.fields.Bloom
+}
+
+// SetBloom sets the smart contract log Bloom filter for this block.
+func (h *Header) SetBloom(newBloom ethtypes.Bloom) {
+ h.fields.Bloom = newBloom
+}
+
+// Number is the block number.
+//
+// The returned instance is a copy; the caller may do anything with it.
+func (h *Header) Number() *big.Int {
+ return new(big.Int).Set(h.fields.Number)
+}
+
+// SetNumber sets the block number.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetNumber(newNumber *big.Int) {
+ h.fields.Number = new(big.Int).Set(newNumber)
+}
+
+// GasLimit is the gas limit for transactions in this block.
+func (h *Header) GasLimit() uint64 {
+ return h.fields.GasLimit
+}
+
+// SetGasLimit sets the gas limit for transactions in this block.
+func (h *Header) SetGasLimit(newGasLimit uint64) {
+ h.fields.GasLimit = newGasLimit
+}
+
+// GasUsed is the amount of gas used by transactions in this block.
+func (h *Header) GasUsed() uint64 {
+ return h.fields.GasUsed
+}
+
+// SetGasUsed sets the amount of gas used by transactions in this block.
+func (h *Header) SetGasUsed(newGasUsed uint64) {
+ h.fields.GasUsed = newGasUsed
+}
+
+// Time is the UNIX timestamp of this block.
+//
+// The returned instance is a copy; the caller may do anything with it.
+func (h *Header) Time() *big.Int {
+ return new(big.Int).Set(h.fields.Time)
+}
+
+// SetTime sets the UNIX timestamp of this block.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetTime(newTime *big.Int) {
+ h.fields.Time = new(big.Int).Set(newTime)
+}
+
+// Extra is the extra data field of this block.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) Extra() []byte {
+ return append(h.fields.Extra[:0:0], h.fields.Extra...)
+}
+
+// SetExtra sets the extra data field of this block.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetExtra(newExtra []byte) {
+ h.fields.Extra = append(newExtra[:0:0], newExtra...)
+}
+
+// MixDigest is the mixhash.
+//
+// This field is a remnant from Ethereum, and Harmony does not use it and always
+// zeroes it out.
+func (h *Header) MixDigest() common.Hash {
+ return h.fields.MixDigest
+}
+
+// SetMixDigest sets the mixhash of this block.
+func (h *Header) SetMixDigest(newMixDigest common.Hash) {
+ h.fields.MixDigest = newMixDigest
+}
+
+// ViewID is the ID of the view in which this block was originally proposed.
+//
+// It normally increases by one for each subsequent block, or by more than one
+// if one or more PBFT/FBFT view changes have occurred.
+//
+// The returned instance is a copy; the caller may do anything with it.
+func (h *Header) ViewID() *big.Int {
+ return new(big.Int).Set(h.fields.ViewID)
+}
+
+// SetViewID sets the view ID in which the block was originally proposed.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetViewID(newViewID *big.Int) {
+ h.fields.ViewID = new(big.Int).Set(newViewID)
+}
+
+// Epoch is the epoch number of this block.
+//
+// The returned instance is a copy; the caller may do anything with it.
+func (h *Header) Epoch() *big.Int {
+ return new(big.Int).Set(h.fields.Epoch)
+}
+
+// SetEpoch sets the epoch number of this block.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetEpoch(newEpoch *big.Int) {
+ h.fields.Epoch = new(big.Int).Set(newEpoch)
+}
+
+// ShardID is the shard ID to which this block belongs.
+func (h *Header) ShardID() uint32 {
+ return h.fields.ShardID
+}
+
+// SetShardID sets the shard ID to which this block belongs.
+func (h *Header) SetShardID(newShardID uint32) {
+ h.fields.ShardID = newShardID
+}
+
+// LastCommitSignature is the FBFT commit group signature for the last block.
+func (h *Header) LastCommitSignature() [96]byte {
+ return h.fields.LastCommitSignature
+}
+
+// SetLastCommitSignature sets the FBFT commit group signature for the last
+// block.
+func (h *Header) SetLastCommitSignature(newLastCommitSignature [96]byte) {
+ h.fields.LastCommitSignature = newLastCommitSignature
+}
+
+// LastCommitBitmap is the signatory bitmap of the previous block. Bit
+// positions index into committee member array.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) LastCommitBitmap() []byte {
+ return append(h.fields.LastCommitBitmap[:0:0], h.fields.LastCommitBitmap...)
+}
+
+// SetLastCommitBitmap sets the signatory bitmap of the previous block.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetLastCommitBitmap(newLastCommitBitmap []byte) {
+ h.fields.LastCommitBitmap = append(newLastCommitBitmap[:0:0], newLastCommitBitmap...)
+}
+
+// ShardStateHash is the shard state hash.
+func (h *Header) ShardStateHash() common.Hash {
+ return common.Hash{}
+}
+
+// SetShardStateHash sets the shard state hash.
+func (h *Header) SetShardStateHash(newShardStateHash common.Hash) {
+ h.Logger(utils.Logger()).Warn().
+ Str("shardStateHash", newShardStateHash.Hex()).
+ Msg("cannot store ShardStateHash in V3 header")
+}
+
+// Vrf is the output of the VRF for the epoch.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) Vrf() []byte {
+ return append(h.fields.Vrf[:0:0], h.fields.Vrf...)
+}
+
+// SetVrf sets the output of the VRF for the epoch.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetVrf(newVrf []byte) {
+ h.fields.Vrf = append(newVrf[:0:0], newVrf...)
+}
+
+// Vdf is the output of the VDF for the epoch.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) Vdf() []byte {
+ return append(h.fields.Vdf[:0:0], h.fields.Vdf...)
+}
+
+// SetVdf sets the output of the VDF for the epoch.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetVdf(newVdf []byte) {
+ h.fields.Vdf = append(newVdf[:0:0], newVdf...)
+}
+
+// ShardState is the RLP-encoded form of shard state (list of committees) for
+// the next epoch.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) ShardState() []byte {
+ return append(h.fields.ShardState[:0:0], h.fields.ShardState...)
+}
+
+// SetShardState sets the RLP-encoded form of shard state
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetShardState(newShardState []byte) {
+ h.fields.ShardState = append(newShardState[:0:0], newShardState...)
+}
+
+// CrossLinks is the RLP-encoded form of non-beacon block headers chosen to be
+// canonical by the beacon committee. This field is present only on beacon
+// chain block headers.
+//
+// The returned slice is a copy; the caller may do anything with it.
+func (h *Header) CrossLinks() []byte {
+ return append(h.fields.CrossLinks[:0:0], h.fields.CrossLinks...)
+}
+
+// SetCrossLinks sets the RLP-encoded form of non-beacon block headers chosen to
+// be canonical by the beacon committee.
+//
+// It stores a copy; the caller may freely modify the original.
+func (h *Header) SetCrossLinks(newCrossLinks []byte) {
+ h.fields.CrossLinks = append(newCrossLinks[:0:0], newCrossLinks...)
+}
+
+// Slashes ..
+func (h *Header) Slashes() []byte {
+ return append(h.fields.Slashes[:0:0], h.fields.Slashes...)
+}
+
+// SetSlashes ..
+func (h *Header) SetSlashes(newSlashes []byte) {
+ h.fields.Slashes = append(newSlashes[:0:0], newSlashes...)
+}
+
+// Hash returns the block hash of the header, which is simply the keccak256 hash of its
+// RLP encoding.
+func (h *Header) Hash() common.Hash {
+ return hash.FromRLP(h)
+}
+
+// Size returns the approximate memory used by all internal contents. It is used
+// to approximate and limit the memory consumption of various caches.
+func (h *Header) Size() common.StorageSize {
+ // TODO: update with new fields
+ var baseFeeBits int
+ if h.BaseFee() != nil {
+ baseFeeBits = h.BaseFee().BitLen()
+ }
+ return common.StorageSize(unsafe.Sizeof(*h)) +
+ common.StorageSize(len(h.Extra())+(h.Number().BitLen()+
+ h.Time().BitLen()+baseFeeBits)/8,
+ )
+}
+
+// Logger returns a sub-logger with block contexts added.
+func (h *Header) Logger(logger *zerolog.Logger) *zerolog.Logger {
+ nlogger := logger.
+ With().
+ Str("blockHash", h.Hash().Hex()).
+ Uint32("blockShard", h.ShardID()).
+ Uint64("blockEpoch", h.Epoch().Uint64()).
+ Uint64("blockNumber", h.Number().Uint64()).
+ Logger()
+ return &nlogger
+}
+
+// GetShardState returns the deserialized shard state object.
+func (h *Header) GetShardState() (shard.State, error) {
+ state, err := shard.DecodeWrapper(h.ShardState())
+ if err != nil {
+ return shard.State{}, err
+ }
+ return *state, nil
+}
+
+// Copy returns a copy of the given header.
+func (h *Header) Copy() blockif.Header {
+ cpy := *h
+ return &cpy
+}
+
+func (h *Header) BaseFee() *big.Int {
+ if h.fields.BaseFee == nil {
+ return nil
+ }
+ out := new(big.Int).Set(h.fields.BaseFee)
+ return out
+}
+
+func (h *Header) SetBaseFee(newBaseFee *big.Int) {
+ h.fields.BaseFee = new(big.Int).Set(newBaseFee)
+}
diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go
new file mode 100644
index 0000000000..6f92fa6472
--- /dev/null
+++ b/consensus/misc/eip1559/eip1559.go
@@ -0,0 +1,98 @@
+// Copyright 2021 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 .
+
+package eip1559
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/misc"
+ "github.com/harmony-one/harmony/block"
+ "github.com/harmony-one/harmony/internal/params"
+ "github.com/pkg/errors"
+)
+
+// VerifyEIP1559Header verifies some header attributes which were changed in EIP-1559,
+// - gas limit check
+// - basefee check
+func VerifyEIP1559Header(config *params.ChainConfig, parent, header *block.Header) error {
+ // Verify that the gas limit remains within allowed bounds
+ parentGasLimit := parent.GasLimit()
+ if !config.IsLondon(parent.Number()) {
+ parentGasLimit = parent.GasLimit() * config.ElasticityMultiplier()
+ }
+ if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit()); err != nil {
+ return err
+ }
+ // Verify the header is not malformed
+ if header.BaseFee == nil {
+ return errors.New("header is missing baseFee")
+ }
+ // Verify the baseFee is correct based on the parent header.
+ expectedBaseFee := CalcBaseFee(config, parent)
+ if header.BaseFee().Cmp(expectedBaseFee) != 0 {
+ return fmt.Errorf("invalid baseFee: have %s, want %s, parentBaseFee %s, parentGasUsed %d",
+ header.BaseFee(), expectedBaseFee, parent.BaseFee(), parent.GasUsed())
+ }
+ return nil
+}
+
+// CalcBaseFee calculates the basefee of the header.
+func CalcBaseFee(config *params.ChainConfig, parent *block.Header) *big.Int {
+ // If the current block is the first EIP-1559 block, return the InitialBaseFee.
+ if !config.IsLondon(parent.Epoch()) {
+ return new(big.Int).SetUint64(params.InitialBaseFee)
+ }
+
+ parentGasTarget := parent.GasLimit() / config.ElasticityMultiplier()
+ // If the parent gasUsed is the same as the target, the baseFee remains unchanged.
+ if parent.GasUsed() == parentGasTarget {
+ return new(big.Int).Set(parent.BaseFee())
+ }
+
+ var (
+ num = new(big.Int)
+ denom = new(big.Int)
+ )
+
+ if parent.GasUsed() > parentGasTarget {
+ // If the parent block used more gas than its target, the baseFee should increase.
+ // max(1, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
+ num.SetUint64(parent.GasUsed() - parentGasTarget)
+ num.Mul(num, parent.BaseFee())
+ num.Div(num, denom.SetUint64(parentGasTarget))
+ num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
+ if num.Cmp(common.Big1) < 0 {
+ return num.Add(parent.BaseFee(), common.Big1)
+ }
+ return num.Add(parent.BaseFee(), num)
+ } else {
+ // Otherwise if the parent block used less gas than its target, the baseFee should decrease.
+ // max(0, parentBaseFee * gasUsedDelta / parentGasTarget / baseFeeChangeDenominator)
+ num.SetUint64(parentGasTarget - parent.GasUsed())
+ num.Mul(num, parent.BaseFee())
+ num.Div(num, denom.SetUint64(parentGasTarget))
+ num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator()))
+
+ baseFee := num.Sub(parent.BaseFee(), num)
+ if baseFee.Cmp(common.Big0) < 0 {
+ baseFee = common.Big0
+ }
+ return baseFee
+ }
+}
diff --git a/consensus/misc/eip1559/eip1559_test.go b/consensus/misc/eip1559/eip1559_test.go
new file mode 100644
index 0000000000..54f0e7df4d
--- /dev/null
+++ b/consensus/misc/eip1559/eip1559_test.go
@@ -0,0 +1,134 @@
+// Copyright 2021 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 .
+
+package eip1559
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/harmony-one/harmony/internal/params"
+
+ "github.com/harmony-one/harmony/block"
+ v4 "github.com/harmony-one/harmony/block/v4"
+)
+
+// copyConfig does a _shallow_ copy of a given config. Safe to set new values, but
+// do not use e.g. SetInt() on the numbers. For testing only
+func copyConfig(original *params.ChainConfig) *params.ChainConfig {
+ return ¶ms.ChainConfig{
+ ChainID: original.ChainID,
+ HomesteadBlock: original.HomesteadBlock,
+ DAOForkBlock: original.DAOForkBlock,
+ DAOForkSupport: original.DAOForkSupport,
+ EIP150Block: original.EIP150Block,
+ EIP155Block: original.EIP155Block,
+ EIP158Block: original.EIP158Block,
+ ByzantiumBlock: original.ByzantiumBlock,
+ ConstantinopleBlock: original.ConstantinopleBlock,
+ PetersburgBlock: original.PetersburgBlock,
+ IstanbulBlock: original.IstanbulBlock,
+ MuirGlacierBlock: original.MuirGlacierBlock,
+ BerlinBlock: original.BerlinBlock,
+ LondonBlock: original.LondonBlock,
+ TerminalTotalDifficulty: original.TerminalTotalDifficulty,
+ Ethash: original.Ethash,
+ Clique: original.Clique,
+ }
+}
+
+func config() *params.ChainConfig {
+ config := copyConfig(params.TestChainConfig)
+ config.EIP1559Epoch = big.NewInt(1)
+ return config
+}
+
+// TestBlockGasLimits tests the gasLimit checks for blocks both across
+// the EIP-1559 boundary and post-1559 blocks
+func TestBlockGasLimits(t *testing.T) {
+ initial := new(big.Int).SetUint64(params.InitialBaseFee)
+
+ for i, tc := range []struct {
+ pGasLimit uint64
+ pNum int64
+ gasLimit uint64
+ ok bool
+ }{
+ // Transitions from non-london to london
+ {10000000, 4, 20000000, true}, // No change
+ {10000000, 4, 20019530, true}, // Upper limit
+ {10000000, 4, 20019531, false}, // Upper +1
+ {10000000, 4, 19980470, true}, // Lower limit
+ {10000000, 4, 19980469, false}, // Lower limit -1
+ // London to London
+ {20000000, 5, 20000000, true},
+ {20000000, 5, 20019530, true}, // Upper limit
+ {20000000, 5, 20019531, false}, // Upper limit +1
+ {20000000, 5, 19980470, true}, // Lower limit
+ {20000000, 5, 19980469, false}, // Lower limit -1
+ {40000000, 5, 40039061, true}, // Upper limit
+ {40000000, 5, 40039062, false}, // Upper limit +1
+ {40000000, 5, 39960939, true}, // lower limit
+ {40000000, 5, 39960938, false}, // Lower limit -1
+ } {
+ parent := &block.Header{
+ GasUsed: tc.pGasLimit / 2,
+ GasLimit: tc.pGasLimit,
+ BaseFee: initial,
+ Number: big.NewInt(tc.pNum),
+ }
+ header := &types.Header{
+ GasUsed: tc.gasLimit / 2,
+ GasLimit: tc.gasLimit,
+ BaseFee: initial,
+ Number: big.NewInt(tc.pNum + 1),
+ }
+ err := VerifyEIP1559Header(config(), parent, header)
+ if tc.ok && err != nil {
+ t.Errorf("test %d: Expected valid header: %s", i, err)
+ }
+ if !tc.ok && err == nil {
+ t.Errorf("test %d: Expected invalid header", i)
+ }
+ }
+}
+
+// TestCalcBaseFee assumes all blocks are 1559-blocks
+func TestCalcBaseFee(t *testing.T) {
+ tests := []struct {
+ parentBaseFee int64
+ parentGasLimit uint64
+ parentGasUsed uint64
+ expectedBaseFee int64
+ }{
+ {params.InitialBaseFee, 20000000, 10000000, params.InitialBaseFee}, // usage == target
+ {params.InitialBaseFee, 20000000, 9000000, 987500000}, // usage below target
+ {params.InitialBaseFee, 20000000, 11000000, 1012500000}, // usage above target
+ }
+ for i, test := range tests {
+ parent := &v4.Header{
+ Number: common.Big32,
+ GasLimit: test.parentGasLimit,
+ GasUsed: test.parentGasUsed,
+ BaseFee: big.NewInt(test.parentBaseFee),
+ }
+ if have, want := CalcBaseFee(config(), parent), big.NewInt(test.expectedBaseFee); have.Cmp(want) != 0 {
+ t.Errorf("test %d: have %d want %d, ", i, have, want)
+ }
+ }
+}
diff --git a/core/evm.go b/core/evm.go
index 8fbdeec778..d959bf990f 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -59,8 +59,8 @@ type ChainContext interface {
ShardID() uint32 // this is implemented by blockchain.go already
}
-// NewEVMContext creates a new context for use in the EVM.
-func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author *common.Address) vm.Context {
+// NewEVMBlockContext creates a new context for use in the EVM.
+func NewEVMBlockContext(msg Message, header *block.Header, chain ChainContext, author *common.Address) vm.BlockContext {
// If we don't have an explicit author (i.e. not mining), extract from the header
var beneficiary common.Address
if author == nil {
@@ -73,21 +73,21 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author
vrfAndProof := header.Vrf()
copy(vrf[:], vrfAndProof[:32])
}
- return vm.Context{
- CanTransfer: CanTransfer,
- Transfer: Transfer,
- GetHash: GetHashFn(header, chain),
- GetVRF: GetVRFFn(header, chain),
- IsValidator: IsValidator,
- Origin: msg.From(),
- GasPrice: new(big.Int).Set(msg.GasPrice()),
- Coinbase: beneficiary,
- GasLimit: header.GasLimit(),
- BlockNumber: header.Number(),
- EpochNumber: header.Epoch(),
- Time: header.Time(),
- VRF: vrf,
- TxType: 0,
+ return vm.BlockContext{
+ CanTransfer: CanTransfer,
+ Transfer: Transfer,
+ GetHash: GetHashFn(header, chain),
+ GetVRF: GetVRFFn(header, chain),
+ IsValidator: IsValidator,
+ //Origin: msg.From(),
+ //GasPrice: new(big.Int).Set(msg.GasPrice()),
+ Coinbase: beneficiary,
+ GasLimit: header.GasLimit(),
+ BlockNumber: header.Number(),
+ EpochNumber: header.Epoch(),
+ Time: header.Time(),
+ VRF: vrf,
+ //TxType: 0,
CreateValidator: CreateValidatorFn(header, chain),
EditValidator: EditValidatorFn(header, chain),
Delegate: DelegateFn(header, chain),
@@ -99,6 +99,19 @@ func NewEVMContext(msg Message, header *block.Header, chain ChainContext, author
}
}
+// NewEVMTxContext creates a new transaction context for a single transaction.
+func NewEVMTxContext(msg Message) vm.TxContext {
+ ctx := vm.TxContext{
+ Origin: msg.From(),
+ GasPrice: new(big.Int).Set(msg.GasPrice()),
+ //BlobHashes: msg.BlobHashes,
+ }
+ //if msg.BlobGasFeeCap != nil {
+ // ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
+ //}
+ return ctx
+}
+
// HandleStakeMsgFn returns a function which accepts
// (1) the chain state database
// (2) the processed staking parameters
diff --git a/core/evm_test.go b/core/evm_test.go
index 3778cad260..e4c63d538c 100644
--- a/core/evm_test.go
+++ b/core/evm_test.go
@@ -67,7 +67,7 @@ func TestEVMStaking(t *testing.T) {
// transaction as message (chainId = 2)
msg, _ := tx.AsMessage(types.NewEIP155Signer(common.Big2))
// context
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase */)
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase */)
// createValidator test
createValidator := sampleCreateValidator(*key)
@@ -105,7 +105,7 @@ func TestEVMStaking(t *testing.T) {
},
}
// redelegate using epoch1, so that we can cover the locked tokens use case as well
- ctx2 := NewEVMContext(msg, blockfactory.ForTest.NewHeader(common.Big1), chain, nil)
+ ctx2 := NewEVMBlockContext(msg, blockfactory.ForTest.NewHeader(common.Big1), chain, nil)
err = db.UpdateValidatorWrapper(wrapper.Address, wrapper)
err = ctx2.Delegate(db, nil, &delegate)
if err != nil {
@@ -437,8 +437,8 @@ func TestWriteCapablePrecompilesIntegration(t *testing.T) {
// gp := new(GasPool).AddGas(math.MaxUint64)
tx := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), 0, big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
msg, _ := tx.AsMessage(types.NewEIP155Signer(common.Big2))
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase */)
- evm := vm.NewEVM(ctx, db, params.TestChainConfig, vm.Config{})
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase */)
+ evm := vm.NewEVM(ctx, NewEVMTxContext(msg), db, params.TestChainConfig, vm.Config{})
// interpreter := vm.NewEVMInterpreter(evm, vm.Config{})
address := common.BytesToAddress([]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, 252})
// caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int)
@@ -466,7 +466,7 @@ func TestWriteCapablePrecompilesIntegration(t *testing.T) {
// now without staking precompile
cfg := params.TestChainConfig
cfg.StakingPrecompileEpoch = big.NewInt(10000000)
- evm = vm.NewEVM(ctx, db, cfg, vm.Config{})
+ evm = vm.NewEVM(ctx, NewEVMTxContext(msg), db, cfg, vm.Config{})
_, _, err = evm.Call(vm.AccountRef(common.Address{}),
createValidator.ValidatorAddress,
[]byte{},
diff --git a/core/state_processor.go b/core/state_processor.go
index ce6a249fd5..d576161702 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -301,11 +301,11 @@ func ApplyTransaction(bc ChainContext, author *common.Address, gp *GasPool, stat
}
// Create a new context to be used in the EVM environment
- context := NewEVMContext(msg, header, bc, author)
+ context := NewEVMBlockContext(msg, header, bc, author)
context.TxType = txType
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
- vmenv := vm.NewEVM(context, statedb, config, cfg)
+ vmenv := vm.NewEVM(context, NewEVMTxContext(msg), statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
result, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
@@ -337,7 +337,7 @@ func ApplyTransaction(bc ChainContext, author *common.Address, gp *GasPool, stat
receipt.EffectiveGasPrice = tx.EffectiveGasPrice(big.NewInt(0), nil)
// if the transaction created a contract, store the creation address in the receipt.
if msg.To() == nil {
- receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
+ receipt.ContractAddress = crypto.CreateAddress(vmenv.TxContext.Origin, tx.Nonce())
}
// Set the receipt logs and create a bloom for filtering
@@ -393,11 +393,11 @@ func ApplyStakingTransaction(
}
// Create a new context to be used in the EVM environment
- context := NewEVMContext(msg, header, bc, author)
+ context := NewEVMBlockContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
- vmenv := vm.NewEVM(context, statedb, config, cfg)
+ vmenv := vm.NewEVM(context, NewEVMTxContext(msg), statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
gas, err = ApplyStakingMessage(vmenv, msg, gp)
diff --git a/core/state_transition.go b/core/state_transition.go
index 3b11f774fc..c0da3b2d5c 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -150,6 +150,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) (ExecutionResult, error) {
+ evm.SetTxContext(NewEVMTxContext(msg))
return NewStateTransition(evm, msg, gp).TransitionDb()
}
@@ -216,8 +217,8 @@ func (st *StateTransition) TransitionDb() (ExecutionResult, error) {
}
msg := st.msg
sender := vm.AccountRef(msg.From())
- homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead
- istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.EpochNumber)
+ homestead := st.evm.ChainConfig().IsS3(st.evm.Context.EpochNumber) // s3 includes homestead
+ istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.EpochNumber)
contractCreation := msg.To() == nil
// Pay intrinsic gas
@@ -283,15 +284,15 @@ func (st *StateTransition) refundGas() {
}
func (st *StateTransition) collectGas() {
- if config := st.evm.ChainConfig(); !config.IsStaking(st.evm.EpochNumber) {
+ if config := st.evm.ChainConfig(); !config.IsStaking(st.evm.Context.EpochNumber) {
// Before staking epoch, add the fees to the block producer
txFee := new(big.Int).Mul(
new(big.Int).SetUint64(st.gasUsed()),
st.gasPrice,
)
- st.state.AddBalance(st.evm.Coinbase, txFee)
+ st.state.AddBalance(st.evm.Context.Coinbase, txFee)
} else if feeCollectors := shard.Schedule.InstanceForEpoch(
- st.evm.EpochNumber,
+ st.evm.Context.EpochNumber,
).FeeCollectors(); len(feeCollectors) > 0 {
// The caller must ensure that the feeCollectors are accurately set
// at the appropriate epochs
@@ -323,8 +324,8 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
msg := st.msg
sender := vm.AccountRef(msg.From())
- homestead := st.evm.ChainConfig().IsS3(st.evm.EpochNumber) // s3 includes homestead
- istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.EpochNumber)
+ homestead := st.evm.ChainConfig().IsS3(st.evm.Context.EpochNumber) // s3 includes homestead
+ istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.EpochNumber)
// Pay intrinsic gas
gas, err := vm.IntrinsicGas(st.data, false, homestead, istanbul, msg.Type() == types.StakeCreateVal)
@@ -358,7 +359,7 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if msg.From() != stkMsg.ValidatorAddress {
return 0, errInvalidSigner
}
- err = st.evm.CreateValidator(st.evm.StateDB, nil, stkMsg)
+ err = st.evm.Context.CreateValidator(st.evm.StateDB, nil, stkMsg)
case types.StakeEditVal:
stkMsg := &stakingTypes.EditValidator{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
@@ -369,7 +370,7 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if msg.From() != stkMsg.ValidatorAddress {
return 0, errInvalidSigner
}
- err = st.evm.EditValidator(st.evm.StateDB, nil, stkMsg)
+ err = st.evm.Context.EditValidator(st.evm.StateDB, nil, stkMsg)
case types.Delegate:
stkMsg := &stakingTypes.Delegate{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
@@ -379,7 +380,7 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if msg.From() != stkMsg.DelegatorAddress {
return 0, errInvalidSigner
}
- err = st.evm.Delegate(st.evm.StateDB, nil, stkMsg)
+ err = st.evm.Context.Delegate(st.evm.StateDB, nil, stkMsg)
case types.Undelegate:
stkMsg := &stakingTypes.Undelegate{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
@@ -389,7 +390,7 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if msg.From() != stkMsg.DelegatorAddress {
return 0, errInvalidSigner
}
- err = st.evm.Undelegate(st.evm.StateDB, nil, stkMsg)
+ err = st.evm.Context.Undelegate(st.evm.StateDB, nil, stkMsg)
case types.CollectRewards:
stkMsg := &stakingTypes.CollectRewards{}
if err = rlp.DecodeBytes(msg.Data(), stkMsg); err != nil {
@@ -399,7 +400,7 @@ func (st *StateTransition) StakingTransitionDb() (usedGas uint64, err error) {
if msg.From() != stkMsg.DelegatorAddress {
return 0, errInvalidSigner
}
- err = st.evm.CollectRewards(st.evm.StateDB, nil, stkMsg)
+ err = st.evm.Context.CollectRewards(st.evm.StateDB, nil, stkMsg)
default:
return 0, stakingTypes.ErrInvalidStakingKind
}
diff --git a/core/state_transition_test.go b/core/state_transition_test.go
index 925fe0848c..1dab62de90 100644
--- a/core/state_transition_test.go
+++ b/core/state_transition_test.go
@@ -72,8 +72,8 @@ func testApplyStakingMessage(test applyStakingMessageTest, t *testing.T) {
msg, _ := StakingToMessage(test.tx, header.Number())
// make EVM
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase */)
- vmenv := vm.NewEVM(ctx, db, params.TestChainConfig, vm.Config{})
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase */)
+ vmenv := vm.NewEVM(ctx, NewEVMTxContext(msg), db, params.TestChainConfig, vm.Config{})
// run the staking tx
_, err := ApplyStakingMessage(vmenv, msg, gp)
@@ -115,10 +115,10 @@ func TestCollectGas(t *testing.T) {
initialBalance := big.NewInt(2e18)
db.AddBalance(from, initialBalance)
msg, _ := tx.AsMessage(types.NewEIP155Signer(common.Big2))
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
ctx.TxType = types.SameShardTx
- vmenv := vm.NewEVM(ctx, db, params.TestChainConfig, vm.Config{})
+ vmenv := vm.NewEVM(ctx, NewEVMTxContext(msg), db, params.TestChainConfig, vm.Config{})
gasPool := new(GasPool).AddGas(math.MaxUint64)
_, err := ApplyMessage(vmenv, msg, gasPool)
if err != nil {
@@ -191,10 +191,11 @@ func TestCollectGasRounding(t *testing.T) {
initialBalance := big.NewInt(2e18)
db.AddBalance(from, initialBalance)
msg, _ := tx.AsMessage(types.NewEIP155Signer(common.Big2))
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
ctx.TxType = types.SameShardTx
- vmenv := vm.NewEVM(ctx, db, params.TestChainConfig, vm.Config{})
+ vmenv := vm.NewEVM(ctx, NewEVMTxContext(msg), db, params.TestChainConfig, vm.Config{})
+ vmenv.SetTxContext(NewEVMTxContext(msg))
gasPool := new(GasPool).AddGas(math.MaxUint64)
st := NewStateTransition(vmenv, msg, gasPool)
// buy gas to set initial gas to 5: gasLimit * gasPrice
@@ -242,7 +243,7 @@ func TestPrepare(t *testing.T) {
initialBalance := big.NewInt(2e18)
db.AddBalance(from, initialBalance)
msg, _ := tx.AsMessage(types.NewEIP155Signer(common.Big2))
- ctx := NewEVMContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
+ ctx := NewEVMBlockContext(msg, header, chain, nil /* coinbase is nil, no block reward */)
ctx.TxType = types.SameShardTx
// populate transient storage
@@ -254,7 +255,7 @@ func TestPrepare(t *testing.T) {
}
// transition state db by calling apply message
- vmenv := vm.NewEVM(ctx, db, params.TestChainConfig, vm.Config{})
+ vmenv := vm.NewEVM(ctx, NewEVMTxContext(msg), db, params.TestChainConfig, vm.Config{})
gasPool := new(GasPool).AddGas(math.MaxUint64)
_, err := ApplyMessage(vmenv, msg, gasPool)
if err != nil {
diff --git a/core/vm/common.go b/core/vm/common.go
index d592a9410d..90ba4a4ad1 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -17,15 +17,14 @@
package vm
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
)
// calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64
-func calcMemSize64(off, l *big.Int) (uint64, bool) {
+func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
if !l.IsUint64() {
return 0, true
}
@@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
// 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) {
+func calcMemSize64WithUint(off *uint256.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() {
+ offset64, overflow := off.Uint64WithOverflow()
+ if overflow {
return 0, true
}
- offset64 := off.Uint64()
val := offset64 + length64
// if value < either of it's parts, then it overflowed
return val, val < offset64
@@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
return common.RightPadBytes(data[start:end], int(size))
}
-// getDataBig returns a slice from the data based on the start and size and pads
-// up to size with zero's. This function is overflow safe.
-func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
- dlen := big.NewInt(int64(len(data)))
-
- s := math.BigMin(start, dlen)
- e := math.BigMin(new(big.Int).Add(s, size), dlen)
- return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
-}
-
-// bigUint64 returns the integer casted to a uint64 and returns whether it
-// overflowed in the process.
-func bigUint64(v *big.Int) (uint64, bool) {
- return v.Uint64(), !v.IsUint64()
-}
-
// toWordSize returns the ceiled word size required for memory expansion.
func toWordSize(size uint64) uint64 {
if size > math.MaxUint64-31 {
diff --git a/core/vm/contract.go b/core/vm/contract.go
index aa18231e0c..a262d7b625 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -20,6 +20,7 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// ContractRef is a reference to the contract's backing object
@@ -82,18 +83,43 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
return c
}
-func (c *Contract) validJumpdest(dest *big.Int) bool {
- udest := dest.Uint64()
+func (c *Contract) validJumpdest(dest *uint256.Int) bool {
+ udest, overflow := dest.Uint64WithOverflow()
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
- if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
+ if overflow || udest >= uint64(len(c.Code)) {
return false
}
// Only JUMPDESTs allowed for destinations
if OpCode(c.Code[udest]) != JUMPDEST {
return false
}
+ return c.isCode(udest)
+}
+
+func (c *Contract) validJumpSubdest(udest uint64) bool {
+ // PC cannot go beyond len(code) and certainly can't be bigger than 63 bits.
+ // Don't bother checking for BEGINSUB in that case.
+ if int64(udest) < 0 || udest >= uint64(len(c.Code)) {
+ return false
+ }
+ // Only BEGINSUBs allowed for destinations
+ if OpCode(c.Code[udest]) != BEGINSUB {
+ return false
+ }
+ return c.isCode(udest)
+}
+
+// isCode returns true if the provided PC location is an actual opcode, as
+// opposed to a data-segment following a PUSHN operation.
+func (c *Contract) isCode(udest uint64) bool {
+ // Do we already have an analysis laying around?
+ if c.analysis != nil {
+ return c.analysis.codeSegment(udest)
+ }
// Do we have a contract hash already?
+ // If we do have a hash, that means it's a 'regular' contract. For regular
+ // contracts ( not temporary initcode), we store the analysis in a map
if c.CodeHash != (common.Hash{}) {
// Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]
@@ -103,6 +129,8 @@ func (c *Contract) validJumpdest(dest *big.Int) bool {
analysis = codeBitmap(c.Code)
c.jumpdests[c.CodeHash] = analysis
}
+ // Also stash it in current contract for faster access
+ c.analysis = analysis
return analysis.codeSegment(udest)
}
// We don't have the code hash, most likely a piece of initcode not already
@@ -163,7 +191,7 @@ func (c *Contract) Address() common.Address {
return c.self.Address()
}
-// Value returns the contracts value (sent to it from it's caller)
+// Value returns the contract's value (sent to it from it's caller)
func (c *Contract) Value() *big.Int {
return c.value
}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 01625cb3d8..4d32a8e49c 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -19,22 +19,18 @@ package vm
import (
"crypto/sha256"
"encoding/binary"
+ "encoding/hex" //Needed for SHA3-256 FIPS202
"errors"
"fmt"
"math/big"
- "github.com/ethereum/go-ethereum/crypto/blake2b"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/harmony-one/harmony/internal/params"
"golang.org/x/crypto/ripemd160"
-
- //Needed for SHA3-256 FIPS202
- "encoding/hex"
-
"golang.org/x/crypto/sha3"
)
@@ -249,10 +245,14 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
type bigModExp struct{}
var (
+ big0 = big.NewInt(0)
big1 = big.NewInt(1)
+ big3 = big.NewInt(3)
big4 = big.NewInt(4)
+ big7 = big.NewInt(7)
big8 = big.NewInt(8)
big16 = big.NewInt(16)
+ big20 = big.NewInt(20)
big32 = big.NewInt(32)
big64 = big.NewInt(64)
big96 = big.NewInt(96)
diff --git a/core/vm/contracts_write.go b/core/vm/contracts_write.go
index 46ba7fe923..e1f6a10476 100644
--- a/core/vm/contracts_write.go
+++ b/core/vm/contracts_write.go
@@ -89,10 +89,10 @@ func (c *stakingPrecompile) RequiredGas(
// otherwise charge similar to a regular staking tx
if migrationMsg, ok := stakeMsg.(*stakingTypes.MigrationMsg); ok {
// charge per delegation to migrate
- return evm.CalculateMigrationGas(evm.StateDB,
+ return evm.Context.CalculateMigrationGas(evm.StateDB,
migrationMsg,
- evm.ChainConfig().IsS3(evm.EpochNumber),
- evm.ChainConfig().IsIstanbul(evm.EpochNumber),
+ evm.ChainConfig().IsS3(evm.Context.EpochNumber),
+ evm.ChainConfig().IsIstanbul(evm.Context.EpochNumber),
)
} else if encoded, err := rlp.EncodeToBytes(stakeMsg); err == nil {
payload = encoded
@@ -101,9 +101,9 @@ func (c *stakingPrecompile) RequiredGas(
}
if gas, err := IntrinsicGas(
payload,
- false, // contractCreation
- evm.ChainConfig().IsS3(evm.EpochNumber), // homestead
- evm.ChainConfig().IsIstanbul(evm.EpochNumber), // istanbul
+ false, // contractCreation
+ evm.ChainConfig().IsS3(evm.Context.EpochNumber), // homestead
+ evm.ChainConfig().IsIstanbul(evm.Context.EpochNumber), // istanbul
false, // isValidatorCreation
); err != nil {
return 0, err // ErrOutOfGas occurs when gas payable > uint64
@@ -132,7 +132,7 @@ func (c *stakingPrecompile) RunWriteCapable(
}
if delegate, ok := stakeMsg.(*stakingTypes.Delegate); ok {
- if err := evm.Delegate(evm.StateDB, rosettaBlockTracer, delegate); err != nil {
+ if err := evm.Context.Delegate(evm.StateDB, rosettaBlockTracer, delegate); err != nil {
return nil, err
} else {
evm.StakeMsgs = append(evm.StakeMsgs, delegate)
@@ -140,10 +140,10 @@ func (c *stakingPrecompile) RunWriteCapable(
}
}
if undelegate, ok := stakeMsg.(*stakingTypes.Undelegate); ok {
- return nil, evm.Undelegate(evm.StateDB, rosettaBlockTracer, undelegate)
+ return nil, evm.Context.Undelegate(evm.StateDB, rosettaBlockTracer, undelegate)
}
if collectRewards, ok := stakeMsg.(*stakingTypes.CollectRewards); ok {
- return nil, evm.CollectRewards(evm.StateDB, rosettaBlockTracer, collectRewards)
+ return nil, evm.Context.CollectRewards(evm.StateDB, rosettaBlockTracer, collectRewards)
}
// Migrate is not supported in precompile and will be done in a batch hard fork
//if migrationMsg, ok := stakeMsg.(*stakingTypes.MigrationMsg); ok {
@@ -242,7 +242,7 @@ func (c *crossShardXferPrecompile) RunWriteCapable(
return nil, err
}
// validate not a contract (toAddress can still be a contract)
- if len(evm.StateDB.GetCode(fromAddress)) > 0 && !evm.IsValidator(evm.StateDB, fromAddress) {
+ if len(evm.StateDB.GetCode(fromAddress)) > 0 && !evm.Context.IsValidator(evm.StateDB, fromAddress) {
return nil, errors.New("cross shard xfer not yet implemented for contracts")
}
// can't have too many shards
@@ -259,10 +259,10 @@ func (c *crossShardXferPrecompile) RunWriteCapable(
}
// now do the actual transfer
// step 1 -> remove funds from the precompile address
- if !evm.CanTransfer(evm.StateDB, contract.Address(), value) {
+ if !evm.Context.CanTransfer(evm.StateDB, contract.Address(), value) {
return nil, errors.New("not enough balance received")
}
- evm.Transfer(evm.StateDB, contract.Address(), toAddress, value, types.SubtractionOnly)
+ evm.Context.Transfer(evm.StateDB, contract.Address(), toAddress, value, types.SubtractionOnly)
// step 2 -> make a cross link
// note that the transaction hash is added by state_processor.go to this receipt
// and that the receiving shard does not care about the `From` but we use the original
@@ -301,5 +301,5 @@ func parseCrossShardXferData(evm *EVM, contract *Contract, input []byte) (
if err != nil {
return common.Address{}, common.Address{}, 0, 0, nil, err
}
- return contract.Caller(), toAddress, evm.ShardID, toShardID, value, nil
+ return contract.Caller(), toAddress, evm.Context.ShardID, toShardID, value, nil
}
diff --git a/core/vm/contracts_write_test.go b/core/vm/contracts_write_test.go
index 479bf420fc..1331956f68 100644
--- a/core/vm/contracts_write_test.go
+++ b/core/vm/contracts_write_test.go
@@ -92,7 +92,7 @@ func testWriteCapablePrecompile(test writeCapablePrecompileTest, t *testing.T, e
}
func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
- var env = NewEVM(Context{CollectRewards: CollectRewardsFn(),
+ var env = NewEVM(BlockContext{CollectRewards: CollectRewardsFn(),
Delegate: DelegateFn(),
Undelegate: UndelegateFn(),
CreateValidator: CreateValidatorFn(),
@@ -100,7 +100,7 @@ func testStakingPrecompile(test writeCapablePrecompileTest, t *testing.T) {
ShardID: 0,
//MigrateDelegations: MigrateDelegationsFn(),
CalculateMigrationGas: CalculateMigrationGasFn(),
- }, nil, params.TestChainConfig, Config{})
+ }, TxContext{}, nil, params.TestChainConfig, Config{})
p := &stakingPrecompile{}
testWriteCapablePrecompile(test, t, env, p)
}
@@ -239,13 +239,13 @@ func testCrossShardXferPrecompile(test writeCapablePrecompileTest, t *testing.T)
if err != nil {
t.Fatalf("Error while initializing state %s", err)
}
- var env = NewEVM(Context{
+ var env = NewEVM(BlockContext{
NumShards: 4,
Transfer: transfer,
CanTransfer: func(_ StateDB, _ common.Address, _ *big.Int) bool {
return true
},
- }, state, params.TestChainConfig, Config{})
+ }, TxContext{}, state, params.TestChainConfig, Config{})
p := &crossShardXferPrecompile{}
testWriteCapablePrecompile(test, t, env, p)
}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 3274c9fee7..6a7a00d9d3 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -18,31 +18,45 @@ package vm
import (
"fmt"
- "math/big"
+ "sort"
- "github.com/ethereum/go-ethereum/common"
- "github.com/harmony-one/harmony/internal/params"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
)
+var activators = map[int]func(*JumpTable){
+ 2929: enable2929,
+ 2200: enable2200,
+ 1884: enable1884,
+ 1344: enable1344,
+ 2315: enable2315,
+}
+
// 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)
- case 1153:
- enable1153(jt)
- default:
+ enablerFn, ok := activators[eipNum]
+ if !ok {
return fmt.Errorf("undefined eip %d", eipNum)
}
+ enablerFn(jt)
return nil
}
+func ValidEip(eipNum int) bool {
+ _, ok := activators[eipNum]
+ return ok
+}
+func ActivateableEips() []string {
+ var nums []string
+ for k := range activators {
+ nums = append(nums, fmt.Sprintf("%d", k))
+ }
+ sort.Strings(nums)
+ return nums
+}
+
// enable1884 applies EIP-1884 to the given jump table:
// - Increase cost of BALANCE to 700
// - Increase cost of EXTCODEHASH to 700
@@ -50,23 +64,22 @@ func EnableEIP(eipNum int, jt *JumpTable) error {
// - Define SELFBALANCE, with cost GasFastStep (5)
func enable1884(jt *JumpTable) {
// Gas cost changes
+ jt[SLOAD].constantGas = params.SloadGasEIP1884
jt[BALANCE].constantGas = params.BalanceGasEIP1884
jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
- jt[SLOAD].constantGas = params.SloadGasEIP1884
// New opcode
- jt[SELFBALANCE] = operation{
+ 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)
+func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
+ callContext.stack.push(balance)
return nil, nil
}
@@ -74,80 +87,117 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
// - Adds an opcode that returns the current chain’s EIP-155 unique identifier
func enable1344(jt *JumpTable) {
// New opcode
- jt[CHAINID] = operation{
+ 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) {
- var chainID *big.Int
- if interpreter.evm.chainConfig.IsChainIdFix(interpreter.evm.EpochNumber) {
- chainID = interpreter.intPool.get().Set(interpreter.evm.chainConfig.EthCompatibleChainID)
- } else {
- chainID = interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
- }
- stack.push(chainID)
+func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
+ callContext.stack.push(chainId)
return nil, nil
}
// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE)
func enable2200(jt *JumpTable) {
+ jt[SLOAD].constantGas = params.SloadGasEIP2200
jt[SSTORE].dynamicGas = gasSStoreEIP2200
}
-// enable1153 applies EIP-1153 "Transient Storage"
-// - Adds TLOAD that reads from transient storage
-// - Adds TSTORE that writes to transient storage
-func enable1153(jt *JumpTable) {
- jt[TLOAD] = operation{
- execute: opTload,
- constantGas: params.WarmStorageReadCostEIP2929,
- minStack: minStack(1, 1),
- maxStack: maxStack(1, 1),
+// enable2315 applies EIP-2315 (Simple Subroutines)
+// - Adds opcodes that jump to and return from subroutines
+func enable2315(jt *JumpTable) {
+ // New opcode
+ jt[BEGINSUB] = &operation{
+ execute: opBeginSub,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 0),
+ maxStack: maxStack(0, 0),
}
-
- jt[TSTORE] = operation{
- execute: opTstore,
- constantGas: params.WarmStorageReadCostEIP2929,
- minStack: minStack(2, 0),
- maxStack: maxStack(2, 0),
+ // New opcode
+ jt[JUMPSUB] = &operation{
+ execute: opJumpSub,
+ constantGas: GasSlowStep,
+ minStack: minStack(1, 0),
+ maxStack: maxStack(1, 0),
+ jumps: true,
+ }
+ // New opcode
+ jt[RETURNSUB] = &operation{
+ execute: opReturnSub,
+ constantGas: GasFastStep,
+ minStack: minStack(0, 0),
+ maxStack: maxStack(0, 0),
+ jumps: true,
}
}
-// opTload implements TLOAD opcode
-func opTload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- loc := stack.peek()
+// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
+// https://eips.ethereum.org/EIPS/eip-2929
+func enable2929(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP2929
- keyBuf := make([]byte, 32)
- key := common.BytesToHash(loc.FillBytes(keyBuf))
+ jt[SLOAD].constantGas = 0
+ jt[SLOAD].dynamicGas = gasSLoadEIP2929
- val := interpreter.evm.StateDB.GetTransientState(contract.Address(), key)
- valBuf := make([]byte, 32)
- val.Big().FillBytes(valBuf)
+ jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
- loc.SetBytes(valBuf)
- return nil, nil
-}
+ jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
-// opTstore implements TSTORE opcode
-func opTstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- if interpreter.readOnly {
- return nil, errWriteProtection
- }
+ jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
+ jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
+
+ jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
+ jt[BALANCE].dynamicGas = gasEip2929AccountCheck
+
+ jt[CALL].constantGas = WarmStorageReadCostEIP2929
+ jt[CALL].dynamicGas = gasCallEIP2929
+
+ jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
+ jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
+
+ jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
+ jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
+
+ jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
+ jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
+
+ // This was previously part of the dynamic cost, but we're using it as a constantGas
+ // factor here
+ jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
+}
- keyBuf := make([]byte, 32)
- stack.pop().FillBytes(keyBuf)
- key := common.Hash(keyBuf)
+// enable3529 enabled "EIP-3529: Reduction in refunds":
+// - Removes refunds for selfdestructs
+// - Reduces refunds for SSTORE
+// - Reduces max refunds to 20% gas
+func enable3529(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP3529
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
+}
- valBuf := make([]byte, 32)
- stack.pop().FillBytes(valBuf)
- val := common.Hash(valBuf)
+// enable3198 applies EIP-3198 (BASEFEE Opcode)
+// - Adds an opcode that returns the current block's base fee.
+func enable3198(jt *JumpTable) {
+ // New opcode
+ jt[BASEFEE] = &operation{
+ execute: opBaseFee,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+}
- interpreter.evm.StateDB.SetTransientState(contract.Address(), key, val)
+// opBaseFee implements BASEFEE opcode
+func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
+ scope.Stack.push(baseFee)
return nil, nil
}
diff --git a/core/vm/errors.go b/core/vm/errors.go
index 7f88f324ea..9086561342 100644
--- a/core/vm/errors.go
+++ b/core/vm/errors.go
@@ -16,15 +16,57 @@
package vm
-import "errors"
+import (
+ "errors"
+ "fmt"
+)
-// List execution errors
+// List evm execution errors
var (
+ // ErrInvalidSubroutineEntry means that a BEGINSUB was reached via iteration,
+ // as opposed to from a JUMPSUB instruction
+ ErrInvalidSubroutineEntry = errors.New("invalid subroutine entry")
ErrOutOfGas = errors.New("out of gas")
ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
ErrDepth = errors.New("max call depth exceeded")
- ErrTraceLimitReached = errors.New("the number of logs reached the specified limit")
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
ErrContractAddressCollision = errors.New("contract address collision")
+ ErrExecutionReverted = errors.New("execution reverted")
+ ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
+ ErrInvalidJump = errors.New("invalid jump destination")
+ ErrWriteProtection = errors.New("write protection")
+ ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
+ ErrGasUintOverflow = errors.New("gas uint64 overflow")
+ ErrInvalidRetsub = errors.New("invalid retsub")
+ ErrReturnStackExceeded = errors.New("return stack limit reached")
ErrNoCompatibleInterpreter = errors.New("no compatible interpreter")
)
+
+// ErrStackUnderflow wraps an evm error when the items on the stack less
+// than the minimal requirement.
+type ErrStackUnderflow struct {
+ stackLen int
+ required int
+}
+
+func (e *ErrStackUnderflow) Error() string {
+ return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required)
+}
+
+// ErrStackOverflow wraps an evm error when the items on the stack exceeds
+// the maximum allowance.
+type ErrStackOverflow struct {
+ stackLen int
+ limit int
+}
+
+func (e *ErrStackOverflow) Error() string {
+ return fmt.Sprintf("stack limit reached %d (%d)", e.stackLen, e.limit)
+}
+
+// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
+type ErrInvalidOpCode struct {
+ opcode OpCode
+}
+
+func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 55f6f8c93d..3f83b22f05 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -26,6 +26,7 @@ import (
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/params"
stakingTypes "github.com/harmony-one/harmony/staking/types"
+ "github.com/holiman/uint256"
)
// emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -71,7 +72,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
precompiles := PrecompiledContractsHomestead
// assign empty write capable precompiles till they are available in the fork
var writeCapablePrecompiles map[common.Address]WriteCapablePrecompiledContract
- if evm.ChainConfig().IsS3(evm.EpochNumber) {
+ if evm.ChainConfig().IsS3(evm.Context.EpochNumber) {
precompiles = PrecompiledContractsByzantium
}
if evm.chainRules.IsIstanbul {
@@ -94,13 +95,13 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
if _, ok := p.(*vrf); ok {
if evm.chainRules.IsPrevVRF {
requestedBlockNum := big.NewInt(0).SetBytes(input)
- minBlockNum := big.NewInt(0).Sub(evm.BlockNumber, common.Big257)
+ minBlockNum := big.NewInt(0).Sub(evm.Context.BlockNumber, common.Big257)
- if requestedBlockNum.Cmp(evm.BlockNumber) == 0 {
+ if requestedBlockNum.Cmp(evm.Context.BlockNumber) == 0 {
input = evm.Context.VRF.Bytes()
- } else if requestedBlockNum.Cmp(minBlockNum) > 0 && requestedBlockNum.Cmp(evm.BlockNumber) < 0 {
+ } else if requestedBlockNum.Cmp(minBlockNum) > 0 && requestedBlockNum.Cmp(evm.Context.BlockNumber) < 0 {
// requested block number is in range
- input = evm.GetVRF(requestedBlockNum.Uint64()).Bytes()
+ input = evm.Context.GetVRF(requestedBlockNum.Uint64()).Bytes()
} else {
// else default to the current block's VRF
input = evm.Context.VRF.Bytes()
@@ -110,7 +111,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
input = evm.Context.VRF.Bytes()
}
} else if _, ok := p.(*epoch); ok {
- input = evm.EpochNumber.Bytes()
+ input = evm.Context.EpochNumber.Bytes()
}
return RunPrecompiledContract(p, input, contract)
}
@@ -131,7 +132,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
evm.interpreter = interpreter
}
- if evm.ChainConfig().IsDataCopyFixEpoch(evm.EpochNumber) {
+ if evm.ChainConfig().IsDataCopyFixEpoch(evm.Context.EpochNumber) {
contract.WithDataCopyFix = true
}
return interpreter.Run(contract, input, readOnly)
@@ -183,6 +184,53 @@ type Context struct {
NumShards uint32 // Used by cross shard transfer precompile
}
+// BlockContext provides the EVM with auxiliary information. Once provided
+// it shouldn't be modified.
+type BlockContext struct {
+ // CanTransfer returns whether the account contains
+ // sufficient ether to transfer the value
+ CanTransfer CanTransferFunc
+ // Transfer transfers ether from one account to the other
+ Transfer TransferFunc
+ // GetHash returns the hash corresponding to n
+ GetHash GetHashFunc
+ // GetVRF returns the VRF corresponding to n
+ GetVRF GetVRFFunc
+
+ // IsValidator determines whether the address corresponds to a validator or a smart contract
+ // true: is a validator address; false: is smart contract address
+ IsValidator IsValidatorFunc
+
+ // Block information
+ Coinbase common.Address // Provides information for COINBASE
+ GasLimit uint64 // Provides information for GASLIMIT
+ BlockNumber *big.Int // Provides information for NUMBER
+ EpochNumber *big.Int // Provides information for EPOCH
+ Time *big.Int // Provides information for TIME
+ VRF common.Hash // Provides information for VRF
+
+ ShardID uint32 // Used by staking and cross shard transfer precompile
+ NumShards uint32 // Used by cross shard transfer precompile
+
+ CreateValidator CreateValidatorFunc
+ EditValidator EditValidatorFunc
+ Delegate DelegateFunc
+ Undelegate UndelegateFunc
+ CollectRewards CollectRewardsFunc
+ CalculateMigrationGas CalculateMigrationGasFunc
+
+ TxType types.TransactionType
+}
+
+// TxContext provides the EVM with information about a transaction.
+// All fields can change between transactions.
+type TxContext struct {
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+
+}
+
// EVM is the Ethereum Virtual Machine base object and provides
// the necessary tools to run a contract on the given state with
// the provided context. It should be noted that any error
@@ -194,8 +242,9 @@ type Context struct {
// The EVM should never be reused and is not thread safe.
type EVM struct {
// Context provides auxiliary blockchain related information
- Context
- // DB gives access to the underlying state
+ Context BlockContext
+ TxContext
+ // StateDB gives access to the underlying state
StateDB StateDB
// Depth is the current call stack
depth int
@@ -218,6 +267,7 @@ type EVM struct {
// available gas is calculated in gasCall* according to the 63/64 rule and later
// applied in opCall*.
callGasTemp uint64
+
// stored temporarily by stakingPrecompile and cleared immediately after return
// (although the EVM object itself is ephemeral)
StakeMsgs []stakingTypes.StakeMsg
@@ -226,13 +276,14 @@ type EVM struct {
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
-func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
+func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
- Context: ctx,
+ Context: blockCtx,
+ TxContext: txCtx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
- chainRules: chainConfig.Rules(ctx.EpochNumber),
+ chainRules: chainConfig.Rules(blockCtx.EpochNumber),
interpreters: make([]Interpreter, 0, 1),
}
@@ -276,6 +327,15 @@ func (evm *EVM) Interpreter() Interpreter {
return evm.interpreter
}
+// SetTxContext resets the EVM with a new transaction context.
+// This is not threadsafe and should only be done very cautiously.
+func (evm *EVM) SetTxContext(txCtx TxContext) {
+ //if evm.chainRules.IsEIP4762 {
+ // txCtx.AccessEvents = state.NewAccessEvents(evm.StateDB.PointCache())
+ //}
+ evm.TxContext = txCtx
+}
+
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
@@ -304,7 +364,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !evm.StateDB.Exist(addr) && txType != types.SubtractionOnly {
precompiles := PrecompiledContractsHomestead
var writeCapablePrecompiles map[common.Address]WriteCapablePrecompiledContract
- if evm.ChainConfig().IsS3(evm.EpochNumber) {
+ if evm.ChainConfig().IsS3(evm.Context.EpochNumber) {
precompiles = PrecompiledContractsByzantium
}
if evm.chainRules.IsIstanbul {
@@ -323,7 +383,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if evm.chainRules.IsCrossShardXferPrecompile {
writeCapablePrecompiles = WriteCapablePrecompiledContractsCrossXfer
}
- if (len(writeCapablePrecompiles) == 0 || writeCapablePrecompiles[addr] == nil) && precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.EpochNumber) && value.Sign() == 0 {
+ if (len(writeCapablePrecompiles) == 0 || writeCapablePrecompiles[addr] == nil) && precompiles[addr] == nil && evm.ChainConfig().IsS3(evm.Context.EpochNumber) && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
@@ -333,7 +393,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
evm.StateDB.CreateAccount(addr)
}
- evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value, txType)
+ evm.Context.Transfer(evm.StateDB, caller.Address(), to.Address(), value, txType)
codeHash := evm.StateDB.GetCodeHash(addr)
code := evm.StateDB.GetCode(addr)
@@ -390,7 +450,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
- if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
@@ -508,7 +568,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.depth > int(params.CallCreateDepth) {
return nil, common.Address{}, gas, ErrDepth
}
- if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
nonce := evm.StateDB.GetNonce(caller.Address())
@@ -522,14 +582,13 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Create a new account on the state
snapshot := evm.StateDB.Snapshot()
evm.StateDB.CreateAccount(address)
- if evm.ChainConfig().IsEIP155(evm.EpochNumber) {
+ if evm.chainRules.IsS3 {
evm.StateDB.SetNonce(address, 1)
}
- evm.Transfer(evm.StateDB, caller.Address(), address, value, types.SameShardTx)
+ evm.Context.Transfer(evm.StateDB, caller.Address(), address, value, types.SameShardTx)
- // initialise a new contract and set the code that is to be used by the
- // EVM. The contract is a scoped environment for this execution context
- // only.
+ // Initialise a new contract and set the code that is to be used by the EVM.
+ // The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)
@@ -545,7 +604,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
ret, err := run(evm, contract, nil, false)
// check whether the max code size has been exceeded
- maxCodeSizeExceeded := evm.ChainConfig().IsEIP155(evm.EpochNumber) && len(ret) > params.MaxCodeSize
+ maxCodeSizeExceeded := evm.ChainConfig().IsEIP155(evm.Context.EpochNumber) && len(ret) > params.MaxCodeSize
// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
@@ -562,7 +621,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
- if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsS3(evm.EpochNumber) || err != ErrCodeStoreOutOfGas)) {
+ if maxCodeSizeExceeded || (err != nil && (evm.ChainConfig().IsS3(evm.Context.EpochNumber) || err != ErrCodeStoreOutOfGas)) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
@@ -589,9 +648,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
//
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
-func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
- contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
+ contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
}
diff --git a/core/vm/evm_test.go b/core/vm/evm_test.go
index 10e50bdbf2..f3c0f179af 100644
--- a/core/vm/evm_test.go
+++ b/core/vm/evm_test.go
@@ -11,9 +11,9 @@ import (
// this test is here so we can cover the input = epoch.bytes() line as well
func TestEpochPrecompile(t *testing.T) {
targetEpoch := big.NewInt(1)
- evm := NewEVM(Context{
+ evm := NewEVM(BlockContext{
EpochNumber: targetEpoch,
- }, nil, params.TestChainConfig, Config{})
+ }, TxContext{}, nil, params.TestChainConfig, Config{})
input := []byte{}
precompileAddr := common.BytesToAddress([]byte{251})
contract := Contract{
diff --git a/core/vm/gas.go b/core/vm/gas.go
index 1b70753407..54a039326e 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -18,9 +18,9 @@ package vm
import (
"math"
- "math/big"
"github.com/harmony-one/harmony/internal/params"
+ "github.com/holiman/uint256"
)
// Gas costs
@@ -33,23 +33,23 @@ const (
GasExtStep uint64 = 20
)
-// calcGas returns the actual gas cost of the call.
+// callGas returns the actual gas cost of the call.
//
// The cost of gas was changed during the homestead price change HF.
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
-func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
+func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
if isEip150 {
availableGas = availableGas - base
gas := availableGas - availableGas/64
// 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. Therefore we return the new gas instead
// of returning an error.
if !callCost.IsUint64() || gas < callCost.Uint64() {
return gas, nil
}
}
if !callCost.IsUint64() {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return callCost.Uint64(), nil
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 892feaa786..71d8ef30ee 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -70,17 +70,17 @@ func memoryCopierGas(stackpos int) gasFunc {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
- words, overflow := bigUint64(stack.Back(stackpos))
+ words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, words); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -96,7 +96,7 @@ var (
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
- current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
)
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@@ -131,11 +131,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
- value := common.BigToHash(y)
+ value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
@@ -183,45 +183,45 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// 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))
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
)
- value := common.BigToHash(y)
+ value := common.Hash(y.Bytes32())
if current == value { // noop (1)
- return params.SstoreNoopGasEIP2200, nil
+ return params.SloadGasEIP2200, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
- return params.SstoreInitGasEIP2200, nil
+ return params.SstoreSetGasEIP2200, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
- evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
- return params.SstoreCleanGasEIP2200, nil // write existing slot (2.1.2)
+ return params.SstoreResetGasEIP2200, 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)
+ evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
- evm.StateDB.AddRefund(params.SstoreClearRefundEIP2200)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
- evm.StateDB.AddRefund(params.SstoreInitRefundEIP2200)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
} else { // reset to original existing slot (2.2.2.2)
- evm.StateDB.AddRefund(params.SstoreCleanRefundEIP2200)
+ evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
}
}
- return params.SstoreDirtyGasEIP2200, nil // dirty update (2.2)
+ return params.SloadGasEIP2200, nil // dirty update (2.2)
}
func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- requestedSize, overflow := bigUint64(stack.Back(1))
+ requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
gas, err := memoryGasCost(mem, memorySize)
@@ -230,18 +230,18 @@ func makeGasLog(n uint64) gasFunc {
}
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
var memorySizeGas uint64
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -252,15 +252,15 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if err != nil {
return 0, err
}
- wordGas, overflow := bigUint64(stack.Back(1))
+ wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -286,15 +286,15 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if err != nil {
return 0, err
}
- wordGas, overflow := bigUint64(stack.Back(2))
+ wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -328,8 +328,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
- transfersValue = stack.Back(2).Sign() != 0
- address = common.BigToAddress(stack.Back(1))
+ transfersValue = !stack.Back(2).IsZero()
+ address = common.Address(stack.Back(1).Bytes20())
)
if evm.chainRules.IsS3 {
if transfersValue && evm.StateDB.Empty(address) {
@@ -347,7 +347,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
}
var overflow bool
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))
@@ -355,7 +355,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
return 0, err
}
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// EIP150 homestead gas reprice fork:
if evm.chainRules.IsS3 {
gas = params.SelfdestructGasEIP150
- var address = common.BigToAddress(stack.Back(0))
+ var address = common.Address(stack.Back(0).Bytes20())
if evm.chainRules.IsS3 {
// if empty and transfers value
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 65973e832c..2e4902bab0 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -89,12 +89,12 @@ func TestEIP2200(t *testing.T) {
statedb.SetState(address, common.Hash{}, common.BytesToHash([]byte{tt.original}))
statedb.Finalise(true) // Push the state into the "original" slot
- vmctx := Context{
+ vmctx := BlockContext{
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}})
+ vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllProtocolChanges, Config{ExtraEips: []int{2200}})
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
if err != tt.failure {
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index c87cc8fa6b..4abbd0b91e 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -25,6 +25,8 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
Memory hexutil.Bytes `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
+ ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
+ ReturnData hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
@@ -50,6 +52,13 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
enc.Stack[k] = (*math.HexOrDecimal256)(v)
}
}
+ if s.ReturnStack != nil {
+ enc.ReturnStack = make([]math.HexOrDecimal64, len(s.ReturnStack))
+ for k, v := range s.ReturnStack {
+ enc.ReturnStack[k] = math.HexOrDecimal64(v)
+ }
+ }
+ enc.ReturnData = s.ReturnData
enc.Storage = s.Storage
enc.Depth = s.Depth
enc.RefundCounter = s.RefundCounter
@@ -74,6 +83,8 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
Memory *hexutil.Bytes `json:"memory"`
MemorySize *int `json:"memSize"`
Stack []*math.HexOrDecimal256 `json:"stack"`
+ ReturnStack []math.HexOrDecimal64 `json:"returnStack"`
+ ReturnData *hexutil.Bytes `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"`
@@ -116,6 +127,15 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
s.Stack[k] = (*big.Int)(v)
}
}
+ if dec.ReturnStack != nil {
+ s.ReturnStack = make([]uint32, len(dec.ReturnStack))
+ for k, v := range dec.ReturnStack {
+ s.ReturnStack[k] = uint32(v)
+ }
+ }
+ if dec.ReturnData != nil {
+ s.ReturnData = *dec.ReturnData
+ }
if dec.Storage != nil {
s.Storage = dec.Storage
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 091ba28ff6..e00250a0af 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -22,9 +22,10 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
- "github.com/harmony-one/harmony/core/types"
- "github.com/harmony-one/harmony/internal/params"
- "github.com/harmony-one/harmony/shard"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
+ coreTypes "github.com/harmony-one/harmony/core/types"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
@@ -33,359 +34,221 @@ var (
tt255 = math.BigPow(2, 255)
errWriteProtection = errors.New("evm: write protection")
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
- ErrExecutionReverted = errors.New("evm: execution reverted")
- errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
- errInvalidJump = errors.New("evm: invalid jump destination")
+ //ErrExecutionReverted = errors.New("evm: execution reverted")
+ errMaxCodeSizeExceeded = errors.New("evm: max code size exceeded")
+ errInvalidJump = errors.New("evm: invalid jump destination")
)
-func opAdd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- math.U256(y.Add(x, y))
-
- interpreter.intPool.put(x)
+func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Add(&x, y)
return nil, nil
}
-func opSub(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- math.U256(y.Sub(x, y))
-
- interpreter.intPool.put(x)
+func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Sub(&x, y)
return nil, nil
}
-func opMul(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- stack.push(math.U256(x.Mul(x, y)))
-
- interpreter.intPool.put(y)
-
+func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mul(&x, y)
return nil, nil
}
-func opDiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if y.Sign() != 0 {
- math.U256(y.Div(x, y))
- } else {
- y.SetUint64(0)
- }
- interpreter.intPool.put(x)
+func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Div(&x, y)
return nil, nil
}
-func opSdiv(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := math.S256(stack.pop()), math.S256(stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 || x.Sign() == 0 {
- stack.push(res)
- } else {
- if x.Sign() != y.Sign() {
- res.Div(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Div(x.Abs(x), y.Abs(y))
- }
- stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SDiv(&x, y)
return nil, nil
}
-func opMod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- if y.Sign() == 0 {
- stack.push(x.SetUint64(0))
- } else {
- stack.push(math.U256(x.Mod(x, y)))
- }
- interpreter.intPool.put(y)
+func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mod(&x, y)
return nil, nil
}
-func opSmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := math.S256(stack.pop()), math.S256(stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 {
- stack.push(res)
- } else {
- if x.Sign() < 0 {
- res.Mod(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Mod(x.Abs(x), y.Abs(y))
- }
- stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SMod(&x, y)
return nil, nil
}
-func opExp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- base, exponent := stack.pop(), stack.pop()
- // some shortcuts
- cmpToOne := exponent.Cmp(big1)
- if cmpToOne < 0 { // Exponent is zero
- // x ^ 0 == 1
- stack.push(base.SetUint64(1))
- } else if base.Sign() == 0 {
- // 0 ^ y, if y != 0, == 0
- stack.push(base.SetUint64(0))
- } else if cmpToOne == 0 { // Exponent is one
- // x ^ 1 == x
- stack.push(base)
- } else {
- stack.push(math.Exp(base, exponent))
- interpreter.intPool.put(base)
- }
- interpreter.intPool.put(exponent)
+func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ base, exponent := callContext.stack.pop(), callContext.stack.peek()
+ exponent.Exp(&base, exponent)
return nil, nil
}
-func opSignExtend(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- back := stack.pop()
- if back.Cmp(big.NewInt(31)) < 0 {
- bit := uint(back.Uint64()*8 + 7)
- num := stack.pop()
- mask := back.Lsh(common.Big1, bit)
- mask.Sub(mask, common.Big1)
- if num.Bit(int(bit)) > 0 {
- num.Or(num, mask.Not(mask))
- } else {
- num.And(num, mask)
- }
-
- stack.push(math.U256(num))
- }
-
- interpreter.intPool.put(back)
+func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ back, num := callContext.stack.pop(), callContext.stack.peek()
+ num.ExtendSign(num, &back)
return nil, nil
}
-func opNot(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x := stack.peek()
- math.U256(x.Not(x))
+func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ x.Not(x)
return nil, nil
}
-func opLt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
+func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Lt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opGt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
+func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Gt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opSlt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(1)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(0)
-
- default:
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Slt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opSgt(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(0)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(1)
-
- default:
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Sgt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opEq(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- if x.Cmp(y) == 0 {
- y.SetUint64(1)
+func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ if x.Eq(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.put(x)
return nil, nil
}
-func opIszero(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x := stack.peek()
- if x.Sign() > 0 {
- x.SetUint64(0)
+func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ if x.IsZero() {
+ x.SetOne()
} else {
- x.SetUint64(1)
+ x.Clear()
}
return nil, nil
}
-func opAnd(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.pop()
- stack.push(x.And(x, y))
-
- interpreter.intPool.put(y)
+func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.And(&x, y)
return nil, nil
}
-func opOr(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- y.Or(x, y)
-
- interpreter.intPool.put(x)
+func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Or(&x, y)
return nil, nil
}
-func opXor(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y := stack.pop(), stack.peek()
- y.Xor(x, y)
-
- interpreter.intPool.put(x)
+func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Xor(&x, y)
return nil, nil
}
-func opByte(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- th, val := stack.pop(), stack.peek()
- if th.Cmp(common.Big32) < 0 {
- b := math.Byte(val, 32, int(th.Int64()))
- val.SetUint64(uint64(b))
- } else {
- val.SetUint64(0)
- }
- interpreter.intPool.put(th)
+func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ th, val := callContext.stack.pop(), callContext.stack.peek()
+ val.Byte(&th)
return nil, nil
}
-func opAddmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Add(x, y)
- x.Mod(x, z)
- stack.push(math.U256(x))
+func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ if z.IsZero() {
+ z.Clear()
} else {
- stack.push(x.SetUint64(0))
+ z.AddMod(&x, &y, z)
}
- interpreter.intPool.put(y, z)
return nil, nil
}
-func opMulmod(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- x, y, z := stack.pop(), stack.pop(), stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Mul(x, y)
- x.Mod(x, z)
- stack.push(math.U256(x))
- } else {
- stack.push(x.SetUint64(0))
- }
- interpreter.intPool.put(y, z)
+func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ z.MulMod(&x, &y, z)
return nil, nil
}
// opSHL implements Shift Left
// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
-func opSHL(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(stack.pop()), math.U256(stack.peek())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Lsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Lsh(value, n))
-
return nil, nil
}
// opSHR implements Logical Shift Right
// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
-func opSHR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(stack.pop()), math.U256(stack.peek())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Rsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Rsh(value, n))
-
return nil, nil
}
// opSAR implements Arithmetic Shift Right
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
-func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
- shift, value := math.U256(stack.pop()), math.S256(stack.pop())
- defer interpreter.intPool.put(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
+func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.GtUint64(256) {
if value.Sign() >= 0 {
- value.SetUint64(0)
+ value.Clear()
} else {
- value.SetInt64(-1)
+ // Max negative shift: all bits set
+ value.SetAllOne()
}
- stack.push(math.U256(value))
return nil, nil
}
n := uint(shift.Uint64())
- value.Rsh(value, n)
- stack.push(math.U256(value))
-
+ value.SRsh(value, n)
return nil, nil
}
-func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset, size := stack.pop(), stack.pop()
- data := memory.GetPtr(offset.Int64(), size.Int64())
+func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.peek()
+ data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@@ -399,141 +262,143 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
- stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
- interpreter.intPool.put(offset, size)
+ size.SetBytes(interpreter.hasherBuf[:])
return nil, nil
}
-
-func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(contract.Address().Bytes()))
+func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
return nil, nil
}
-func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- slot := stack.peek()
- slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
+func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ address := common.Address(slot.Bytes20())
+ slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
-func opOrigin(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
+func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
-
-func opCaller(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(contract.Caller().Bytes()))
+func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
return nil, nil
}
-func opCallValue(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().Set(contract.value))
+func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(callContext.contract.value)
+ callContext.stack.push(v)
return nil, nil
}
-func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(getDataBig(contract.Input, stack.pop(), big32)))
+func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ x := callContext.stack.peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(callContext.contract.Input, offset, 32)
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
return nil, nil
}
-func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetInt64(int64(len(contract.Input))))
+func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
return nil, nil
}
-func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- dataOffset = stack.pop()
- length = stack.pop()
+ memOffset = callContext.stack.pop()
+ dataOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(contract.Input, dataOffset, length))
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+ callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
- interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil
}
-func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
+func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
-func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- dataOffset = stack.pop()
- length = stack.pop()
-
- end = interpreter.intPool.get().Add(dataOffset, length)
+ memOffset = callContext.stack.pop()
+ dataOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- defer interpreter.intPool.put(memOffset, dataOffset, length, end)
- if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
- return nil, errReturnDataOutOfBounds
+ offset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ return nil, ErrReturnDataOutOfBounds
}
- memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
-
+ // we can reuse dataOffset now (aliasing it for clarity)
+ var end = dataOffset
+ end.Add(&dataOffset, &length)
+ end64, overflow := end.Uint64WithOverflow()
+ if overflow || uint64(len(interpreter.returnData)) < end64 {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
return nil, nil
}
-func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- slot := stack.peek()
- address := common.BigToAddress(slot)
- fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
- interpreter.evm.ShardID == shard.BeaconChainShardID &&
- interpreter.evm.StateDB.IsValidator(address)
- if fixValidatorCode {
- // https://github.com/ethereum/solidity/blob/develop/Changelog.md#081-2021-01-27
- // per this link,
.code.length calls extcodesize on the address so this fix will work
- slot.SetUint64(0)
- return nil, nil
- }
- slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
-
+func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
return nil, nil
}
-func opCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- l := interpreter.intPool.get().SetInt64(int64(len(contract.Code)))
- stack.push(l)
-
+func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ l := new(uint256.Int)
+ l.SetUint64(uint64(len(callContext.contract.Code)))
+ callContext.stack.push(l)
return nil, nil
}
-func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- memOffset = stack.pop()
- codeOffset = stack.pop()
- length = stack.pop()
+ memOffset = callContext.stack.pop()
+ codeOffset = callContext.stack.pop()
+ length = callContext.stack.pop()
)
- codeCopy := getDataBig(contract.Code, codeOffset, length)
- memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
-func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- addr = common.BigToAddress(stack.pop())
+ stack = callContext.stack
+ a = stack.pop()
memOffset = stack.pop()
codeOffset = stack.pop()
length = stack.pop()
)
- var code []byte
- fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
- interpreter.evm.ShardID == shard.BeaconChainShardID &&
- interpreter.evm.StateDB.IsValidator(addr)
- if fixValidatorCode {
- // for EOAs that are not validators, statedb returns nil
- code = nil
- } else {
- code = interpreter.evm.StateDB.GetCode(addr)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
}
- codeCopy := getDataBig(code, codeOffset, length)
- memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+ addr := common.Address(a.Bytes20())
+ codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
@@ -570,182 +435,223 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
// (6) Caller tries to get the code hash for an account which is marked as deleted,
//
// this account should be regarded as a non-existent account and zero should be returned.
-func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- slot := stack.peek()
- address := common.BigToAddress(slot)
+func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ slot := callContext.stack.peek()
+ address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) {
- slot.SetUint64(0)
+ slot.Clear()
} else {
- fixValidatorCode := interpreter.evm.chainRules.IsValidatorCodeFix &&
- interpreter.evm.ShardID == shard.BeaconChainShardID &&
- interpreter.evm.StateDB.IsValidator(address)
- if fixValidatorCode {
- slot.SetBytes(emptyCodeHash.Bytes())
- } else {
- slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
- }
+ slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
return nil, nil
}
-func opGasprice(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
+func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.GasPrice)
+ callContext.stack.push(v)
return nil, nil
}
-func opBlockhash(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- num := stack.pop()
-
- n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
- stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
+func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ num := callContext.stack.peek()
+ num64, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+ var upper, lower uint64
+ upper = interpreter.evm.Context.BlockNumber.Uint64()
+ if upper < 257 {
+ lower = 0
} else {
- stack.push(interpreter.intPool.getZero())
+ lower = upper - 256
+ }
+ if num64 >= lower && num64 < upper {
+ num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
+ } else {
+ num.Clear()
}
- interpreter.intPool.put(num, n)
return nil, nil
}
-func opCoinbase(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
+func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes()))
return nil, nil
}
-func opTimestamp(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
+func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Context.Time)
+ callContext.stack.push(v)
return nil, nil
}
-func opNumber(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
+func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber)
+ callContext.stack.push(v)
return nil, nil
}
-func opDifficulty(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().Set(big.NewInt(0))))
+/*
+func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty)
+ callContext.stack.push(v)
return nil, nil
}
+*/
-func opGasLimit(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(math.U256(interpreter.intPool.get().SetUint64(interpreter.evm.GasLimit)))
+func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit))
return nil, nil
}
-func opPop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- interpreter.intPool.put(stack.pop())
+func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.pop()
return nil, nil
}
-func opMload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- v := stack.peek()
- offset := v.Int64()
- v.SetBytes(memory.GetPtr(offset, 32))
+func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ v := callContext.stack.peek()
+ offset := int64(v.Uint64())
+ v.SetBytes(callContext.memory.GetPtr(offset, 32))
return nil, nil
}
-func opMstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// pop value of the stack
- mStart, val := stack.pop(), stack.pop()
- memory.Set32(mStart.Uint64(), val)
-
- interpreter.intPool.put(mStart, val)
+ mStart, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.Set32(mStart.Uint64(), &val)
return nil, nil
}
-func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- off, val := stack.pop().Int64(), stack.pop().Int64()
- memory.store[off] = byte(val & 0xff)
-
+func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ off, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil
}
-func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- loc := stack.peek()
- val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
+func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ loc := callContext.stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash)
loc.SetBytes(val.Bytes())
return nil, nil
}
-func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- loc := common.BigToHash(stack.pop())
- val := stack.pop()
- interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
-
- interpreter.intPool.put(val)
+func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ loc := callContext.stack.pop()
+ val := callContext.stack.pop()
+ interpreter.evm.StateDB.SetState(callContext.contract.Address(),
+ loc.Bytes32(), val.Bytes32())
return nil, nil
}
-func opJump(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- pos := stack.pop()
- if !contract.validJumpdest(pos) {
- return nil, errInvalidJump
+func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ pos := callContext.stack.pop()
+ if !callContext.contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
}
*pc = pos.Uint64()
-
- interpreter.intPool.put(pos)
return nil, nil
}
-func opJumpi(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- pos, cond := stack.pop(), stack.pop()
- if cond.Sign() != 0 {
- if !contract.validJumpdest(pos) {
- return nil, errInvalidJump
+func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ pos, cond := callContext.stack.pop(), callContext.stack.pop()
+ if !cond.IsZero() {
+ if !callContext.contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
}
*pc = pos.Uint64()
} else {
*pc++
}
+ return nil, nil
+}
- interpreter.intPool.put(pos, cond)
+func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
return nil, nil
}
-func opJumpdest(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opBeginSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ return nil, ErrInvalidSubroutineEntry
+}
+
+func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if len(callContext.rstack.data) >= 1023 {
+ return nil, ErrReturnStackExceeded
+ }
+ pos := callContext.stack.pop()
+ if !pos.IsUint64() {
+ return nil, ErrInvalidJump
+ }
+ posU64 := pos.Uint64()
+ if !callContext.contract.validJumpSubdest(posU64) {
+ return nil, ErrInvalidJump
+ }
+ callContext.rstack.push(uint32(*pc))
+ *pc = posU64 + 1
return nil, nil
}
-func opPc(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(*pc))
+func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ if len(callContext.rstack.data) == 0 {
+ return nil, ErrInvalidRetsub
+ }
+ // Other than the check that the return stack is not empty, there is no
+ // need to validate the pc from 'returns', since we only ever push valid
+ //values onto it via jumpsub.
+ *pc = uint64(callContext.rstack.pop()) + 1
return nil, nil
}
-func opMsize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetInt64(int64(memory.Len())))
+func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil
}
-func opGas(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.push(interpreter.intPool.get().SetUint64(contract.Gas))
+func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
return nil, nil
}
-func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
+ return nil, nil
+}
+
+func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- value = stack.pop()
- offset, size = stack.pop(), stack.pop()
- input = memory.GetCopy(offset.Int64(), size.Int64())
- gas = contract.Gas
+ value = callContext.stack.pop()
+ offset, size = callContext.stack.pop(), callContext.stack.pop()
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = callContext.contract.Gas
)
- if interpreter.evm.ChainConfig().IsS3(interpreter.evm.EpochNumber) {
+ if interpreter.evm.ChainConfig().IsS3(interpreter.evm.Context.EpochNumber) {
gas -= gas / 64
}
+ // reuse size int for stackvalue
+ stackvalue := size
- contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(contract, input, gas, value)
+ callContext.contract.UseGas(gas)
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
+ bigVal = value.ToBig()
+ }
+
+ res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
- if interpreter.evm.ChainConfig().IsS3(interpreter.evm.EpochNumber) && suberr == ErrCodeStoreOutOfGas {
- stack.push(interpreter.intPool.getZero())
+ if interpreter.evm.ChainConfig().IsS3(interpreter.evm.Context.EpochNumber) && suberr == ErrCodeStoreOutOfGas {
+ stackvalue.Clear()
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
- stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
- contract.Gas += returnGas
- interpreter.intPool.put(value, offset, size)
+ callContext.stack.push(&stackvalue)
+ callContext.contract.Gas += returnGas
if suberr == ErrExecutionReverted {
return res, nil
@@ -753,27 +659,35 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memor
return nil, nil
}
-func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- endowment = stack.pop()
- offset, size = stack.pop(), stack.pop()
- salt = stack.pop()
- input = memory.GetCopy(offset.Int64(), size.Int64())
- gas = contract.Gas
+ endowment = callContext.stack.pop()
+ offset, size = callContext.stack.pop(), callContext.stack.pop()
+ salt = callContext.stack.pop()
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = callContext.contract.Gas
)
// Apply EIP150
gas -= gas / 64
- contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create2(contract, input, gas, endowment, salt)
+ callContext.contract.UseGas(gas)
+ // reuse size int for stackvalue
+ stackvalue := size
+ //TODO: use uint256.Int instead of converting with toBig()
+ bigEndowment := big0
+ if !endowment.IsZero() {
+ bigEndowment = endowment.ToBig()
+ }
+ res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
+ bigEndowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
- stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
- contract.Gas += returnGas
- interpreter.intPool.put(endowment, offset, size, salt)
+ callContext.stack.push(&stackvalue)
+ callContext.contract.Gas += returnGas
if suberr == ErrExecutionReverted {
return res, nil
@@ -781,151 +695,154 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
return nil, nil
}
-func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ // We can use this as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
- args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ var bigVal = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
+ if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.Call(contract, toAddr, args, gas, value)
+
+ ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal)
+
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- if contract.WithDataCopyFix {
- ret = common.CopyBytes(ret)
- }
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opCallCode(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
gas += params.CallStipend
+ bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallCode(contract, toAddr, args, gas, value)
+
+ ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- if contract.WithDataCopyFix {
- ret = common.CopyBytes(ret)
- }
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- ret, returnGas, err := interpreter.evm.DelegateCall(contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- if contract.WithDataCopyFix {
- ret = common.CopyBytes(ret)
- }
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opStaticCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.put(stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
- toAddr := common.BigToAddress(addr)
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- ret, returnGas, err := interpreter.evm.StaticCall(contract, toAddr, args, gas)
+ ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
if err != nil {
- stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
- if contract.WithDataCopyFix {
- ret = common.CopyBytes(ret)
- }
- memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
- contract.Gas += returnGas
+ callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
-func opReturn(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset, size := stack.pop(), stack.pop()
- ret := memory.GetPtr(offset.Int64(), size.Int64())
+func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.pop()
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
-func opRevert(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- offset, size := stack.pop(), stack.pop()
- ret := memory.GetPtr(offset.Int64(), size.Int64())
+func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ offset, size := callContext.stack.pop(), callContext.stack.pop()
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
-func opStop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
return nil, nil
}
-func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- balance := interpreter.evm.StateDB.GetBalance(contract.Address())
- interpreter.evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
-
- interpreter.evm.StateDB.Suicide(contract.Address())
+func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ beneficiary := callContext.stack.pop()
+ balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
+ interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
+ interpreter.evm.StateDB.Suicide(callContext.contract.Address())
return nil, nil
}
@@ -933,47 +850,48 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
// make log instruction function
func makeLog(size int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
topics := make([]common.Hash, size)
+ stack := callContext.stack
mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
- topics[i] = common.BigToHash(stack.pop())
+ addr := stack.pop()
+ topics[i] = addr.Bytes32()
}
- d := memory.GetCopy(mStart.Int64(), mSize.Int64())
- interpreter.evm.StateDB.AddLog(&types.Log{
- Address: contract.Address(),
+ d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
+ interpreter.evm.StateDB.AddLog((*coreTypes.Log)(&types.Log{
+ Address: callContext.contract.Address(),
Topics: topics,
Data: d,
// This is a non-consensus field, but assigned here because
// core/state doesn't know the current block number.
- BlockNumber: interpreter.evm.BlockNumber.Uint64(),
- })
+ BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(),
+ }))
- interpreter.intPool.put(mStart, mSize)
return nil, nil
}
}
// opPush1 is a specialized version of pushN
-func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- codeLen = uint64(len(contract.Code))
- integer = interpreter.intPool.get()
+ codeLen = uint64(len(callContext.contract.Code))
+ integer = new(uint256.Int)
)
- *pc++
+ *pc += 1
if *pc < codeLen {
- stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
+ callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
} else {
- stack.push(integer.SetUint64(0))
+ callContext.stack.push(integer.Clear())
}
return nil, nil
}
// make push instruction function
func makePush(size uint64, pushByteSize int) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- codeLen := len(contract.Code)
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ codeLen := len(callContext.contract.Code)
startMin := codeLen
if int(*pc+1) < startMin {
@@ -985,8 +903,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
endMin = startMin + pushByteSize
}
- integer := interpreter.intPool.get()
- stack.push(integer.SetBytes(common.RightPadBytes(contract.Code[startMin:endMin], pushByteSize)))
+ integer := new(uint256.Int)
+ callContext.stack.push(integer.SetBytes(common.RightPadBytes(
+ callContext.contract.Code[startMin:endMin], pushByteSize)))
*pc += size
return nil, nil
@@ -995,8 +914,8 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.dup(interpreter.intPool, int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.dup(int(size))
return nil, nil
}
}
@@ -1005,8 +924,8 @@ func makeDup(size int64) executionFunc {
func makeSwap(size int64) executionFunc {
// switch n + 1 otherwise n would be swapped with n
size++
- return func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
- stack.swap(int(size))
+ return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ callContext.stack.swap(int(size))
return nil, nil
}
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 7c03b6ccbb..05bc24ad05 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -20,16 +20,13 @@ import (
"bytes"
"encoding/json"
"fmt"
- "math/big"
- "os"
+ "io/ioutil"
"testing"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/internal/params"
+ "github.com/holiman/uint256"
)
type TwoOperandTestcase struct {
@@ -95,47 +92,29 @@ func init() {
func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
stack = newstack()
+ rstack = newReturnStack()
pc = uint64(0)
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
- 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 {
- x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
- y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
- expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
+ expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x)
stack.push(y)
- opFn(&pc, evmInterpreter, nil, nil, stack)
+ opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
+ if len(stack.data) != 1 {
+ t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
+ }
actual := stack.pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
}
- // Check pool usage
- // 1.pool is not allowed to contain anything on the stack
- // 2.pool is not allowed to contain the same pointers twice
- if evmInterpreter.intPool.pool.len() > 0 {
-
- poolvals := make(map[*big.Int]struct{})
- poolvals[actual] = struct{}{}
-
- for evmInterpreter.intPool.pool.len() > 0 {
- key := evmInterpreter.intPool.get()
- if _, exist := poolvals[key]; exist {
- t.Errorf("Testcase %v %d, pool contains double-entry", name, i)
- }
- poolvals[key] = struct{}{}
- }
- }
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func TestByteOp(t *testing.T) {
@@ -211,22 +190,59 @@ func TestSAR(t *testing.T) {
testTwoOperandOp(t, tests, opSAR, "sar")
}
+func TestAddMod(t *testing.T) {
+ var (
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack = newstack()
+ evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
+ pc = uint64(0)
+ )
+ tests := []struct {
+ x string
+ y string
+ z string
+ expected string
+ }{
+ {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ },
+ }
+ // x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+ // in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+
+ for i, test := range tests {
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y))
+ z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z))
+ expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected))
+ stack.push(z)
+ stack.push(y)
+ stack.push(x)
+ opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil})
+ actual := stack.pop()
+ if actual.Cmp(expected) != 0 {
+ t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
+ }
+ }
+}
+
// getResult is a convenience function to generate the expected values
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
- pc = uint64(0)
- interpreter = env.interpreter.(*EVMInterpreter)
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, rstack = newstack(), newReturnStack()
+ 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))
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x)
stack.push(y)
- opFn(&pc, interpreter, nil, nil, stack)
+ opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
actual := stack.pop()
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
}
@@ -243,7 +259,7 @@ func TestWriteExpectedValues(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- _ = os.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0644)
+ _ = ioutil.WriteFile(fmt.Sprintf("testdata/testcases_%v.json", name), data, 0644)
if err != nil {
t.Fatal(err)
}
@@ -253,7 +269,7 @@ func TestWriteExpectedValues(t *testing.T) {
// TestJsonTestcases runs through all the testcases defined as json-files
func TestJsonTestcases(t *testing.T) {
for name := range twoOpMethods {
- data, err := os.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name))
+ data, err := ioutil.ReadFile(fmt.Sprintf("testdata/testcases_%v.json", name))
if err != nil {
t.Fatal("Failed to read file", err)
}
@@ -263,15 +279,14 @@ func TestJsonTestcases(t *testing.T) {
}
}
-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 executionFunc, args ...string) {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, rstack = newstack(), newReturnStack()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
// convert args
byteArgs := make([][]byte, len(args))
for i, arg := range args {
@@ -281,13 +296,13 @@ func opBenchmark(bench *testing.B, op func(pc *uint64, interpreter *EVMInterpret
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
for _, arg := range byteArgs {
- a := new(big.Int).SetBytes(arg)
+ a := new(uint256.Int)
+ a.SetBytes(arg)
stack.push(a)
}
- op(&pc, evmInterpreter, nil, nil, stack)
+ op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
stack.pop()
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpAdd64(b *testing.B) {
@@ -500,72 +515,66 @@ func BenchmarkOpIsZero(b *testing.B) {
func TestOpMstore(t *testing.T) {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, rstack = newstack(), newReturnStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
- stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
- opMstore(&pc, evmInterpreter, nil, mem, stack)
+ stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
+ opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
- stack.pushN(big.NewInt(0x1), big.NewInt(0))
- opMstore(&pc, evmInterpreter, nil, mem, stack)
+ stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
+ opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpMstore(bench *testing.B) {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, rstack = newstack(), newReturnStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
- memStart := big.NewInt(0)
- value := big.NewInt(0x1337)
+ memStart := new(uint256.Int)
+ value := new(uint256.Int).SetUint64(0x1337)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.pushN(value, memStart)
- opMstore(&pc, evmInterpreter, nil, mem, stack)
+ stack.pushN(*value, *memStart)
+ opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpSHA3(bench *testing.B) {
var (
- env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
- stack = newstack()
+ env = NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
+ stack, rstack = newstack(), newReturnStack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(32)
pc := uint64(0)
- start := big.NewInt(0)
+ start := uint256.NewInt(0)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.pushN(big.NewInt(32), start)
- opSha3(&pc, evmInterpreter, nil, mem, stack)
+ stack.pushN(*uint256.NewInt(0).SetUint64(32), *start)
+ opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func TestCreate2Addreses(t *testing.T) {
@@ -639,52 +648,5 @@ func TestCreate2Addreses(t *testing.T) {
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
}
-
- }
-}
-
-func TestOpTstoreAndTload(t *testing.T) {
- // initialize context, evm, statedb, etc.
- var (
- stateDB, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- env = NewEVM(Context{}, stateDB, params.TestChainConfig, Config{})
- stack = newstack()
- memory = NewMemory()
- evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
- caller = common.Address{}
- to = common.Address{1}
- contractRef = vm.AccountRef(caller)
- contract = NewContract(contractRef, vm.AccountRef(to), new(big.Int), 0)
- value = common.Hex2Bytes("12345")
- )
-
- stateDB.CreateAccount(caller)
- stateDB.CreateAccount(to)
- env.interpreter = evmInterpreter
- pc := uint64(0)
-
- // push to value then location to the stack
- stack.push(new(big.Int).SetBytes(value))
- stack.push(new(big.Int))
-
- // call tstore and ensure stack length is 0
- opTstore(&pc, evmInterpreter, contract, memory, stack)
- if stack.len() != 0 {
- t.Fatal("stack should be empty")
- }
-
- // push location to the stack
- stack.push(new(big.Int))
-
- // call tload and ensure stack length is 1
- opTload(&pc, evmInterpreter, contract, memory, stack)
- if stack.len() != 1 {
- t.Fatal("stack should have a single element")
- }
-
- // ensure the value read is same as the original value
- val := stack.peek()
- if !bytes.Equal(value, val.Bytes()) {
- t.Fatalf("wrong value loaded to the stack")
}
}
diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go
deleted file mode 100644
index 72d55ff671..0000000000
--- a/core/vm/int_pool_verifier.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 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 .
-
-//go:build VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-import "fmt"
-
-const verifyPool = true
-
-func verifyIntegerPool(ip *intPool) {
- for i, item := range ip.pool.data {
- if item.Cmp(checkVal) != 0 {
- panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i))
- }
- }
-}
diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go
deleted file mode 100644
index 6cbe6cafe6..0000000000
--- a/core/vm/int_pool_verifier_empty.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 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 .
-
-//go:build !VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-const verifyPool = false
-
-func verifyIntegerPool(ip *intPool) {}
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 4e4b2073c0..e2fe76907d 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -19,10 +19,9 @@ package vm
import (
"math/big"
- "github.com/harmony-one/harmony/numeric"
-
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/types"
+ "github.com/harmony-one/harmony/numeric"
staking "github.com/harmony-one/harmony/staking/types"
)
@@ -59,8 +58,8 @@ type StateDB interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
- GetTransientState(addr common.Address, key common.Hash) common.Hash
- SetTransientState(addr common.Address, key, value common.Hash)
+ //GetTransientState(addr common.Address, key common.Hash) common.Hash
+ //SetTransientState(addr common.Address, key, value common.Hash)
Suicide(common.Address) bool
HasSuicided(common.Address) bool
@@ -72,6 +71,15 @@ type StateDB interface {
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
+ AddressInAccessList(addr common.Address) bool
+ SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
+ // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
+ // even if the feature/fork is not active yet
+ AddAddressToAccessList(addr common.Address)
+ // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
+ // even if the feature/fork is not active yet
+ AddSlotToAccessList(addr common.Address, slot common.Hash)
+
Prepare()
RevertToSnapshot(int)
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 3b8df026f8..299c58bb14 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -17,39 +17,27 @@
package vm
import (
- "fmt"
"hash"
"sync/atomic"
- "github.com/harmony-one/harmony/internal/utils"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/harmony-one/harmony/internal/utils"
)
// Config are the configuration options for the Interpreter
type Config struct {
- // Debug enabled debugging Interpreter options
- Debug bool
- // Tracer is the op code logger
- Tracer Tracer
- // NoRecursion disabled Interpreter call, callcode,
- // delegate call and create.
- NoRecursion bool
- // Enable recording of SHA3/keccak preimages
- EnablePreimageRecording bool
- // JumpTable contains the EVM instruction table. This
- // may be left uninitialised and will be set to the default
- // table.
- JumpTable [256]operation
+ Debug bool // Enables debugging
+ Tracer Tracer // Opcode logger
+ NoRecursion bool // Disables call, callcode, delegate call and create
+ EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
- // Type of the EWASM interpreter
- EWASMInterpreter string
- // Type of the EVM interpreter
- EVMInterpreter string
+ JumpTable [256]*operation // EVM instruction table, automatically populated if unset
- // ExtraEips the additional EIPS that are to be enabled
- ExtraEips []int
+ EWASMInterpreter string // External EWASM interpreter options
+ EVMInterpreter string // External EVM interpreter options
+
+ ExtraEips []int // Additional EIPS that are to be enabled
}
// Interpreter is used to run Ethereum based contracts and will utilise the
@@ -74,6 +62,15 @@ type Interpreter interface {
CanRun([]byte) bool
}
+// callCtx contains the things that are per-call, such as stack and memory,
+// but not transients like pc and gas
+type callCtx struct {
+ memory *Memory
+ stack *Stack
+ rstack *ReturnStack
+ contract *Contract
+}
+
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
// Read to get a variable amount of data from the hash state. Read is faster than Sum
// because it doesn't copy the internal state, but also modifies the internal state.
@@ -87,8 +84,6 @@ type EVMInterpreter struct {
evm *EVM
cfg Config
- intPool *intPool
-
hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
@@ -101,7 +96,7 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
// We use the STOP instruction whether to see
// the jump table was initialised. If it was not
// we'll set the default jump table.
- if !cfg.JumpTable[STOP].valid {
+ if cfg.JumpTable[STOP] == nil {
var jt JumpTable
switch {
case evm.chainRules.IsIstanbul:
@@ -132,15 +127,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
//
// It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for
-// errExecutionReverted which means revert-and-keep-gas-left.
+// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
- if in.intPool == nil {
- in.intPool = poolOfIntPools.get()
- defer func() {
- poolOfIntPools.put(in.intPool)
- in.intPool = nil
- }()
- }
// Increment the call depth which is restricted to 1024
in.evm.depth++
@@ -163,9 +151,16 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
}
var (
- op OpCode // current opcode
- mem = NewMemory() // bound memory
- stack = newstack() // local stack
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = newstack() // local stack
+ returns = newReturnStack() // local returns stack
+ callContext = &callCtx{
+ memory: mem,
+ stack: stack,
+ rstack: returns,
+ contract: contract,
+ }
// For optimisation reason we're using uint64 as the program counter.
// It's theoretically possible to go above 2^64. The YP defines the PC
// to be uint256. Practically much less so feasible.
@@ -177,18 +172,22 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
logged bool // deferred Tracer should ignore already logged steps
res []byte // result of the opcode execution function
)
+ // Don't move this deferrred function, it's placed before the capturestate-deferred method,
+ // so that it get's executed _after_: the capturestate needs the stacks before
+ // they are returned to the pools
+ defer func() {
+ returnStack(stack)
+ returnRStack(returns)
+ }()
contract.Input = input
- // Reclaim the stack as an int pool when the execution stops
- defer func() { in.intPool.put(stack.data...) }()
-
if in.cfg.Debug {
defer func() {
if err != nil {
if !logged {
- in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
} else {
- in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
}
}
}()
@@ -197,7 +196,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
// the execution of one of the operations or until the done flag is set by the
// parent context.
- for atomic.LoadInt32(&in.evm.abort) == 0 {
+ steps := 0
+ for {
+ steps++
+ if steps%1000 == 0 && atomic.LoadInt32(&in.evm.abort) != 0 {
+ break
+ }
if in.cfg.Debug {
// Capture pre-execution values for tracing.
logged, pcCopy, gasCopy = false, pc, contract.Gas
@@ -207,14 +211,14 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
- if !operation.valid {
- return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
+ if operation == nil {
+ return nil, &ErrInvalidOpCode{opcode: op}
}
// Validate stack
if sLen := stack.len(); sLen < operation.minStack {
- return nil, fmt.Errorf("stack underflow (%d <=> %d)", sLen, operation.minStack)
+ return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
} else if sLen > operation.maxStack {
- return nil, fmt.Errorf("stack limit reached %d (%d)", sLen, operation.maxStack)
+ return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
// If the operation is valid, enforce and write restrictions
if in.readOnly && in.evm.chainRules.IsS3 {
@@ -224,7 +228,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// account to the others means the state is modified and should also
// return with an error.
if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
- return nil, errWriteProtection
+ return nil, ErrWriteProtection
}
}
// Static portion of gas
@@ -241,12 +245,12 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
if operation.memorySize != nil {
memSize, overflow := operation.memorySize(stack)
if overflow {
- return nil, errGasUintOverflow
+ return nil, ErrGasUintOverflow
}
// memory is expanded in words of 32 bytes. Gas
// is also calculated in words.
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
- return nil, errGasUintOverflow
+ return nil, ErrGasUintOverflow
}
}
// Dynamic portion of gas
@@ -264,29 +268,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
mem.Resize(memorySize)
}
- var afterHook HookAfter
if in.cfg.Debug {
- afterHook, _ = in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+ in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
logged = true
}
// execute the operation
- res, err = operation.execute(&pc, in, contract, mem, stack)
-
- // record the after executed operation content
- if afterHook != nil {
- afterHook(mem, stack)
- }
-
- // verifyPool is a build flag. Pool verification makes sure the integrity
- // of the integer pool by comparing values to a default value.
- if verifyPool {
- verifyIntegerPool(in.intPool)
- }
+ res, err = operation.execute(&pc, in, callContext)
// if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation.
if operation.returns {
- in.returnData = res
+ in.returnData = common.CopyBytes(res)
}
switch {
diff --git a/core/vm/intpool.go b/core/vm/intpool.go
deleted file mode 100644
index 917a78d560..0000000000
--- a/core/vm/intpool.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2017 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 .
-
-package vm
-
-import (
- "math/big"
- "sync"
-)
-
-var checkVal = big.NewInt(-42)
-
-const poolLimit = 256
-
-// intPool is a pool of big integers that
-// can be reused for all big.Int operations.
-type intPool struct {
- pool *Stack
-}
-
-func newIntPool() *intPool {
- return &intPool{pool: newstack()}
-}
-
-// get retrieves a big int from the pool, allocating one if the pool is empty.
-// Note, the returned int's value is arbitrary and will not be zeroed!
-func (p *intPool) get() *big.Int {
- if p.pool.len() > 0 {
- return p.pool.pop()
- }
- return new(big.Int)
-}
-
-// getZero retrieves a big int from the pool, setting it to zero or allocating
-// a new one if the pool is empty.
-func (p *intPool) getZero() *big.Int {
- if p.pool.len() > 0 {
- return p.pool.pop().SetUint64(0)
- }
- return new(big.Int)
-}
-
-// put returns an allocated big int to the pool to be later reused by get calls.
-// Note, the values as saved as is; neither put nor get zeroes the ints out!
-func (p *intPool) put(is ...*big.Int) {
- if len(p.pool.data) > poolLimit {
- return
- }
- for _, i := range is {
- // verifyPool is a build flag. Pool verification makes sure the integrity
- // of the integer pool by comparing values to a default value.
- if verifyPool {
- i.Set(checkVal)
- }
- p.pool.push(i)
- }
-}
-
-// The intPool pool's default capacity
-const poolDefaultCap = 25
-
-// intPoolPool manages a pool of intPools.
-type intPoolPool struct {
- pools []*intPool
- lock sync.Mutex
-}
-
-var poolOfIntPools = &intPoolPool{
- pools: make([]*intPool, 0, poolDefaultCap),
-}
-
-// get is looking for an available pool to return.
-func (ipp *intPoolPool) get() *intPool {
- ipp.lock.Lock()
- defer ipp.lock.Unlock()
-
- if len(poolOfIntPools.pools) > 0 {
- ip := ipp.pools[len(ipp.pools)-1]
- ipp.pools = ipp.pools[:len(ipp.pools)-1]
- return ip
- }
- return newIntPool()
-}
-
-// put a pool that has been allocated with get.
-func (ipp *intPoolPool) put(ip *intPool) {
- ipp.lock.Lock()
- defer ipp.lock.Unlock()
-
- if len(ipp.pools) < cap(ipp.pools) {
- ipp.pools = append(ipp.pools, ip)
- }
-}
diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go
deleted file mode 100644
index 6c0d00f3ce..0000000000
--- a/core/vm/intpool_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 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 .
-
-package vm
-
-import (
- "testing"
-)
-
-func TestIntPoolPoolGet(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
-
- nip := poolOfIntPools.get()
- if nip == nil {
- t.Fatalf("Invalid pool allocation")
- }
-}
-
-func TestIntPoolPoolPut(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
-
- nip := poolOfIntPools.get()
- if len(poolOfIntPools.pools) != 0 {
- t.Fatalf("Pool got added to list when none should have been")
- }
-
- poolOfIntPools.put(nip)
- if len(poolOfIntPools.pools) == 0 {
- t.Fatalf("Pool did not get added to list when one should have been")
- }
-}
-
-func TestIntPoolPoolReUse(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
- nip := poolOfIntPools.get()
- poolOfIntPools.put(nip)
- poolOfIntPools.get()
-
- if len(poolOfIntPools.pools) != 0 {
- t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
- }
-}
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 1a5af77f11..302d43c6db 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -18,12 +18,13 @@ package vm
import (
"errors"
+ "fmt"
"github.com/harmony-one/harmony/internal/params"
)
type (
- executionFunc func(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
+ executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
memorySizeFunc func(*Stack) (size uint64, overflow bool)
@@ -48,7 +49,6 @@ type operation struct {
halts bool // indicates whether the operation should halt further execution
jumps bool // indicates whether the program counter should not increment
writes bool // determines whether this a state modifying operation
- valid bool // indication whether the retrieved operation is valid and known
reverts bool // determines whether the operation reverts state (implicitly halts)
returns bool // determines whether the operations sets the return data content
}
@@ -64,7 +64,42 @@ var (
)
// JumpTable contains the EVM opcodes supported at a given fork.
-type JumpTable [256]operation
+type JumpTable [256]*operation
+
+func validate(jt JumpTable) JumpTable {
+ for i, op := range jt {
+ if op == nil {
+ panic(fmt.Sprintf("op %#x is not set", i))
+ }
+ // The interpreter has an assumption that if the memorySize function is
+ // set, then the dynamicGas function is also set. This is a somewhat
+ // arbitrary assumption, and can be removed if we need to -- but it
+ // allows us to avoid a condition check. As long as we have that assumption
+ // in there, this little sanity check prevents us from merging in a
+ // change which violates it.
+ if op.memorySize != nil && op.dynamicGas == nil {
+ panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
+ }
+ }
+ return jt
+}
+
+// newLondonInstructionSet returns the frontier, homestead, byzantium,
+// contantinople, istanbul, petersburg, berlin and london instructions.
+func newLondonInstructionSet() JumpTable {
+ instructionSet := newBerlinInstructionSet()
+ //enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529 // wee d
+ enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
+ return instructionSet
+}
+
+// newBerlinInstructionSet returns the frontier, homestead, byzantium,
+// contantinople, istanbul, petersburg and berlin instructions.
+func newBerlinInstructionSet() JumpTable {
+ instructionSet := newIstanbulInstructionSet()
+ enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
+ return validate(instructionSet)
+}
// newIstanbulInstructionSet returns the frontier, homestead
// byzantium, contantinople and petersburg instructions.
@@ -82,42 +117,37 @@ func newIstanbulInstructionSet() JumpTable {
// byzantium and contantinople instructions.
func newConstantinopleInstructionSet() JumpTable {
instructionSet := newByzantiumInstructionSet()
- instructionSet[SHL] = operation{
+ instructionSet[SHL] = &operation{
execute: opSHL,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SHR] = operation{
+ instructionSet[SHR] = &operation{
execute: opSHR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[SAR] = operation{
+ instructionSet[SAR] = &operation{
execute: opSAR,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
}
- instructionSet[EXTCODEHASH] = operation{
+ instructionSet[EXTCODEHASH] = &operation{
execute: opExtCodeHash,
constantGas: params.ExtcodeHashGasConstantinople,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
}
- instructionSet[CREATE2] = operation{
+ instructionSet[CREATE2] = &operation{
execute: opCreate2,
constantGas: params.Create2Gas,
dynamicGas: gasCreate2,
minStack: minStack(4, 1),
maxStack: maxStack(4, 1),
memorySize: memoryCreate2,
- valid: true,
writes: true,
returns: true,
}
@@ -128,39 +158,35 @@ func newConstantinopleInstructionSet() JumpTable {
// byzantium instructions.
func newByzantiumInstructionSet() JumpTable {
instructionSet := newSpuriousDragonInstructionSet()
- instructionSet[STATICCALL] = operation{
+ instructionSet[STATICCALL] = &operation{
execute: opStaticCall,
constantGas: params.CallGasEIP150,
dynamicGas: gasStaticCall,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryStaticCall,
- valid: true,
returns: true,
}
- instructionSet[RETURNDATASIZE] = operation{
+ instructionSet[RETURNDATASIZE] = &operation{
execute: opReturnDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
}
- instructionSet[RETURNDATACOPY] = operation{
+ instructionSet[RETURNDATACOPY] = &operation{
execute: opReturnDataCopy,
constantGas: GasFastestStep,
dynamicGas: gasReturnDataCopy,
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryReturnDataCopy,
- valid: true,
}
- instructionSet[REVERT] = operation{
+ instructionSet[REVERT] = &operation{
execute: opRevert,
dynamicGas: gasRevert,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryRevert,
- valid: true,
reverts: true,
returns: true,
}
@@ -192,14 +218,13 @@ func newTangerineWhistleInstructionSet() JumpTable {
// instructions that can be executed during the homestead phase.
func newHomesteadInstructionSet() JumpTable {
instructionSet := newFrontierInstructionSet()
- instructionSet[DELEGATECALL] = operation{
+ instructionSet[DELEGATECALL] = &operation{
execute: opDelegateCall,
dynamicGas: gasDelegateCall,
constantGas: params.CallGasFrontier,
minStack: minStack(6, 1),
maxStack: maxStack(6, 1),
memorySize: memoryDelegateCall,
- valid: true,
returns: true,
}
return instructionSet
@@ -215,161 +240,138 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
halts: true,
- valid: true,
},
ADD: {
execute: opAdd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MUL: {
execute: opMul,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SUB: {
execute: opSub,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
DIV: {
execute: opDiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SDIV: {
execute: opSdiv,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
MOD: {
execute: opMod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SMOD: {
execute: opSmod,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ADDMOD: {
execute: opAddmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
MULMOD: {
execute: opMulmod,
constantGas: GasMidStep,
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
- valid: true,
},
EXP: {
execute: opExp,
dynamicGas: gasExpFrontier,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SIGNEXTEND: {
execute: opSignExtend,
constantGas: GasFastStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
LT: {
execute: opLt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
GT: {
execute: opGt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SLT: {
execute: opSlt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SGT: {
execute: opSgt,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
EQ: {
execute: opEq,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
ISZERO: {
execute: opIszero,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
AND: {
execute: opAnd,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
XOR: {
execute: opXor,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
OR: {
execute: opOr,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
NOT: {
execute: opNot,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
BYTE: {
execute: opByte,
constantGas: GasFastestStep,
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
- valid: true,
},
SHA3: {
execute: opSha3,
@@ -378,56 +380,48 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 1),
maxStack: maxStack(2, 1),
memorySize: memorySha3,
- valid: true,
},
ADDRESS: {
execute: opAddress,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
BALANCE: {
execute: opBalance,
constantGas: params.BalanceGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
ORIGIN: {
execute: opOrigin,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLER: {
execute: opCaller,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLVALUE: {
execute: opCallValue,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATALOAD: {
execute: opCallDataLoad,
constantGas: GasFastestStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
CALLDATASIZE: {
execute: opCallDataSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CALLDATACOPY: {
execute: opCallDataCopy,
@@ -436,14 +430,12 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCallDataCopy,
- valid: true,
},
CODESIZE: {
execute: opCodeSize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
CODECOPY: {
execute: opCodeCopy,
@@ -452,21 +444,18 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryCodeCopy,
- valid: true,
},
GASPRICE: {
execute: opGasprice,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
EXTCODESIZE: {
execute: opExtCodeSize,
constantGas: params.ExtcodeSizeGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
EXTCODECOPY: {
execute: opExtCodeCopy,
@@ -475,56 +464,49 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryExtCodeCopy,
- valid: true,
},
BLOCKHASH: {
execute: opBlockhash,
constantGas: GasExtStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
COINBASE: {
execute: opCoinbase,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
TIMESTAMP: {
execute: opTimestamp,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
NUMBER: {
execute: opNumber,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
- DIFFICULTY: {
+ /*DIFFICULTY: {
execute: opDifficulty,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
- },
+
+ },*/
GASLIMIT: {
execute: opGasLimit,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
POP: {
execute: opPop,
constantGas: GasQuickStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
- valid: true,
},
MLOAD: {
execute: opMload,
@@ -533,7 +515,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
memorySize: memoryMLoad,
- valid: true,
},
MSTORE: {
execute: opMstore,
@@ -542,7 +523,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryMStore,
- valid: true,
},
MSTORE8: {
execute: opMstore8,
@@ -551,22 +531,18 @@ func newFrontierInstructionSet() JumpTable {
memorySize: memoryMStore8,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
-
- valid: true,
},
SLOAD: {
execute: opSload,
constantGas: params.SloadGasFrontier,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
- valid: true,
},
SSTORE: {
execute: opSstore,
dynamicGas: gasSStore,
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
- valid: true,
writes: true,
},
JUMP: {
@@ -575,7 +551,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
jumps: true,
- valid: true,
},
JUMPI: {
execute: opJumpi,
@@ -583,483 +558,414 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
jumps: true,
- valid: true,
},
PC: {
execute: opPc,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
MSIZE: {
execute: opMsize,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
GAS: {
execute: opGas,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
JUMPDEST: {
execute: opJumpdest,
constantGas: params.JumpdestGas,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
- valid: true,
},
PUSH1: {
execute: opPush1,
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH2: {
execute: makePush(2, 2),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH3: {
execute: makePush(3, 3),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH4: {
execute: makePush(4, 4),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH5: {
execute: makePush(5, 5),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH6: {
execute: makePush(6, 6),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH7: {
execute: makePush(7, 7),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH8: {
execute: makePush(8, 8),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH9: {
execute: makePush(9, 9),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH10: {
execute: makePush(10, 10),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH11: {
execute: makePush(11, 11),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH12: {
execute: makePush(12, 12),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH13: {
execute: makePush(13, 13),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH14: {
execute: makePush(14, 14),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH15: {
execute: makePush(15, 15),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH16: {
execute: makePush(16, 16),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH17: {
execute: makePush(17, 17),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH18: {
execute: makePush(18, 18),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH19: {
execute: makePush(19, 19),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH20: {
execute: makePush(20, 20),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH21: {
execute: makePush(21, 21),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH22: {
execute: makePush(22, 22),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH23: {
execute: makePush(23, 23),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH24: {
execute: makePush(24, 24),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH25: {
execute: makePush(25, 25),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH26: {
execute: makePush(26, 26),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH27: {
execute: makePush(27, 27),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH28: {
execute: makePush(28, 28),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH29: {
execute: makePush(29, 29),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH30: {
execute: makePush(30, 30),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH31: {
execute: makePush(31, 31),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
PUSH32: {
execute: makePush(32, 32),
constantGas: GasFastestStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
- valid: true,
},
DUP1: {
execute: makeDup(1),
constantGas: GasFastestStep,
minStack: minDupStack(1),
maxStack: maxDupStack(1),
- valid: true,
},
DUP2: {
execute: makeDup(2),
constantGas: GasFastestStep,
minStack: minDupStack(2),
maxStack: maxDupStack(2),
- valid: true,
},
DUP3: {
execute: makeDup(3),
constantGas: GasFastestStep,
minStack: minDupStack(3),
maxStack: maxDupStack(3),
- valid: true,
},
DUP4: {
execute: makeDup(4),
constantGas: GasFastestStep,
minStack: minDupStack(4),
maxStack: maxDupStack(4),
- valid: true,
},
DUP5: {
execute: makeDup(5),
constantGas: GasFastestStep,
minStack: minDupStack(5),
maxStack: maxDupStack(5),
- valid: true,
},
DUP6: {
execute: makeDup(6),
constantGas: GasFastestStep,
minStack: minDupStack(6),
maxStack: maxDupStack(6),
- valid: true,
},
DUP7: {
execute: makeDup(7),
constantGas: GasFastestStep,
minStack: minDupStack(7),
maxStack: maxDupStack(7),
- valid: true,
},
DUP8: {
execute: makeDup(8),
constantGas: GasFastestStep,
minStack: minDupStack(8),
maxStack: maxDupStack(8),
- valid: true,
},
DUP9: {
execute: makeDup(9),
constantGas: GasFastestStep,
minStack: minDupStack(9),
maxStack: maxDupStack(9),
- valid: true,
},
DUP10: {
execute: makeDup(10),
constantGas: GasFastestStep,
minStack: minDupStack(10),
maxStack: maxDupStack(10),
- valid: true,
},
DUP11: {
execute: makeDup(11),
constantGas: GasFastestStep,
minStack: minDupStack(11),
maxStack: maxDupStack(11),
- valid: true,
},
DUP12: {
execute: makeDup(12),
constantGas: GasFastestStep,
minStack: minDupStack(12),
maxStack: maxDupStack(12),
- valid: true,
},
DUP13: {
execute: makeDup(13),
constantGas: GasFastestStep,
minStack: minDupStack(13),
maxStack: maxDupStack(13),
- valid: true,
},
DUP14: {
execute: makeDup(14),
constantGas: GasFastestStep,
minStack: minDupStack(14),
maxStack: maxDupStack(14),
- valid: true,
},
DUP15: {
execute: makeDup(15),
constantGas: GasFastestStep,
minStack: minDupStack(15),
maxStack: maxDupStack(15),
- valid: true,
},
DUP16: {
execute: makeDup(16),
constantGas: GasFastestStep,
minStack: minDupStack(16),
maxStack: maxDupStack(16),
- valid: true,
},
SWAP1: {
execute: makeSwap(1),
constantGas: GasFastestStep,
minStack: minSwapStack(2),
maxStack: maxSwapStack(2),
- valid: true,
},
SWAP2: {
execute: makeSwap(2),
constantGas: GasFastestStep,
minStack: minSwapStack(3),
maxStack: maxSwapStack(3),
- valid: true,
},
SWAP3: {
execute: makeSwap(3),
constantGas: GasFastestStep,
minStack: minSwapStack(4),
maxStack: maxSwapStack(4),
- valid: true,
},
SWAP4: {
execute: makeSwap(4),
constantGas: GasFastestStep,
minStack: minSwapStack(5),
maxStack: maxSwapStack(5),
- valid: true,
},
SWAP5: {
execute: makeSwap(5),
constantGas: GasFastestStep,
minStack: minSwapStack(6),
maxStack: maxSwapStack(6),
- valid: true,
},
SWAP6: {
execute: makeSwap(6),
constantGas: GasFastestStep,
minStack: minSwapStack(7),
maxStack: maxSwapStack(7),
- valid: true,
},
SWAP7: {
execute: makeSwap(7),
constantGas: GasFastestStep,
minStack: minSwapStack(8),
maxStack: maxSwapStack(8),
- valid: true,
},
SWAP8: {
execute: makeSwap(8),
constantGas: GasFastestStep,
minStack: minSwapStack(9),
maxStack: maxSwapStack(9),
- valid: true,
},
SWAP9: {
execute: makeSwap(9),
constantGas: GasFastestStep,
minStack: minSwapStack(10),
maxStack: maxSwapStack(10),
- valid: true,
},
SWAP10: {
execute: makeSwap(10),
constantGas: GasFastestStep,
minStack: minSwapStack(11),
maxStack: maxSwapStack(11),
- valid: true,
},
SWAP11: {
execute: makeSwap(11),
constantGas: GasFastestStep,
minStack: minSwapStack(12),
maxStack: maxSwapStack(12),
- valid: true,
},
SWAP12: {
execute: makeSwap(12),
constantGas: GasFastestStep,
minStack: minSwapStack(13),
maxStack: maxSwapStack(13),
- valid: true,
},
SWAP13: {
execute: makeSwap(13),
constantGas: GasFastestStep,
minStack: minSwapStack(14),
maxStack: maxSwapStack(14),
- valid: true,
},
SWAP14: {
execute: makeSwap(14),
constantGas: GasFastestStep,
minStack: minSwapStack(15),
maxStack: maxSwapStack(15),
- valid: true,
},
SWAP15: {
execute: makeSwap(15),
constantGas: GasFastestStep,
minStack: minSwapStack(16),
maxStack: maxSwapStack(16),
- valid: true,
},
SWAP16: {
execute: makeSwap(16),
constantGas: GasFastestStep,
minStack: minSwapStack(17),
maxStack: maxSwapStack(17),
- valid: true,
},
LOG0: {
execute: makeLog(0),
@@ -1067,7 +973,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(2, 0),
maxStack: maxStack(2, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG1: {
@@ -1076,7 +981,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 0),
maxStack: maxStack(3, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG2: {
@@ -1085,7 +989,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(4, 0),
maxStack: maxStack(4, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG3: {
@@ -1094,7 +997,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(5, 0),
maxStack: maxStack(5, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
LOG4: {
@@ -1103,7 +1005,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(6, 0),
maxStack: maxStack(6, 0),
memorySize: memoryLog,
- valid: true,
writes: true,
},
CREATE: {
@@ -1113,7 +1014,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(3, 1),
maxStack: maxStack(3, 1),
memorySize: memoryCreate,
- valid: true,
writes: true,
returns: true,
},
@@ -1124,7 +1024,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
returns: true,
},
CALLCODE: {
@@ -1134,7 +1033,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,
- valid: true,
returns: true,
},
RETURN: {
@@ -1144,7 +1042,6 @@ func newFrontierInstructionSet() JumpTable {
maxStack: maxStack(2, 0),
memorySize: memoryReturn,
halts: true,
- valid: true,
},
SELFDESTRUCT: {
execute: opSuicide,
@@ -1152,7 +1049,6 @@ func newFrontierInstructionSet() JumpTable {
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
halts: true,
- valid: true,
writes: true,
},
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index e3ecd9e748..d0ed716276 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -18,17 +18,22 @@ package vm
import (
"encoding/hex"
+ "errors"
"fmt"
"io"
"math/big"
+ "strings"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
)
+var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
+
// Storage represents a contract's storage.
type Storage map[common.Hash]common.Hash
@@ -38,20 +43,19 @@ func (s Storage) Copy() Storage {
for key, value := range s {
cpy[key] = value
}
-
return cpy
}
-type LogFilter func(pc uint64, op OpCode) bool
-
// LogConfig are the configuration options for structured logger the EVM
type LogConfig struct {
- DisableMemory bool // disable memory capture
- DisableStack bool // disable stack capture
- DisableStorage bool // disable storage capture
- Debug bool // print output during capture end
- Limit int // maximum length of output, but zero means unlimited
- LogFilter LogFilter
+ DisableMemory bool // disable memory capture
+ DisableStack bool // disable stack capture
+ DisableStorage bool // disable storage capture
+ DisableReturnData bool // disable return data capture
+ Debug bool // print output during capture end
+ Limit int // maximum length of output, but zero means unlimited
+ // Chain overrides, can be used to execute a trace using future fork rules
+ Overrides *params.ChainConfig `json:"overrides,omitempty"`
}
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
@@ -68,6 +72,8 @@ type StructLog struct {
Memory []byte `json:"memory"`
MemorySize int `json:"memSize"`
Stack []*big.Int `json:"stack"`
+ ReturnStack []uint32 `json:"returnStack"`
+ ReturnData []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
@@ -80,9 +86,11 @@ type StructLog struct {
// overrides for gencodec
type structLogMarshaling struct {
Stack []*math.HexOrDecimal256
+ ReturnStack []math.HexOrDecimal64
Gas math.HexOrDecimal64
GasCost math.HexOrDecimal64
Memory hexutil.Bytes
+ ReturnData hexutil.Bytes
OpName string `json:"opName"` // adds call to OpName() in MarshalJSON
ErrorString string `json:"error"` // adds call to ErrorString() in MarshalJSON
}
@@ -100,8 +108,6 @@ func (s *StructLog) ErrorString() string {
return ""
}
-type HookAfter = func(memory *Memory, stack *Stack)
-
// Tracer is used to collect execution traces from an EVM transaction
// execution. CaptureState is called for each step of the VM with the
// current VM state.
@@ -109,8 +115,8 @@ type HookAfter = func(memory *Memory, stack *Stack)
// if you need to retain them beyond the current call.
type Tracer interface {
CaptureStart(env *EVM, 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) (HookAfter, error)
- CaptureFault(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, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error
+ CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
}
@@ -122,16 +128,16 @@ type Tracer interface {
type StructLogger struct {
cfg LogConfig
- logs []*StructLog
- changedValues map[common.Address]Storage
- output []byte
- err error
+ storage map[common.Address]Storage
+ logs []StructLog
+ output []byte
+ err error
}
// NewStructLogger returns a new logger
func NewStructLogger(cfg *LogConfig) *StructLogger {
logger := &StructLogger{
- changedValues: make(map[common.Address]Storage),
+ storage: make(map[common.Address]Storage),
}
if cfg != nil {
logger.cfg = *cfg
@@ -140,39 +146,17 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
-func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
+func (l *StructLogger) CaptureStart(_ *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
return nil
}
// CaptureState logs a new structured log message and pushes it out to the environment
//
-// CaptureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) (HookAfter, error) {
+// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
+func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
- return nil, ErrTraceLimitReached
- }
-
- if l.cfg.LogFilter != nil {
- if !l.cfg.LogFilter(pc, op) {
- return nil, nil
- }
- }
-
- // initialise new changed values storage container for this contract
- // if not present.
- if l.changedValues[contract.Address()] == nil {
- l.changedValues[contract.Address()] = make(Storage)
- }
-
- // capture SSTORE opcodes and determine the changed value and store
- // it in the local storage container.
- if op == SSTORE && stack.len() >= 2 {
- var (
- value = common.BigToHash(stack.data[stack.len()-2])
- address = common.BigToHash(stack.data[stack.len()-1])
- )
- l.changedValues[contract.Address()][address] = value
+ return errTraceLimitReached
}
// Copy a snapshot of the current memory state to a new buffer
var mem []byte
@@ -185,13 +169,44 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
if !l.cfg.DisableStack {
stck = make([]*big.Int, len(stack.Data()))
for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item)
+ stck[i] = new(big.Int).Set(item.ToBig())
}
}
+ var rstack []uint32
+ if !l.cfg.DisableStack && rStack != nil {
+ rstck := make([]uint32, len(rStack.data))
+ copy(rstck, rStack.data)
+ }
// Copy a snapshot of the current storage to a new container
var storage Storage
if !l.cfg.DisableStorage {
- storage = l.changedValues[contract.Address()].Copy()
+ // initialise new changed values storage container for this contract
+ // if not present.
+ if l.storage[contract.Address()] == nil {
+ l.storage[contract.Address()] = make(Storage)
+ }
+ // capture SLOAD opcodes and record the read entry in the local storage
+ if op == SLOAD && stack.len() >= 1 {
+ var (
+ address = common.Hash(stack.data[stack.len()-1].Bytes32())
+ value = env.StateDB.GetState(contract.Address(), address)
+ )
+ l.storage[contract.Address()][address] = value
+ }
+ // capture SSTORE opcodes and record the written entry in the local storage.
+ if op == SSTORE && stack.len() >= 2 {
+ var (
+ value = common.Hash(stack.data[stack.len()-2].Bytes32())
+ address = common.Hash(stack.data[stack.len()-1].Bytes32())
+ )
+ l.storage[contract.Address()][address] = value
+ }
+ storage = l.storage[contract.Address()].Copy()
+ }
+ var rdata []byte
+ if !l.cfg.DisableReturnData {
+ rdata = make([]byte, len(rData))
+ copy(rdata, rData)
}
var operatorEvent map[string]string
@@ -203,35 +218,14 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
}
// create a new snapshot of the EVM.
- log := &StructLog{pc, op, contract.CallerAddress, contract.Address(), gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err, nil, nil, operatorEvent}
- afterHook := func(memory *Memory, stack *Stack) {
- // Copy a snapshot of the current memory state to a new buffer
- var mem []byte
- if !l.cfg.DisableMemory {
- mem = make([]byte, len(memory.Data()))
- copy(mem, memory.Data())
- }
-
- // Copy a snapshot of the current stack state to a new buffer
- var stck []*big.Int
- if !l.cfg.DisableStack {
- stck = make([]*big.Int, len(stack.Data()))
- for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item)
- }
- }
-
- log.AfterStack = stck
- log.AfterMemory = mem
- }
-
+ log := StructLog{pc, op, contract.CallerAddress, contract.Address(), gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err, nil, nil, operatorEvent}
l.logs = append(l.logs, log)
- return afterHook, nil
+ return nil
}
// CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode.
-func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
return nil
}
@@ -249,7 +243,7 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
}
// StructLogs returns the captured log entries.
-func (l *StructLogger) StructLogs() []*StructLog { return l.logs }
+func (l *StructLogger) StructLogs() []StructLog { return l.logs }
// Error returns the VM error captured by the trace.
func (l *StructLogger) Error() error { return l.err }
@@ -272,6 +266,12 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
fmt.Fprintf(writer, "%08d %x\n", len(log.Stack)-i-1, math.PaddedBigBytes(log.Stack[i], 32))
}
}
+ if len(log.ReturnStack) > 0 {
+ fmt.Fprintln(writer, "ReturnStack:")
+ for i := len(log.Stack) - 1; i >= 0; i-- {
+ fmt.Fprintf(writer, "%08d 0x%x (%d)\n", len(log.Stack)-i-1, log.ReturnStack[i], log.ReturnStack[i])
+ }
+ }
if len(log.Memory) > 0 {
fmt.Fprintln(writer, "Memory:")
fmt.Fprint(writer, hex.Dump(log.Memory))
@@ -282,6 +282,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
fmt.Fprintf(writer, "%x: %x\n", h, item)
}
}
+ if len(log.ReturnData) > 0 {
+ fmt.Fprintln(writer, "ReturnData:")
+ fmt.Fprint(writer, hex.Dump(log.ReturnData))
+ }
fmt.Fprintln(writer)
}
}
@@ -299,3 +303,77 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
fmt.Fprintln(writer)
}
}
+
+type mdLogger struct {
+ out io.Writer
+ cfg *LogConfig
+}
+
+// NewMarkdownLogger creates a logger which outputs information in a format adapted
+// for human readability, and is also a valid markdown table
+func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
+ l := &mdLogger{writer, cfg}
+ if l.cfg == nil {
+ l.cfg = &LogConfig{}
+ }
+ return l
+}
+
+func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
+ if !create {
+ fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ } else {
+ fmt.Fprintf(t.out, "From: `%v`\nCreate at: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
+ from.String(), to.String(),
+ input, gas, value)
+ }
+
+ fmt.Fprintf(t.out, `
+| Pc | Op | Cost | Stack | RStack | Refund |
+|-------|-------------|------|-----------|-----------|---------|
+`)
+ return nil
+}
+
+func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
+ fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
+
+ if !t.cfg.DisableStack {
+ // format stack
+ var a []string
+ for _, elem := range stack.data {
+ a = append(a, fmt.Sprintf("%v", elem.String()))
+ }
+ b := fmt.Sprintf("[%v]", strings.Join(a, ","))
+ fmt.Fprintf(t.out, "%10v |", b)
+
+ // format return stack
+ a = a[:0]
+ for _, elem := range rStack.data {
+ a = append(a, fmt.Sprintf("%2d", elem))
+ }
+ b = fmt.Sprintf("[%v]", strings.Join(a, ","))
+ fmt.Fprintf(t.out, "%10v |", b)
+ }
+ fmt.Fprintf(t.out, "%10v |", env.StateDB.GetRefund())
+ fmt.Fprintln(t.out, "")
+ if err != nil {
+ fmt.Fprintf(t.out, "Error: %v\n", err)
+ }
+ return nil
+}
+
+func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+
+ fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
+
+ return nil
+}
+
+func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) error {
+ fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
+ output, gasUsed, err)
+ return nil
+}
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index fefe06ffff..5ae15a2e2b 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -1,18 +1,18 @@
// Copyright 2017 The go-ethereum Authors
-// This file is part of go-ethereum.
+// This file is part of the go-ethereum library.
//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
+// 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.
//
-// go-ethereum is distributed in the hope that it will be useful,
+// 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 General Public License for more details.
+// GNU Lesser General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see .
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
package vm
@@ -26,7 +26,6 @@ import (
"github.com/ethereum/go-ethereum/common/math"
)
-// JSONLogger ...
type JSONLogger struct {
encoder *json.Encoder
cfg *LogConfig
@@ -42,13 +41,12 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
return l
}
-// CaptureStart ...
func (l *JSONLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
return nil
}
// CaptureState outputs state information on the logger.
-func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) (HookAfter, error) {
+func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
log := StructLog{
Pc: pc,
Op: op,
@@ -66,13 +64,22 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
- log.Stack = stack.Data()
+ //TODO(@holiman) improve this
+ logstack := make([]*big.Int, len(stack.Data()))
+ for i, item := range stack.Data() {
+ logstack[i] = item.ToBig()
+ }
+ log.Stack = logstack
+ log.ReturnStack = rStack.data
}
- return nil, l.encoder.Encode(log)
+ if !l.cfg.DisableReturnData {
+ log.ReturnData = rData
+ }
+ return l.encoder.Encode(log)
}
// CaptureFault outputs state information on the logger.
-func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
return nil
}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 191733eed2..2f08d5dca8 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -21,17 +21,19 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
+ "github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/internal/params"
+ "github.com/holiman/uint256"
)
type dummyContractRef struct {
calledForEach bool
}
-func (dummyContractRef) ReturnGas(*big.Int) {}
-func (dummyContractRef) Address() common.Address { return common.Address{} }
-func (dummyContractRef) Value() *big.Int { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte, bool) {}
+func (dummyContractRef) ReturnGas(*big.Int) {}
+func (dummyContractRef) Address() common.Address { return common.Address{} }
+func (dummyContractRef) Value() *big.Int { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
d.calledForEach = true
}
@@ -42,28 +44,29 @@ func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyStatedb struct {
- StateDB
+ state.DB
}
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
- env = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
+ env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()
+ rstack = newReturnStack()
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
)
- stack.push(big.NewInt(1))
- stack.push(big.NewInt(0))
+ stack.push(uint256.NewInt(0).SetUint64(1))
+ stack.push(uint256.NewInt(0))
var index common.Hash
- logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
- if len(logger.changedValues[contract.Address()]) == 0 {
- t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
+ logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil)
+ if len(logger.storage[contract.Address()]) == 0 {
+ t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
}
exp := common.BigToHash(big.NewInt(1))
- if logger.changedValues[contract.Address()][index] != exp {
- t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index])
+ if logger.storage[contract.Address()][index] != exp {
+ t.Errorf("expected %x, got %x", exp, logger.storage[contract.Address()][index])
}
}
diff --git a/core/vm/memory.go b/core/vm/memory.go
index c06b0054b8..dcc7099569 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -18,9 +18,8 @@ package vm
import (
"fmt"
- "math/big"
- "github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
)
// Memory implements a simple memory model for the ethereum virtual machine.
@@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
// 32 bytes.
-func (m *Memory) Set32(offset uint64, val *big.Int) {
+func (m *Memory) Set32(offset uint64, val *uint256.Int) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+32 > uint64(len(m.store)) {
@@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) {
// Zero the memory area
copy(m.store[offset:offset+32], []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, 0})
// Fill in relevant bits
- math.ReadBits(val, m.store[offset:offset+32])
+ val.WriteToSlice(m.store[offset:])
}
// Resize resizes the memory to size
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index 9faafaf0a3..72ca2b8eb3 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -101,26 +101,30 @@ const (
NUMBER
DIFFICULTY
GASLIMIT
- CHAINID = 0x46
- SELFBALANCE = 0x47
+ CHAINID = 0x46
+ SELFBALANCE = 0x47
+ BASEFEE OpCode = 0x48
)
// 0x50 range - 'storage' and execution.
const (
- POP OpCode = 0x50 + iota
- MLOAD
- MSTORE
- MSTORE8
- SLOAD
- SSTORE
- JUMP
- JUMPI
- PC
- MSIZE
- GAS
- JUMPDEST
- TLOAD // 0x5c
- TSTORE // 0x5d
+ POP OpCode = 0x50
+ MLOAD OpCode = 0x51
+ MSTORE OpCode = 0x52
+ MSTORE8 OpCode = 0x53
+ SLOAD OpCode = 0x54
+ SSTORE OpCode = 0x55
+ JUMP OpCode = 0x56
+ JUMPI OpCode = 0x57
+ PC OpCode = 0x58
+ MSIZE OpCode = 0x59
+ GAS OpCode = 0x5a
+ JUMPDEST OpCode = 0x5b
+ BEGINSUB OpCode = 0x5c
+ RETURNSUB OpCode = 0x5d
+ JUMPSUB OpCode = 0x5e
+ //TLOAD OpCode = 0x5f // 0x5c
+ //TSTORE OpCode = 0x60 // 0x5d
)
// 0x60 range.
@@ -283,6 +287,7 @@ var opCodeToString = map[OpCode]string{
GASLIMIT: "GASLIMIT",
CHAINID: "CHAINID",
SELFBALANCE: "SELFBALANCE",
+ BASEFEE: "BASEFEE",
// 0x50 range - 'storage' and execution.
POP: "POP",
@@ -299,8 +304,8 @@ var opCodeToString = map[OpCode]string{
MSIZE: "MSIZE",
GAS: "GAS",
JUMPDEST: "JUMPDEST",
- TLOAD: "TLOAD",
- TSTORE: "TSTORE",
+ //TLOAD: "TLOAD",
+ //TSTORE: "TSTORE",
// 0x60 range - push.
PUSH1: "PUSH1",
@@ -466,84 +471,84 @@ var stringToOp = map[string]OpCode{
"MSIZE": MSIZE,
"GAS": GAS,
"JUMPDEST": JUMPDEST,
- "TLOAD": TLOAD,
- "TSTORE": TSTORE,
- "PUSH1": PUSH1,
- "PUSH2": PUSH2,
- "PUSH3": PUSH3,
- "PUSH4": PUSH4,
- "PUSH5": PUSH5,
- "PUSH6": PUSH6,
- "PUSH7": PUSH7,
- "PUSH8": PUSH8,
- "PUSH9": PUSH9,
- "PUSH10": PUSH10,
- "PUSH11": PUSH11,
- "PUSH12": PUSH12,
- "PUSH13": PUSH13,
- "PUSH14": PUSH14,
- "PUSH15": PUSH15,
- "PUSH16": PUSH16,
- "PUSH17": PUSH17,
- "PUSH18": PUSH18,
- "PUSH19": PUSH19,
- "PUSH20": PUSH20,
- "PUSH21": PUSH21,
- "PUSH22": PUSH22,
- "PUSH23": PUSH23,
- "PUSH24": PUSH24,
- "PUSH25": PUSH25,
- "PUSH26": PUSH26,
- "PUSH27": PUSH27,
- "PUSH28": PUSH28,
- "PUSH29": PUSH29,
- "PUSH30": PUSH30,
- "PUSH31": PUSH31,
- "PUSH32": PUSH32,
- "DUP1": DUP1,
- "DUP2": DUP2,
- "DUP3": DUP3,
- "DUP4": DUP4,
- "DUP5": DUP5,
- "DUP6": DUP6,
- "DUP7": DUP7,
- "DUP8": DUP8,
- "DUP9": DUP9,
- "DUP10": DUP10,
- "DUP11": DUP11,
- "DUP12": DUP12,
- "DUP13": DUP13,
- "DUP14": DUP14,
- "DUP15": DUP15,
- "DUP16": DUP16,
- "SWAP1": SWAP1,
- "SWAP2": SWAP2,
- "SWAP3": SWAP3,
- "SWAP4": SWAP4,
- "SWAP5": SWAP5,
- "SWAP6": SWAP6,
- "SWAP7": SWAP7,
- "SWAP8": SWAP8,
- "SWAP9": SWAP9,
- "SWAP10": SWAP10,
- "SWAP11": SWAP11,
- "SWAP12": SWAP12,
- "SWAP13": SWAP13,
- "SWAP14": SWAP14,
- "SWAP15": SWAP15,
- "SWAP16": SWAP16,
- "LOG0": LOG0,
- "LOG1": LOG1,
- "LOG2": LOG2,
- "LOG3": LOG3,
- "LOG4": LOG4,
- "CREATE": CREATE,
- "CREATE2": CREATE2,
- "CALL": CALL,
- "RETURN": RETURN,
- "CALLCODE": CALLCODE,
- "REVERT": REVERT,
- "SELFDESTRUCT": SELFDESTRUCT,
+ //"TLOAD": TLOAD,
+ //"TSTORE": TSTORE,
+ "PUSH1": PUSH1,
+ "PUSH2": PUSH2,
+ "PUSH3": PUSH3,
+ "PUSH4": PUSH4,
+ "PUSH5": PUSH5,
+ "PUSH6": PUSH6,
+ "PUSH7": PUSH7,
+ "PUSH8": PUSH8,
+ "PUSH9": PUSH9,
+ "PUSH10": PUSH10,
+ "PUSH11": PUSH11,
+ "PUSH12": PUSH12,
+ "PUSH13": PUSH13,
+ "PUSH14": PUSH14,
+ "PUSH15": PUSH15,
+ "PUSH16": PUSH16,
+ "PUSH17": PUSH17,
+ "PUSH18": PUSH18,
+ "PUSH19": PUSH19,
+ "PUSH20": PUSH20,
+ "PUSH21": PUSH21,
+ "PUSH22": PUSH22,
+ "PUSH23": PUSH23,
+ "PUSH24": PUSH24,
+ "PUSH25": PUSH25,
+ "PUSH26": PUSH26,
+ "PUSH27": PUSH27,
+ "PUSH28": PUSH28,
+ "PUSH29": PUSH29,
+ "PUSH30": PUSH30,
+ "PUSH31": PUSH31,
+ "PUSH32": PUSH32,
+ "DUP1": DUP1,
+ "DUP2": DUP2,
+ "DUP3": DUP3,
+ "DUP4": DUP4,
+ "DUP5": DUP5,
+ "DUP6": DUP6,
+ "DUP7": DUP7,
+ "DUP8": DUP8,
+ "DUP9": DUP9,
+ "DUP10": DUP10,
+ "DUP11": DUP11,
+ "DUP12": DUP12,
+ "DUP13": DUP13,
+ "DUP14": DUP14,
+ "DUP15": DUP15,
+ "DUP16": DUP16,
+ "SWAP1": SWAP1,
+ "SWAP2": SWAP2,
+ "SWAP3": SWAP3,
+ "SWAP4": SWAP4,
+ "SWAP5": SWAP5,
+ "SWAP6": SWAP6,
+ "SWAP7": SWAP7,
+ "SWAP8": SWAP8,
+ "SWAP9": SWAP9,
+ "SWAP10": SWAP10,
+ "SWAP11": SWAP11,
+ "SWAP12": SWAP12,
+ "SWAP13": SWAP13,
+ "SWAP14": SWAP14,
+ "SWAP15": SWAP15,
+ "SWAP16": SWAP16,
+ "LOG0": LOG0,
+ "LOG1": LOG1,
+ "LOG2": LOG2,
+ "LOG3": LOG3,
+ "LOG4": LOG4,
+ "CREATE": CREATE,
+ "CREATE2": CREATE2,
+ "CALL": CALL,
+ "RETURN": RETURN,
+ "CALLCODE": CALLCODE,
+ "REVERT": REVERT,
+ "SELFDESTRUCT": SELFDESTRUCT,
}
// StringToOp finds the opcode whose name is stored in `str`.
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
new file mode 100644
index 0000000000..55e8331c42
--- /dev/null
+++ b/core/vm/operations_acl.go
@@ -0,0 +1,323 @@
+// Copyright 2020 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 .
+
+package vm
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+const (
+ ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
+ ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
+ WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
+)
+
+func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
+ return func(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.peek()
+ slot = common.Hash(x.Bytes32())
+ current = evm.StateDB.GetState(contract.Address(), slot)
+ cost = uint64(0)
+ )
+ // Check slot presence in the access list
+ if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ cost = params.ColdSloadCostEIP2929
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ if !addrPresent {
+ // Once we're done with YOLOv2 and schedule this for mainnet, might
+ // be good to remove this panic here, which is just really a
+ // canary to have during testing
+ panic("impossible case: address was not present in access list during sstore op")
+ }
+ }
+ value := common.Hash(y.Bytes32())
+
+ if current == value { // noop (1)
+ // EIP 2200 original clause:
+ // return params.SloadGasEIP2200, nil
+ return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return cost + params.SstoreSetGasEIP2200, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ // EIP-2200 original clause:
+ // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
+ return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
+ }
+ if original != (common.Hash{}) {
+ if current == (common.Hash{}) { // recreate slot (2.2.1.1)
+ evm.StateDB.SubRefund(clearingRefund)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ // EIP 2200 Original clause:
+ //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
+ } else { // reset to original existing slot (2.2.2.2)
+ // EIP 2200 Original clause:
+ // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
+ // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
+ // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
+ // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
+ evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
+ }
+ }
+ // EIP-2200 original clause:
+ //return params.SloadGasEIP2200, nil // dirty update (2.2)
+ return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
+ }
+}
+
+// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
+//
+// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
+// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
+// Additionally, modify the parameters defined in EIP 2200 as follows:
+//
+// Parameter Old value New value
+// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
+// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
+//
+// The other parameters defined in EIP 2200 are unchanged.
+// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
+func gasSStoreEIP2929(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.peek()
+ slot = common.Hash(x.Bytes32())
+ current = evm.StateDB.GetState(contract.Address(), slot)
+ cost = uint64(0)
+ )
+ // Check slot presence in the access list
+ if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ cost = ColdSloadCostEIP2929
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ if !addrPresent {
+ // Once we're done with YOLOv2 and schedule this for mainnet, might
+ // be good to remove this panic here, which is just really a
+ // canary to have during testing
+ panic("impossible case: address was not present in access list during sstore op")
+ }
+ }
+ value := common.Hash(y.Bytes32())
+
+ if current == value { // noop (1)
+ // EIP 2200 original clause:
+ // return params.SloadGasEIP2200, nil
+ return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return cost + params.SstoreSetGasEIP2200, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
+ }
+ // EIP-2200 original clause:
+ // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
+ return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), 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.SstoreClearsScheduleRefundEIP2200)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ // EIP 2200 Original clause:
+ //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
+ } else { // reset to original existing slot (2.2.2.2)
+ // EIP 2200 Original clause:
+ // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
+ // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
+ // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
+ // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
+ evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
+ }
+ }
+ // EIP-2200 original clause:
+ //return params.SloadGasEIP2200, nil // dirty update (2.2)
+ return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
+}
+
+// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
+// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
+// whose storage is being read) is not yet in accessed_storage_keys,
+// charge 2100 gas and add the pair to accessed_storage_keys.
+// If the pair is already in accessed_storage_keys, charge 100 gas.
+func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ loc := stack.peek()
+ slot := common.Hash(loc.Bytes32())
+ // Check slot presence in the access list
+ if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ // If the caller cannot afford the cost, this change will be rolled back
+ // If he does afford it, we can skip checking the same thing later on, during execution
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ return ColdSloadCostEIP2929, nil
+ }
+ return WarmStorageReadCostEIP2929, nil
+}
+
+// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
+// EIP spec:
+// > If the target is not in accessed_addresses,
+// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
+// > Otherwise, charge WARM_STORAGE_READ_COST gas.
+func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // memory expansion first (dynamic part of pre-2929 implementation)
+ gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ addr := common.Address(stack.peek().Bytes20())
+ // Check slot presence in the access list
+ if !evm.StateDB.AddressInAccessList(addr) {
+ evm.StateDB.AddAddressToAccessList(addr)
+ var overflow bool
+ // We charge (cold-warm), since 'warm' is already charged as constantGas
+ if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+ }
+ return gas, nil
+}
+
+// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
+// If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
+// is also using 'warm' as constant factor.
+// This method is used by:
+// - extcodehash,
+// - extcodesize,
+// - (ext) balance
+func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ addr := common.Address(stack.peek().Bytes20())
+ // Check slot presence in the access list
+ if !evm.StateDB.AddressInAccessList(addr) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(addr)
+ // The warm storage read cost is already charged as constantGas
+ return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
+ }
+ return 0, nil
+}
+
+func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
+ return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ addr := common.Address(stack.Back(1).Bytes20())
+ // Check slot presence in the access list
+ if !evm.StateDB.AddressInAccessList(addr) {
+ evm.StateDB.AddAddressToAccessList(addr)
+ // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
+ if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
+ return 0, ErrOutOfGas
+ }
+ }
+ // Now call the old calculator, which takes into account
+ // - create new account
+ // - transfer value
+ // - memory expansion
+ // - 63/64ths rule
+ return oldCalculator(evm, contract, stack, mem, memorySize)
+ }
+}
+
+var (
+ gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall)
+ gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
+ gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
+ gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
+
+ gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
+
+ // gasSStoreEIP2539 implements gas cost for SSTORE according to EIP-2539
+ // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
+ gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
+)
+
+func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ address = common.Address(stack.peek().Bytes20())
+ )
+ if !evm.StateDB.AddressInAccessList(address) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(address)
+ gas = ColdAccountAccessCostEIP2929
+ }
+ // if empty and transfers value
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
+ gas += params.CreateBySelfdestructGas
+ }
+ if !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(params.SelfdestructRefundGas)
+ }
+ return gas, nil
+
+}
+
+// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
+func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
+ gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ address = common.Address(stack.peek().Bytes20())
+ )
+ if !evm.StateDB.AddressInAccessList(address) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(address)
+ gas = params.ColdAccountAccessCostEIP2929
+ }
+ // if empty and transfers value
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
+ gas += params.CreateBySelfdestructGas
+ }
+ if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(params.SelfdestructRefundGas)
+ }
+ return gas, nil
+ }
+ return gasFunc
+}
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index ab37b97093..9668728019 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -24,22 +24,25 @@ import (
// NewEnv ...
func NewEnv(cfg *Config) *vm.EVM {
- context := vm.Context{
+ context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
IsValidator: core.IsValidator,
GetHash: func(uint64) common.Hash { return common.Hash{} },
GetVRF: func(uint64) common.Hash { return common.Hash{} },
- Origin: cfg.Origin,
+ //Origin: cfg.Origin,
Coinbase: cfg.Coinbase,
BlockNumber: cfg.BlockNumber,
EpochNumber: cfg.EpochNumber,
VRF: cfg.VRF,
Time: cfg.Time,
GasLimit: cfg.GasLimit,
- GasPrice: cfg.GasPrice,
+ //GasPrice: cfg.GasPrice,
}
- return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
+ return vm.NewEVM(context, vm.TxContext{
+ Origin: cfg.Origin,
+ GasPrice: cfg.GasPrice,
+ }, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index c9c3d07f4b..af27d6552c 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -18,36 +18,48 @@ package vm
import (
"fmt"
- "math/big"
+ "sync"
+
+ "github.com/holiman/uint256"
)
+var stackPool = sync.Pool{
+ New: func() interface{} {
+ return &Stack{data: make([]uint256.Int, 0, 16)}
+ },
+}
+
// Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
type Stack struct {
- data []*big.Int
+ data []uint256.Int
}
func newstack() *Stack {
- return &Stack{data: make([]*big.Int, 0, 1024)}
+ return stackPool.Get().(*Stack)
}
-// Data returns the underlying big.Int array.
-func (st *Stack) Data() []*big.Int {
+func returnStack(s *Stack) {
+ s.data = s.data[:0]
+ stackPool.Put(s)
+}
+
+// Data returns the underlying uint256.Int array.
+func (st *Stack) Data() []uint256.Int {
return st.data
}
-func (st *Stack) push(d *big.Int) {
+func (st *Stack) push(d *uint256.Int) {
// NOTE push limit (1024) is checked in baseCheck
- //stackItem := new(big.Int).Set(d)
- //st.data = append(st.data, stackItem)
- st.data = append(st.data, d)
+ st.data = append(st.data, *d)
}
-func (st *Stack) pushN(ds ...*big.Int) {
+func (st *Stack) pushN(ds ...uint256.Int) {
+ // FIXME: Is there a way to pass args by pointers.
st.data = append(st.data, ds...)
}
-func (st *Stack) pop() (ret *big.Int) {
+func (st *Stack) pop() (ret uint256.Int) {
ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1]
return
@@ -61,17 +73,17 @@ func (st *Stack) swap(n int) {
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
}
-func (st *Stack) dup(pool *intPool, n int) {
- st.push(pool.get().Set(st.data[st.len()-n]))
+func (st *Stack) dup(n int) {
+ st.push(&st.data[st.len()-n])
}
-func (st *Stack) peek() *big.Int {
- return st.data[st.len()-1]
+func (st *Stack) peek() *uint256.Int {
+ return &st.data[st.len()-1]
}
// Back returns the n'th item in stack
-func (st *Stack) Back(n int) *big.Int {
- return st.data[st.len()-n-1]
+func (st *Stack) Back(n int) *uint256.Int {
+ return &st.data[st.len()-n-1]
}
// Print dumps the content of the stack
@@ -86,3 +98,34 @@ func (st *Stack) Print() {
}
fmt.Println("#############")
}
+
+var rStackPool = sync.Pool{
+ New: func() interface{} {
+ return &ReturnStack{data: make([]uint32, 0, 10)}
+ },
+}
+
+// ReturnStack is an object for basic return stack operations.
+type ReturnStack struct {
+ data []uint32
+}
+
+func newReturnStack() *ReturnStack {
+ return rStackPool.Get().(*ReturnStack)
+}
+
+func returnRStack(rs *ReturnStack) {
+ rs.data = rs.data[:0]
+ rStackPool.Put(rs)
+}
+
+func (st *ReturnStack) push(d uint32) {
+ st.data = append(st.data, d)
+}
+
+// A uint32 is sufficient as for code below 4.2G
+func (st *ReturnStack) pop() (ret uint32) {
+ ret = st.data[len(st.data)-1]
+ st.data = st.data[:len(st.data)-1]
+ return
+}
diff --git a/hmy/hmy.go b/hmy/hmy.go
index c82ea7e78e..4081538fd1 100644
--- a/hmy/hmy.go
+++ b/hmy/hmy.go
@@ -240,8 +240,8 @@ func (hmy *Harmony) GetNodeMetadata() commonRPC.NodeMetadata {
// GetEVM returns a new EVM entity
func (hmy *Harmony) GetEVM(ctx context.Context, msg core.Message, state *state.DB, header *block.Header) (*vm.EVM, error) {
state.SetBalance(msg.From(), math.MaxBig256)
- vmCtx := core.NewEVMContext(msg, header, hmy.BlockChain, nil)
- return vm.NewEVM(vmCtx, state, hmy.BlockChain.Config(), *hmy.BlockChain.GetVMConfig()), nil
+ vmCtx := core.NewEVMBlockContext(msg, header, hmy.BlockChain, nil)
+ return vm.NewEVM(vmCtx, core.NewEVMTxContext(msg), state, hmy.BlockChain.Config(), *hmy.BlockChain.GetVMConfig()), nil
}
// ChainDb ..
diff --git a/hmy/override.go b/hmy/override.go
index dfe7431671..50a7955cdf 100644
--- a/hmy/override.go
+++ b/hmy/override.go
@@ -121,7 +121,7 @@ type BlockOverrides struct {
// apply overrides the given header fields into the given block context
// difficulty & random not part of vm.Context
-func (b *BlockOverrides) Apply(blockCtx *vm.Context) {
+func (b *BlockOverrides) Apply(blockCtx *vm.BlockContext) {
if b == nil {
return
}
diff --git a/hmy/override_test.go b/hmy/override_test.go
index 5f07bcbd14..8b25d6a0cd 100644
--- a/hmy/override_test.go
+++ b/hmy/override_test.go
@@ -173,7 +173,7 @@ func TestBlockOverrides(t *testing.T) {
// Difficulty, PrevRandao, BaseFeePerGas, and BlobBaseFee are no-op
}
- ctx := &vm.Context{
+ ctx := &vm.BlockContext{
BlockNumber: big.NewInt(0),
Time: big.NewInt(0),
GasLimit: 0,
diff --git a/hmy/tracer.go b/hmy/tracer.go
index b0af45820c..7fdf79ea38 100644
--- a/hmy/tracer.go
+++ b/hmy/tracer.go
@@ -175,7 +175,7 @@ func (hmy *Harmony) TraceChain(ctx context.Context, start, end *types.Block, con
signer = ethSigner
}
msg, _ := tx.AsMessage(signer)
- vmCtx := core.NewEVMContext(msg, task.block.Header(), hmy.BlockChain, nil)
+ vmCtx := core.NewEVMBlockContext(msg, task.block.Header(), hmy.BlockChain, nil)
res, err := hmy.TraceTx(ctx, msg, vmCtx, task.statedb, config)
if err != nil {
@@ -384,7 +384,7 @@ traceLoop:
msg, _ := tx.AsMessage(signer)
statedb.SetTxContext(tx.Hash(), blockHash, i)
statedb.SetTxHashETH(tx.ConvertToEth().Hash())
- vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ vmctx := core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
res, err := hmy.TraceTx(ctx, msg, vmctx, statedb, config)
if err != nil {
results[i] = &TxTraceResult{Error: err.Error()}
@@ -466,7 +466,7 @@ func (hmy *Harmony) TraceBlock(ctx context.Context, block *types.Block, config *
}
msg, _ := txs[task.index].AsMessage(signer)
- vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ vmctx := core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
tx := txs[task.index]
task.statedb.SetTxContext(tx.Hash(), blockHash, task.index)
task.statedb.SetTxHashETH(tx.ConvertToEth().Hash())
@@ -493,9 +493,9 @@ func (hmy *Harmony) TraceBlock(ctx context.Context, block *types.Block, config *
msg, _ := tx.AsMessage(signer)
statedb.SetTxContext(tx.Hash(), block.Hash(), i)
statedb.SetTxHashETH(tx.ConvertToEth().Hash())
- vmctx := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ vmctx := core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
- vmenv := vm.NewEVM(vmctx, statedb, hmy.BlockChain.Config(), vm.Config{})
+ vmenv := vm.NewEVM(vmctx, core.NewEVMTxContext(msg), statedb, hmy.BlockChain.Config(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
failed = err
break
@@ -566,7 +566,7 @@ func (hmy *Harmony) standardTraceBlockToFile(ctx context.Context, block *types.B
// Prepare the transaction for un-traced execution
var (
msg, _ = tx.AsMessage(signer)
- vmctx = core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ vmctx = core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
vmConf vm.Config
dump *os.File
@@ -593,7 +593,7 @@ func (hmy *Harmony) standardTraceBlockToFile(ctx context.Context, block *types.B
}
}
// Execute the transaction and flush any traces to disk
- vmenv := vm.NewEVM(vmctx, statedb, hmy.BlockChain.Config(), vmConf)
+ vmenv := vm.NewEVM(vmctx, core.NewEVMTxContext(msg), statedb, hmy.BlockChain.Config(), vmConf)
_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
if writer != nil {
writer.Flush()
@@ -710,7 +710,7 @@ func (hmy *Harmony) ComputeStateDB(block *types.Block, reexec uint64) (*state.DB
// executes the given message in the provided environment. The return value will
// be tracer dependent.
// NOTE: Only support default StructLogger tracer
-func (hmy *Harmony) TraceTx(ctx context.Context, message core.Message, vmctx vm.Context, statedb *state.DB, config *TraceConfig) (interface{}, error) {
+func (hmy *Harmony) TraceTx(ctx context.Context, message core.Message, vmctx vm.BlockContext, statedb *state.DB, config *TraceConfig) (interface{}, error) {
// Assemble the structured logger or the JavaScript tracer
var (
tracer vm.Tracer
@@ -751,7 +751,7 @@ func (hmy *Harmony) TraceTx(ctx context.Context, message core.Message, vmctx vm.
tracer = vm.NewStructLogger(config.LogConfig)
}
// Run the transaction with tracing enabled.
- vmenv := vm.NewEVM(vmctx, statedb, hmy.BlockChain.Config(), vm.Config{Debug: true, Tracer: tracer})
+ vmenv := vm.NewEVM(vmctx, core.NewEVMTxContext(message), statedb, hmy.BlockChain.Config(), vm.Config{Debug: true, Tracer: tracer})
result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
if err != nil {
@@ -780,19 +780,19 @@ func (hmy *Harmony) TraceTx(ctx context.Context, message core.Message, vmctx vm.
}
// ComputeTxEnv returns the execution environment of a certain transaction.
-func (hmy *Harmony) ComputeTxEnv(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.Context, *state.DB, error) {
+func (hmy *Harmony) ComputeTxEnv(block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.DB, error) {
// Create the parent state database
parent := hmy.BlockChain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
- return nil, vm.Context{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("parent %#x not found", block.ParentHash())
}
statedb, err := hmy.ComputeStateDB(parent, reexec)
if err != nil {
- return nil, vm.Context{}, nil, err
+ return nil, vm.BlockContext{}, nil, err
}
if txIndex == 0 && len(block.Transactions()) == 0 {
- return nil, vm.Context{}, statedb, nil
+ return nil, vm.BlockContext{}, statedb, nil
}
// Recompute transactions up to the target index.
@@ -807,23 +807,23 @@ func (hmy *Harmony) ComputeTxEnv(block *types.Block, txIndex int, reexec uint64)
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer)
- context := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ context := core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
if idx == txIndex {
return msg, context, statedb, nil
}
// Not yet the searched for transaction, execute on top of the current state
- vmenv := vm.NewEVM(context, statedb, hmy.BlockChain.Config(), vm.Config{})
+ vmenv := vm.NewEVM(context, core.NewEVMTxContext(msg), statedb, hmy.BlockChain.Config(), vm.Config{})
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.GasLimit())); err != nil {
- return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
statedb.Finalise(true)
}
- return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
+ return nil, vm.BlockContext{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, block.Hash())
}
// ComputeTxEnvEachBlockWithoutApply returns the execution environment of a certain transaction.
-func (hmy *Harmony) ComputeTxEnvEachBlockWithoutApply(block *types.Block, reexec uint64, cb func(int, *types.Transaction, core.Message, vm.Context, *state.DB) bool) error {
+func (hmy *Harmony) ComputeTxEnvEachBlockWithoutApply(block *types.Block, reexec uint64, cb func(int, *types.Transaction, core.Message, vm.BlockContext, *state.DB) bool) error {
// Create the parent state database
parent := hmy.BlockChain.GetBlock(block.ParentHash(), block.NumberU64()-1)
if parent == nil {
@@ -846,7 +846,7 @@ func (hmy *Harmony) ComputeTxEnvEachBlockWithoutApply(block *types.Block, reexec
// Assemble the transaction call message and return if the requested offset
msg, _ := tx.AsMessage(signer)
- context := core.NewEVMContext(msg, block.Header(), hmy.BlockChain, nil)
+ context := core.NewEVMBlockContext(msg, block.Header(), hmy.BlockChain, nil)
if !cb(idx, tx, msg, context, statedb) {
return nil
}
@@ -965,7 +965,7 @@ func (r *StructLogRes) GetOperatorEvent(key string) string {
}
// FormatLogs formats EVM returned structured logs for json output
-func FormatLogs(logs []*vm.StructLog, conf *TraceConfig) []StructLogRes {
+func FormatLogs(logs []vm.StructLog, conf *TraceConfig) []StructLogRes {
formatted := make([]StructLogRes, len(logs))
for index, trace := range logs {
formatted[index] = StructLogRes{
diff --git a/hmy/tracers/block_tracer.go b/hmy/tracers/block_tracer.go
index 12391c6fb9..cb6e0a8cec 100644
--- a/hmy/tracers/block_tracer.go
+++ b/hmy/tracers/block_tracer.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/core/vm"
+ "github.com/holiman/uint256"
)
type action struct {
@@ -41,7 +42,7 @@ type action struct {
gasUsed uint64
outOff int64
outLen int64
- value *big.Int
+ value *uint256.Int
err error
revert []byte
subCalls []*action
@@ -64,7 +65,7 @@ func (c *action) fromStorage(blockStorage *TraceBlockStorage, acStorage *ActionS
if c.op == vm.CREATE || c.op == vm.CREATE2 {
fromIndex := int(acStorage.readNumber().Int64())
toIndex := int(acStorage.readNumber().Int64())
- c.value = acStorage.readNumber()
+ c.value, _ = uint256.FromBig(acStorage.readNumber())
inputIndex := int(acStorage.readNumber().Int64())
outputIndex := int(acStorage.readNumber().Int64())
c.gas = acStorage.readNumber().Uint64()
@@ -78,7 +79,7 @@ func (c *action) fromStorage(blockStorage *TraceBlockStorage, acStorage *ActionS
if c.op == vm.CALL || c.op == vm.CALLCODE || c.op == vm.DELEGATECALL || c.op == vm.STATICCALL {
fromIndex := int(acStorage.readNumber().Int64())
toIndex := int(acStorage.readNumber().Int64())
- c.value = acStorage.readNumber()
+ c.value, _ = uint256.FromBig(acStorage.readNumber())
inputIndex := int(acStorage.readNumber().Int64())
outputIndex := int(acStorage.readNumber().Int64())
c.gas = acStorage.readNumber().Uint64()
@@ -94,7 +95,7 @@ func (c *action) fromStorage(blockStorage *TraceBlockStorage, acStorage *ActionS
toIndex := int(acStorage.readNumber().Int64())
c.from = blockStorage.getAddress(fromIndex)
c.to = blockStorage.getAddress(toIndex)
- c.value = acStorage.readNumber()
+ c.value, _ = uint256.FromBig(acStorage.readNumber())
}
}
@@ -111,44 +112,44 @@ func (c action) toStorage(blockStorage *TraceBlockStorage) *ActionStorage {
acStorage.appendByte(errByte)
if errByte != 0 {
revertIndex := blockStorage.indexData(c.revert)
- acStorage.appendNumber(big.NewInt(int64(revertIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(revertIndex)))
}
if c.op == vm.CREATE || c.op == vm.CREATE2 {
fromIndex := blockStorage.indexAddress(c.from)
toIndex := blockStorage.indexAddress(c.to)
inputIndex := blockStorage.indexData(c.input)
outputIndex := blockStorage.indexData(c.output)
- acStorage.appendNumber(big.NewInt(int64(fromIndex)))
- acStorage.appendNumber(big.NewInt(int64(toIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(fromIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(toIndex)))
acStorage.appendNumber(c.value)
- acStorage.appendNumber(big.NewInt(int64(inputIndex)))
- acStorage.appendNumber(big.NewInt(int64(outputIndex)))
- acStorage.appendNumber((&big.Int{}).SetUint64(c.gas))
- acStorage.appendNumber((&big.Int{}).SetUint64(c.gasUsed))
+ acStorage.appendNumber(uint256.NewInt(uint64(inputIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(outputIndex)))
+ acStorage.appendNumber((&uint256.Int{}).SetUint64(c.gas))
+ acStorage.appendNumber((&uint256.Int{}).SetUint64(c.gasUsed))
return acStorage
}
if c.op == vm.CALL || c.op == vm.CALLCODE || c.op == vm.DELEGATECALL || c.op == vm.STATICCALL {
if c.value == nil {
- c.value = big.NewInt(0)
+ c.value = uint256.NewInt(0)
}
fromIndex := blockStorage.indexAddress(c.from)
toIndex := blockStorage.indexAddress(c.to)
inputIndex := blockStorage.indexData(c.input)
outputIndex := blockStorage.indexData(c.output)
- acStorage.appendNumber(big.NewInt(int64(fromIndex)))
- acStorage.appendNumber(big.NewInt(int64(toIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64((fromIndex))))
+ acStorage.appendNumber(uint256.NewInt(uint64(toIndex)))
acStorage.appendNumber(c.value)
- acStorage.appendNumber(big.NewInt(int64(inputIndex)))
- acStorage.appendNumber(big.NewInt(int64(outputIndex)))
- acStorage.appendNumber((&big.Int{}).SetUint64(c.gas))
- acStorage.appendNumber((&big.Int{}).SetUint64(c.gasUsed))
+ acStorage.appendNumber(uint256.NewInt(uint64(inputIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(outputIndex)))
+ acStorage.appendNumber((&uint256.Int{}).SetUint64(c.gas))
+ acStorage.appendNumber((&uint256.Int{}).SetUint64(c.gasUsed))
return acStorage
}
if c.op == vm.SELFDESTRUCT {
fromIndex := blockStorage.indexAddress(c.from)
toIndex := blockStorage.indexAddress(c.to)
- acStorage.appendNumber(big.NewInt(int64(fromIndex)))
- acStorage.appendNumber(big.NewInt(int64(toIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(fromIndex)))
+ acStorage.appendNumber(uint256.NewInt(uint64(toIndex)))
acStorage.appendNumber(c.value)
return acStorage
}
@@ -159,8 +160,8 @@ func (c action) toJsonStr() (string, *string, *string) {
callType := strings.ToLower(c.op.String())
if c.op == vm.CREATE || c.op == vm.CREATE2 {
action := fmt.Sprintf(
- `{"from":"0x%x","gas":"0x%x","init":"0x%x","value":"0x%s"}`,
- c.from, c.gas, c.input, c.value.Text(16),
+ `{"from":"0x%x","gas":"0x%x","init":"0x%x","value":"%s"}`,
+ c.from, c.gas, c.input, c.value.Hex(),
)
output := fmt.Sprintf(
`{"address":"0x%x","code":"0x%x","gasUsed":"0x%x"}`,
@@ -170,12 +171,12 @@ func (c action) toJsonStr() (string, *string, *string) {
}
if c.op == vm.CALL || c.op == vm.CALLCODE || c.op == vm.DELEGATECALL || c.op == vm.STATICCALL {
if c.value == nil {
- c.value = big.NewInt(0)
+ c.value = uint256.NewInt(0)
}
var valueStr string
if c.op != vm.STATICCALL && c.op != vm.DELEGATECALL {
- valueStr = fmt.Sprintf(`,"value":"0x%s"`, c.value.Text(16))
+ valueStr = fmt.Sprintf(`,"value":"%s"`, c.value.Hex())
}
action := fmt.Sprintf(
@@ -191,8 +192,8 @@ func (c action) toJsonStr() (string, *string, *string) {
}
if c.op == vm.SELFDESTRUCT {
action := fmt.Sprintf(
- `{"refundAddress":"0x%x","balance":"0x%s","address":"0x%x"}`,
- c.to, c.value.Text(16), c.from,
+ `{"refundAddress":"0x%x","balance":"%s","address":"0x%x"}`,
+ c.to, c.value.Hex(), c.from,
)
return "suicide", &action, nil
}
@@ -241,30 +242,31 @@ func (jst *ParityBlockTracer) CaptureStart(env *vm.EVM, from common.Address, to
if create {
jst.cur.op = vm.CREATE // virtual create
}
+ vv, _ := uint256.FromBig(value)
jst.cur.from = from
jst.cur.to = to
jst.cur.input = input
jst.cur.gas = gas
- jst.cur.value = (&big.Int{}).Set(value)
+ jst.cur.value = vv
jst.cur.blockHash = env.StateDB.BlockHash()
jst.cur.transactionPosition = uint64(env.StateDB.TxIndex())
jst.cur.transactionHash = env.StateDB.TxHashETH()
- jst.cur.blockNumber = env.BlockNumber.Uint64()
+ jst.cur.blockNumber = env.Context.BlockNumber.Uint64()
jst.cur.descended = false
jst.cur.push(&jst.cur.action)
return nil
}
// CaptureState implements the ParityBlockTracer interface to trace a single step of VM execution.
-func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) (vm.HookAfter, error) {
+func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, returnStack *vm.ReturnStack, bts []byte, contract *vm.Contract, depth int, err error) error {
if err != nil {
- return nil, jst.CaptureFault(env, pc, op, gas, cost, memory, stack, contract, depth, err)
+ return jst.CaptureFault(env, pc, op, gas, cost, memory, stack, nil, contract, depth, err)
}
var retErr error
- stackPeek := func(n int) *big.Int {
+ stackPeek := func(n int) *uint256.Int {
if n >= len(stack.Data()) {
retErr = errors.New("tracer bug:stack overflow")
- return big.NewInt(0)
+ return uint256.NewInt(0)
}
return stack.Back(n)
}
@@ -278,41 +280,42 @@ func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
switch op {
case vm.CREATE, vm.CREATE2:
- inOff := stackPeek(1).Int64()
- inSize := stackPeek(2).Int64()
+ inOff := int64(stackPeek(1).Uint64())
+ inSize := int64(stackPeek(2).Uint64())
jst.cur.push(&action{
op: op,
from: contract.Address(),
input: memoryCopy(inOff, inSize),
gasIn: gas,
gasCost: cost,
- value: (&big.Int{}).Set(stackPeek(0)),
+ value: stackPeek(0),
})
jst.cur.descended = true
- return nil, retErr
+ return retErr
case vm.SELFDESTRUCT:
ac := jst.cur.last()
+ value, _ := uint256.FromBig(env.StateDB.GetBalance(contract.Address()))
ac.push(&action{
op: op,
from: contract.Address(),
- to: common.BigToAddress(stackPeek(0)),
+ to: stackPeek(0).Bytes20(),
gasIn: gas,
gasCost: cost,
- value: env.StateDB.GetBalance(contract.Address()),
+ value: value,
})
- return nil, retErr
+ return retErr
case vm.CALL, vm.CALLCODE, vm.DELEGATECALL, vm.STATICCALL:
- to := common.BigToAddress(stackPeek(1))
+ to := stackPeek(1).Bytes20()
precompiles := vm.PrecompiledContractsVRF
if _, exist := precompiles[to]; exist {
- return nil, nil
+ return nil
}
off := 1
if op == vm.DELEGATECALL || op == vm.STATICCALL {
off = 0
}
- inOff := stackPeek(2 + off).Int64()
- inSize := stackPeek(3 + off).Int64()
+ inOff := int64(stackPeek(2 + off).Uint64())
+ inSize := int64(stackPeek(3 + off).Uint64())
callObj := &action{
op: op,
from: contract.Address(),
@@ -320,15 +323,15 @@ func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
input: memoryCopy(inOff, inSize),
gasIn: gas,
gasCost: cost,
- outOff: stackPeek(4 + off).Int64(),
- outLen: stackPeek(5 + off).Int64(),
+ outOff: stackPeek(4 + off).ToBig().Int64(),
+ outLen: stackPeek(5 + off).ToBig().Int64(),
}
if op != vm.DELEGATECALL && op != vm.STATICCALL {
- callObj.value = (&big.Int{}).Set(stackPeek(2))
+ callObj.value = stackPeek(2).Clone()
}
jst.cur.push(callObj)
jst.cur.descended = true
- return nil, retErr
+ return retErr
}
if jst.cur.descended {
@@ -340,10 +343,10 @@ func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
if op == vm.REVERT {
last := jst.cur.last()
last.err = errors.New("execution reverted")
- revertOff := stackPeek(0).Int64()
- revertLen := stackPeek(1).Int64()
+ revertOff := int64(stackPeek(0).Uint64())
+ revertLen := int64(stackPeek(1).Uint64())
last.revert = memoryCopy(revertOff, revertLen)
- return nil, retErr
+ return retErr
}
if depth == jst.cur.len()-1 { // depth == len - 1
call := jst.cur.pop()
@@ -352,7 +355,7 @@ func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
ret := stackPeek(0)
if ret.Sign() != 0 {
- call.to = common.BigToAddress(ret)
+ call.to = ret.Bytes20()
call.output = env.StateDB.GetCode(call.to)
} else if call.err == nil {
call.err = errors.New("internal failure")
@@ -370,12 +373,12 @@ func (jst *ParityBlockTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode,
}
jst.cur.last().push(call)
}
- return nil, retErr
+ return retErr
}
// CaptureFault implements the ParityBlockTracer interface to trace an execution fault
// while running an opcode.
-func (jst *ParityBlockTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (jst *ParityBlockTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, returnStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
if jst.cur.last().err != nil {
return nil
}
@@ -459,7 +462,7 @@ func (jst *ParityBlockTracer) GetResult() ([]json.RawMessage, error) {
)
var resultPiece string
if ac.err != nil {
- resultPiece = fmt.Sprintf(`,"error":"Reverted","revert":"0x%x"`, ac.revert)
+ resultPiece = fmt.Sprintf(`,"error":"Reverted","revert":"%x"`, ac.revert)
} else if outStr != nil {
resultPiece = fmt.Sprintf(`,"result":%s`, *outStr)
diff --git a/hmy/tracers/block_tracer_storage.go b/hmy/tracers/block_tracer_storage.go
index 2377102734..86829ea365 100644
--- a/hmy/tracers/block_tracer_storage.go
+++ b/hmy/tracers/block_tracer_storage.go
@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/crypto/hash"
+ "github.com/holiman/uint256"
)
type ActionStorage struct {
@@ -40,7 +41,7 @@ func (storage *ActionStorage) appendByte(byt byte) {
func (storage *ActionStorage) appendFixed(data []byte) {
storage.TraceData = append(storage.TraceData, data...)
}
-func (storage *ActionStorage) appendNumber(num *big.Int) {
+func (storage *ActionStorage) appendNumber(num *uint256.Int) {
bytes, _ := rlp.EncodeToBytes(num)
storage.appendByte(uint8(len(bytes)))
storage.appendFixed(bytes)
diff --git a/hmy/tracers/block_tracer_test.go b/hmy/tracers/block_tracer_test.go
index 5589546801..b56a96b0f5 100644
--- a/hmy/tracers/block_tracer_test.go
+++ b/hmy/tracers/block_tracer_test.go
@@ -4,9 +4,9 @@ import (
"bytes"
"encoding/json"
"errors"
+ "fmt"
"math/big"
"strconv"
-
"strings"
"testing"
@@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/core/vm"
"github.com/harmony-one/harmony/internal/utils"
+ "github.com/holiman/uint256"
)
var TestJsonsMock = []byte(`{"14054302":[{"blockNumber":14054302,"blockHash":"0x04d7a0d62d3211151db0dadcaebcb1686c4a3df0e551a00c023c651546293975","transactionHash":"0xce49e42e0fbd37a0cfd08c2da3f1acc371ddbc02c428afa123a43663e57953d7","transactionPosition":0,"subtraces":0,"traceAddress":[0],"type":"suicide","action":{"refundAddress":"0x12e49d93588e0056bd25530c3b1e8aac68f4b70a","balance":"0x0","address":"0x7006c42d6fa41844baa53b0388f9542e634cf55a"},"result":null}],"14833359":[{"blockNumber":14833359,"blockHash":"0x6d6660f3d042a145c7f95c408f28cbf036a18eaf603161c2c00ca3f6041d8b52","transactionHash":"0x9fd0daef346c72d51f7482ddc9a466caf52fa6a116ed13ee0c003e57e632b7c0","transactionPosition":0,"subtraces":0,"traceAddress":[],"type":"create","action":{"from":"0x8520021f89450394244cd4abda4cfe2f1b0ef61c","gas":"0x1017d","init":"0x608060405234801561001057600080fd5b50610149806100206000396000f3fe6080604052600436106100295760003560e01c80630c2ad69c1461002e57806315d55b281461007a575b600080fd5b6100646004803603604081101561004457600080fd5b810190808035906020019092919080359060200190929190505050610091565b6040518082815260200191505060405180910390f35b34801561008657600080fd5b5061008f6100a5565b005b600081838161009c57fe5b04905092915050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f68656c6c6f00000000000000000000000000000000000000000000000000000081525060200191505060405180910390fdfea26469706673582212202f9958b958267c4ed653e54dc0161cfb9b772209cbe086f4a9ac3d967f22f09564736f6c634300060c0033","value":"0x0"},"result":{"address":"0xf29fcf3a375ce5dd1c58f0e8a584ab5d782cc12b","code":"0x6080604052600436106100295760003560e01c80630c2ad69c1461002e57806315d55b281461007a575b600080fd5b6100646004803603604081101561004457600080fd5b810190808035906020019092919080359060200190929190505050610091565b6040518082815260200191505060405180910390f35b34801561008657600080fd5b5061008f6100a5565b005b600081838161009c57fe5b04905092915050565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260058152602001807f68656c6c6f00000000000000000000000000000000000000000000000000000081525060200191505060405180910390fdfea26469706673582212202f9958b958267c4ed653e54dc0161cfb9b772209cbe086f4a9ac3d967f22f09564736f6c634300060c0033","gasUsed":"0x1017d"}},{"blockNumber":14833359,"blockHash":"0x6d6660f3d042a145c7f95c408f28cbf036a18eaf603161c2c00ca3f6041d8b52","transactionHash":"0xc3b81fa2f6786ffd11a588b9d951a39adb46b6e29abad819b0cb09ee32ea7072","transactionPosition":1,"subtraces":2,"traceAddress":[],"type":"call","action":{"callType":"call","value":"0x0","to":"0x4596817192fbbf0142c576ed3e7cfc0e8f40bbbe","gas":"0x2b71c","from":"0x87946ddc76a4c0a75c8ca1f63dffd0612ae6458c","input":"0x1801fbe5aebcf6e3d785238603dd88bb43cbdfcfeb51c95b570113ee65d2f9271d3b59510000000dcdf493a5e1610e23c037bc4c4e04ab9a6d8fe9d0d462ecd8d45643ac"},"result":{"output":"0x0000000000000000000000000000000000000000000000000000000000000001","gasUsed":"0x13c58"}}]}`)
@@ -95,7 +96,12 @@ func initFromJson(ts *TraceBlockStorage, bytes []byte) {
}
ac.from = callAc.From
ac.to = callAc.To
- ac.value, _ = new(big.Int).SetString(callAc.Value[2:], 16)
+ ac.value = new(uint256.Int)
+ _ = ac.value.SetFromHex(callAc.Value[2:]) //, 16)
+ _prev, _ := new(big.Int).SetString(callAc.Value[2:], 16)
+ if _prev.Uint64() != ac.value.Uint64() {
+ panic(fmt.Sprintf("big int set from hex failed, expected be equal %s and %s", _prev.String(), ac.value.String()))
+ }
ac.gas, _ = strconv.ParseUint(callAc.Gas, 0, 64)
ac.input = utils.FromHex(callAc.Input)
@@ -125,7 +131,12 @@ func initFromJson(ts *TraceBlockStorage, bytes []byte) {
panic(err)
}
ac.from = callAc.From
- ac.value, _ = new(big.Int).SetString(callAc.Value[2:], 16)
+ ac.value = new(uint256.Int)
+ _ = ac.value.SetFromHex(callAc.Value[2:]) //, 16)
+ _prev, _ := new(big.Int).SetString(callAc.Value[2:], 16)
+ if _prev.Uint64() != ac.value.Uint64() {
+ panic(fmt.Sprintf("big int set from hex failed, expected be equal, %s and %s", _prev.String(), ac.value.String()))
+ }
ac.gas, _ = strconv.ParseUint(callAc.Gas, 0, 64)
ac.input = utils.FromHex(callAc.Init)
@@ -145,7 +156,14 @@ func initFromJson(ts *TraceBlockStorage, bytes []byte) {
ac.from = callAc.Address
ac.to = callAc.RefundAddress
- ac.value, _ = new(big.Int).SetString(callAc.Balance[2:], 16)
+
+ ac.value = new(uint256.Int)
+ _ = ac.value.SetFromHex(callAc.Balance[2:]) //, 16)
+
+ _prev, _ := new(big.Int).SetString(callAc.Balance[2:], 16)
+ if _prev.Uint64() != ac.value.Uint64() {
+ panic(fmt.Sprintf("big int set from hex failed, expected be equal, %d and %d", _prev.Uint64(), ac.value.Uint64()))
+ }
}
ts.Hash = obj.BlockHash
ts.Number = obj.BlockNumber
@@ -188,7 +206,7 @@ func TestStorage(t *testing.T) {
t.Error(err)
}
if !bytes.Equal(jsonRaw, testJson) {
- t.Fatal("restroe failed!")
+ t.Fatalf("restroe failed!, expected: %s, got %s", testJson, jsonRaw)
}
block.ToDB(func(key, data []byte) {
for _, kv := range memDB {
@@ -248,7 +266,7 @@ func unmarshalAction(jsonstr string) (*action, error) {
ac.to = common.HexToAddress(actionInterface["to"])
ac.gas, _ = strconv.ParseUint(actionInterface["gas"], 0, 64)
ac.input = utils.FromHex(actionInterface["input"])
- ac.value = big.NewInt(0)
+ ac.value = uint256.NewInt(0)
ac.value.UnmarshalText([]byte(actionInterface["value"]))
switch strings.ToUpper(callType) {
case "CALL":
@@ -265,7 +283,7 @@ func unmarshalAction(jsonstr string) (*action, error) {
if initCode, exist := actionInterface["init"]; exist {
ac.op = vm.CREATE
ac.from = common.HexToAddress(actionInterface["from"])
- ac.value = big.NewInt(0)
+ ac.value = uint256.NewInt(0)
ac.value.UnmarshalText([]byte(actionInterface["value"]))
ac.gas, _ = strconv.ParseUint(actionInterface["gas"], 0, 64)
ac.input = utils.FromHex(initCode)
@@ -275,7 +293,7 @@ func unmarshalAction(jsonstr string) (*action, error) {
ac.op = vm.SELFDESTRUCT
ac.from = common.HexToAddress(actionInterface["address"])
ac.to = common.HexToAddress(refundAddress)
- ac.value = big.NewInt(0)
+ ac.value = uint256.NewInt(0)
ac.value.UnmarshalText([]byte(actionInterface["balance"]))
return &ac, nil
}
diff --git a/hmy/tracers/rosetta_block_tracer.go b/hmy/tracers/rosetta_block_tracer.go
index d1b573d7a9..d865e20536 100644
--- a/hmy/tracers/rosetta_block_tracer.go
+++ b/hmy/tracers/rosetta_block_tracer.go
@@ -20,6 +20,7 @@ import (
"math/big"
"github.com/harmony-one/harmony/core/vm"
+ "github.com/holiman/uint256"
)
type RosettaLogItem struct {
@@ -41,7 +42,7 @@ type RosettaBlockTracer struct {
func (rbt *RosettaBlockTracer) formatAction(depth []int, parentErr error, ac *action) *RosettaLogItem {
val := ac.value
if val == nil {
- val = big.NewInt(0)
+ val = uint256.NewInt(0)
}
return &RosettaLogItem{
@@ -51,7 +52,7 @@ func (rbt *RosettaBlockTracer) formatAction(depth []int, parentErr error, ac *ac
Depth: depth,
From: &vm.RosettaLogAddressItem{Account: &ac.from},
To: &vm.RosettaLogAddressItem{Account: &ac.to},
- Value: val,
+ Value: val.ToBig(),
}
}
diff --git a/hmy/tracers/tracer.go b/hmy/tracers/tracer.go
index bc349a5147..2498e348ca 100644
--- a/hmy/tracers/tracer.go
+++ b/hmy/tracers/tracer.go
@@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
return new(big.Int)
}
- return sw.stack.Data()[len(sw.stack.Data())-idx-1]
+ return sw.stack.Data()[len(sw.stack.Data())-idx-1].ToBig()
}
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
@@ -543,17 +543,17 @@ func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) (vm.HookAfter, error) {
+func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, returnStack *vm.ReturnStack, bts []byte, contract *vm.Contract, depth int, err error) error {
if jst.err == nil {
// Initialize the context if it wasn't done yet
if !jst.inited {
- jst.ctx["block"] = env.BlockNumber.Uint64()
+ jst.ctx["block"] = env.Context.BlockNumber.Uint64()
jst.inited = true
}
// If tracing was interrupted, set the error and stop
if atomic.LoadUint32(&jst.interrupt) > 0 {
jst.err = jst.reason
- return nil, nil
+ return nil
}
jst.opWrapper.op = op
jst.stackWrapper.stack = stack
@@ -577,12 +577,12 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
jst.err = wrapError("step", err)
}
}
- return nil, nil
+ return nil
}
// CaptureFault implements the Tracer interface to trace an execution fault
// while running an opcode.
-func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, returnStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
if jst.err == nil {
// Apart from the error, everything matches the previous invocation
jst.errorValue = new(string)
diff --git a/internal/params/config.go b/internal/params/config.go
index 62d55d83f5..4dd018e648 100644
--- a/internal/params/config.go
+++ b/internal/params/config.go
@@ -328,6 +328,7 @@ var (
MaxRateEpoch: EpochTBD,
DevnetExternalEpoch: EpochTBD,
TestnetExternalEpoch: EpochTBD,
+ EIP1559Epoch: big.NewInt(1),
IsOneSecondEpoch: big.NewInt(4),
}
@@ -381,6 +382,7 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
+ big.NewInt(0),
}
// TestChainConfig ...
@@ -433,6 +435,7 @@ var (
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
+ big.NewInt(0),
}
// TestRules ...
@@ -615,6 +618,9 @@ type ChainConfig struct {
// if crosslink are not sent for an entire epoch signed and toSign will be 0 and 0. when that happen, next epoch there will no shard 1 validator elected in the committee.
HIP32Epoch *big.Int `json:"hip32-epoch,omitempty"`
+ // HIP33Epoch: effectiveGasPrice+block v4+evm opcode BASEFEE
+ EIP1559Epoch *big.Int `json:"eip1559-epoch,omitempty"`
+
IsOneSecondEpoch *big.Int `json:"is-one-second-epoch,omitempty"`
}
@@ -909,6 +915,20 @@ func (c *ChainConfig) IsOneEpochBeforeHIP30(epoch *big.Int) bool {
return new(big.Int).Sub(c.HIP30Epoch, epoch).Cmp(common.Big1) == 0
}
+func (c *ChainConfig) IsLondon(epoch *big.Int) bool {
+ return false
+}
+
+// BaseFeeChangeDenominator
+func (c *ChainConfig) BaseFeeChangeDenominator() uint64 {
+ return DefaultBaseFeeChangeDenominator
+}
+
+// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have.
+func (c *ChainConfig) ElasticityMultiplier() uint64 {
+ return DefaultElasticityMultiplier
+}
+
// UpdateEthChainIDByShard update the ethChainID based on shard ID.
func UpdateEthChainIDByShard(shardID uint32) {
once.Do(func() {
diff --git a/internal/params/protocol_params.go b/internal/params/protocol_params.go
index e67bb29ae3..d6e3aed3fc 100644
--- a/internal/params/protocol_params.go
+++ b/internal/params/protocol_params.go
@@ -62,12 +62,9 @@ const (
// NetSstoreDirtyGas ...
NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty.
- // NetSstoreClearRefund ...
- NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
- // NetSstoreResetRefund ...
- NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
- // NetSstoreResetClearRefund ...
- NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
+ SstoreSetGasEIP2200 uint64 = 20000 // Once per SSTORE operation from clean zero to non-zero
+ SstoreResetGasEIP2200 uint64 = 5000 // Once per SSTORE operation from clean non-zero to something else
+ SstoreClearsScheduleRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
// SstoreSentryGasEIP2200 ...
SstoreSentryGasEIP2200 uint64 = 2300 // Minimum gas required to be present for an SSTORE call, not consumed
@@ -86,6 +83,10 @@ const (
// SstoreClearRefundEIP2200 ...
SstoreClearRefundEIP2200 uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
+ NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
+ NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
+ NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
+
// todo(sun): implement eip2929
ColdAccountAccessCostEIP2929 uint64 = 2600 // COLD_ACCOUNT_ACCESS_COST
ColdSloadCostEIP2929 uint64 = 2100 // COLD_SLOAD_COST
@@ -126,6 +127,10 @@ const (
// TxDataNonZeroGasEIP2028 ...
TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul)
+ DefaultBaseFeeChangeDenominator = 8 // Bounds the amount the base fee can change between blocks.
+ DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have.
+ InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks.
+
// 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)
@@ -137,6 +142,7 @@ const (
SloadGasFrontier uint64 = 50
SloadGasEIP150 uint64 = 200
SloadGasEIP1884 uint64 = 800 // Cost of SLOAD after EIP 1884 (part of Istanbul)
+ SloadGasEIP2200 uint64 = 800 // Cost of SLOAD after EIP 2200 (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)
diff --git a/node/harmony/worker/worker.go b/node/harmony/worker/worker.go
index 5d94e24112..a644be84af 100644
--- a/node/harmony/worker/worker.go
+++ b/node/harmony/worker/worker.go
@@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/block"
blockfactory "github.com/harmony-one/harmony/block/factory"
+ "github.com/harmony-one/harmony/consensus/misc/eip1559"
"github.com/harmony-one/harmony/consensus/reward"
"github.com/harmony-one/harmony/core"
"github.com/harmony-one/harmony/core/state"
@@ -368,6 +369,11 @@ func (w *Worker) UpdateCurrent() (Environment, error) {
Time(big.NewInt(timestamp)).
ShardID(w.chain.ShardID()).
Header()
+
+ if w.chain.Config().IsLondon(header.Epoch()) {
+ header.SetBaseFee(eip1559.CalcBaseFee(w.chain.Config(), parent))
+ }
+
return w.makeCurrent(parent, header)
}
diff --git a/p2p/gater.go b/p2p/gater.go
deleted file mode 100644
index 8b63b9fb73..0000000000
--- a/p2p/gater.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package p2p
-
-import (
- libp2p_dht "github.com/libp2p/go-libp2p-kad-dht"
- "github.com/libp2p/go-libp2p/core/connmgr"
- "github.com/libp2p/go-libp2p/core/control"
- "github.com/libp2p/go-libp2p/core/network"
- "github.com/libp2p/go-libp2p/core/peer"
- ma "github.com/multiformats/go-multiaddr"
-)
-
-type Gater struct {
- isGating bool
-}
-
-func NewGater(disablePrivateIPScan bool) connmgr.ConnectionGater {
- return Gater{
- isGating: disablePrivateIPScan,
- }
-}
-
-func (gater Gater) InterceptPeerDial(p peer.ID) (allow bool) {
- return true
-}
-
-// Blocking connections at this stage is typical for address filtering.
-func (gater Gater) InterceptAddrDial(p peer.ID, m ma.Multiaddr) (allow bool) {
- if gater.isGating {
- return libp2p_dht.PublicQueryFilter(nil, peer.AddrInfo{
- ID: p,
- Addrs: []ma.Multiaddr{m},
- })
- } else {
- return true
- }
-}
-
-func (gater Gater) InterceptAccept(network.ConnMultiaddrs) (allow bool) {
- return true
-}
-
-func (gater Gater) InterceptSecured(network.Direction, peer.ID, network.ConnMultiaddrs) (allow bool) {
- return true
-}
-
-// NOTE: the go-libp2p implementation currently IGNORES the disconnect reason.
-func (gater Gater) InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason) {
- return true, 0
-}
diff --git a/p2p/gating/blocking.go b/p2p/gating/blocking.go
index a156446752..6ab416870e 100644
--- a/p2p/gating/blocking.go
+++ b/p2p/gating/blocking.go
@@ -1,36 +1,31 @@
package gating
import (
- "net"
-
- ds "github.com/ipfs/go-datastore"
- "github.com/libp2p/go-libp2p/core/connmgr"
+ libp2p_dht "github.com/libp2p/go-libp2p-kad-dht"
"github.com/libp2p/go-libp2p/core/peer"
- "github.com/libp2p/go-libp2p/p2p/net/conngater"
+ ma "github.com/multiformats/go-multiaddr"
)
-type BlockingConnectionGater interface {
- connmgr.ConnectionGater
-
- // BlockPeer adds a peer to the set of blocked peers.
- // Note: active connections to the peer are not automatically closed.
- BlockPeer(p peer.ID) error
- UnblockPeer(p peer.ID) error
- ListBlockedPeers() []peer.ID
-
- // BlockAddr adds an IP address to the set of blocked addresses.
- // Note: active connections to the IP address are not automatically closed.
- BlockAddr(ip net.IP) error
- UnblockAddr(ip net.IP) error
- ListBlockedAddrs() []net.IP
+// ExpiryConnectionGater enhances a ExtendedConnectionGater by implementing ban-expiration
+type BlockingConnectionGater struct {
+ ExtendedConnectionGater
+ isGating bool
+}
- // BlockSubnet adds an IP subnet to the set of blocked addresses.
- // Note: active connections to the IP subnet are not automatically closed.
- BlockSubnet(ipnet *net.IPNet) error
- UnblockSubnet(ipnet *net.IPNet) error
- ListBlockedSubnets() []*net.IPNet
+func AddBlocking(gater ExtendedConnectionGater, disablePrivateIPScan bool) *BlockingConnectionGater {
+ return &BlockingConnectionGater{
+ ExtendedConnectionGater: gater,
+ isGating: disablePrivateIPScan,
+ }
}
-func NewBlockingConnectionGater(store ds.Batching) (BlockingConnectionGater, error) {
- return conngater.NewBasicConnectionGater(store)
+// Blocking connections at this stage is typical for address filtering.
+func (g *BlockingConnectionGater) InterceptAddrDial(p peer.ID, m ma.Multiaddr) (allow bool) {
+ if g.isGating {
+ return libp2p_dht.PublicQueryFilter(nil, peer.AddrInfo{
+ ID: p,
+ Addrs: []ma.Multiaddr{m},
+ })
+ }
+ return true
}
diff --git a/p2p/gating/expiry.go b/p2p/gating/expiry.go
index d221845b82..ff9d489f26 100644
--- a/p2p/gating/expiry.go
+++ b/p2p/gating/expiry.go
@@ -24,23 +24,23 @@ type ExpiryStore interface {
store.PeerBanStore
}
-// ExpiryConnectionGater enhances a BlockingConnectionGater by implementing ban-expiration
+// ExpiryConnectionGater enhances a ExtendedConnectionGater by implementing ban-expiration
type ExpiryConnectionGater struct {
- BlockingConnectionGater
+ ExtendedConnectionGater
store ExpiryStore
clock clock.Clock
}
-func AddBanExpiry(gater BlockingConnectionGater, store ExpiryStore, clock clock.Clock) *ExpiryConnectionGater {
+func AddBanExpiry(gater ExtendedConnectionGater, store ExpiryStore, clock clock.Clock) *ExpiryConnectionGater {
return &ExpiryConnectionGater{
- BlockingConnectionGater: gater,
+ ExtendedConnectionGater: gater,
store: store,
clock: clock,
}
}
func (g *ExpiryConnectionGater) UnblockPeer(p peer.ID) error {
- if err := g.BlockingConnectionGater.UnblockPeer(p); err != nil {
+ if err := g.ExtendedConnectionGater.UnblockPeer(p); err != nil {
utils.Logger().Warn().
Str("method", "UnblockPeer").
Str("peer_id", p.String()).
@@ -131,7 +131,7 @@ func (g *ExpiryConnectionGater) addrBanExpiryCheck(ma multiaddr.Multiaddr) (allo
}
func (g *ExpiryConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
- if !g.BlockingConnectionGater.InterceptPeerDial(p) {
+ if !g.ExtendedConnectionGater.InterceptPeerDial(p) {
return false
}
peerBan := g.peerBanExpiryCheck(p)
@@ -145,7 +145,7 @@ func (g *ExpiryConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
}
func (g *ExpiryConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) {
- if !g.BlockingConnectionGater.InterceptAddrDial(id, ma) {
+ if !g.ExtendedConnectionGater.InterceptAddrDial(id, ma) {
return false
}
peerBan := g.peerBanExpiryCheck(id)
@@ -170,7 +170,7 @@ func (g *ExpiryConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multi
}
func (g *ExpiryConnectionGater) InterceptAccept(mas network.ConnMultiaddrs) (allow bool) {
- if !g.BlockingConnectionGater.InterceptAccept(mas) {
+ if !g.ExtendedConnectionGater.InterceptAccept(mas) {
return false
}
addrBan := g.addrBanExpiryCheck(mas.RemoteMultiaddr())
@@ -187,7 +187,7 @@ func (g *ExpiryConnectionGater) InterceptSecured(direction network.Direction, id
if direction == network.DirOutbound {
return true
}
- if !g.BlockingConnectionGater.InterceptSecured(direction, id, mas) {
+ if !g.ExtendedConnectionGater.InterceptSecured(direction, id, mas) {
return false
}
peerBan := g.peerBanExpiryCheck(id)
diff --git a/p2p/gating/gater.go b/p2p/gating/gater.go
new file mode 100644
index 0000000000..bc3135e7ef
--- /dev/null
+++ b/p2p/gating/gater.go
@@ -0,0 +1,36 @@
+package gating
+
+import (
+ "net"
+
+ ds "github.com/ipfs/go-datastore"
+ "github.com/libp2p/go-libp2p/core/connmgr"
+ "github.com/libp2p/go-libp2p/core/peer"
+ "github.com/libp2p/go-libp2p/p2p/net/conngater"
+)
+
+type ExtendedConnectionGater interface {
+ connmgr.ConnectionGater
+
+ // BlockPeer adds a peer to the set of blocked peers.
+ // Note: active connections to the peer are not automatically closed.
+ BlockPeer(p peer.ID) error
+ UnblockPeer(p peer.ID) error
+ ListBlockedPeers() []peer.ID
+
+ // BlockAddr adds an IP address to the set of blocked addresses.
+ // Note: active connections to the IP address are not automatically closed.
+ BlockAddr(ip net.IP) error
+ UnblockAddr(ip net.IP) error
+ ListBlockedAddrs() []net.IP
+
+ // BlockSubnet adds an IP subnet to the set of blocked addresses.
+ // Note: active connections to the IP subnet are not automatically closed.
+ BlockSubnet(ipnet *net.IPNet) error
+ UnblockSubnet(ipnet *net.IPNet) error
+ ListBlockedSubnets() []*net.IPNet
+}
+
+func NewExtendedConnectionGater(store ds.Batching) (ExtendedConnectionGater, error) {
+ return conngater.NewBasicConnectionGater(store)
+}
diff --git a/p2p/gater_test.go b/p2p/gating/gater_test.go
similarity index 70%
rename from p2p/gater_test.go
rename to p2p/gating/gater_test.go
index 1a5ee37de1..40616ae972 100644
--- a/p2p/gater_test.go
+++ b/p2p/gating/gater_test.go
@@ -1,17 +1,26 @@
-package p2p
+package gating
import (
"testing"
+ ds "github.com/ipfs/go-datastore"
+ dsSync "github.com/ipfs/go-datastore/sync"
ma "github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+func createTestDataStore() ds.Batching {
+ return dsSync.MutexWrap(ds.NewMapDatastore())
+}
func TestGaterBlocking(t *testing.T) {
- gater := NewGater(true)
+ store := createTestDataStore()
+ gater, err := NewExtendedConnectionGater(store)
+ assert.Nil(t, err, "%s", err)
require.NotNil(t, &gater, "%s", &gater)
+ gater = AddBlocking(gater, true)
+
public, err := ma.NewMultiaddr("/ip4/1.1.1.1/udp/53")
assert.Nil(t, err, "%s", err)
allowed := gater.InterceptAddrDial("somePeer", public)
@@ -24,7 +33,9 @@ func TestGaterBlocking(t *testing.T) {
}
func TestGaterNotBlocking(t *testing.T) {
- gater := NewGater(false)
+ store := createTestDataStore()
+ gater, err := NewExtendedConnectionGater(store)
+ assert.Nil(t, err, "%s", err)
require.NotNil(t, &gater, "%s", &gater)
public, err := ma.NewMultiaddr("/ip4/1.1.1.1/udp/53")
diff --git a/p2p/gating/metrics.go b/p2p/gating/metrics.go
index 3a2a1cf775..4e34e96493 100644
--- a/p2p/gating/metrics.go
+++ b/p2p/gating/metrics.go
@@ -12,30 +12,30 @@ type ConnectionGaterMetrics interface {
}
type MeteredConnectionGater struct {
- BlockingConnectionGater
+ ExtendedConnectionGater
//m ConnectionGaterMetrics
}
-func AddMetering(gater BlockingConnectionGater) *MeteredConnectionGater {
- return &MeteredConnectionGater{BlockingConnectionGater: gater}
+func AddMetering(gater ExtendedConnectionGater) *MeteredConnectionGater {
+ return &MeteredConnectionGater{ExtendedConnectionGater: gater}
}
func (g *MeteredConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
- allow = g.BlockingConnectionGater.InterceptPeerDial(p)
+ allow = g.ExtendedConnectionGater.InterceptPeerDial(p)
return allow
}
func (g *MeteredConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) {
- allow = g.BlockingConnectionGater.InterceptAddrDial(id, ma)
+ allow = g.ExtendedConnectionGater.InterceptAddrDial(id, ma)
return allow
}
func (g *MeteredConnectionGater) InterceptAccept(mas network.ConnMultiaddrs) (allow bool) {
- allow = g.BlockingConnectionGater.InterceptAccept(mas)
+ allow = g.ExtendedConnectionGater.InterceptAccept(mas)
return allow
}
func (g *MeteredConnectionGater) InterceptSecured(dir network.Direction, id peer.ID, mas network.ConnMultiaddrs) (allow bool) {
- allow = g.BlockingConnectionGater.InterceptSecured(dir, id, mas)
+ allow = g.ExtendedConnectionGater.InterceptSecured(dir, id, mas)
return allow
}
diff --git a/p2p/gating/mocks/BlockingConnectionGater.go b/p2p/gating/mocks/BlockingConnectionGater.go
index ade24d40c6..fa3dee5f69 100644
--- a/p2p/gating/mocks/BlockingConnectionGater.go
+++ b/p2p/gating/mocks/BlockingConnectionGater.go
@@ -16,8 +16,8 @@ import (
peer "github.com/libp2p/go-libp2p/core/peer"
)
-// BlockingConnectionGater is an autogenerated mock type for the BlockingConnectionGater type
-type BlockingConnectionGater struct {
+// ExtendedConnectionGater is an autogenerated mock type for the ExtendedConnectionGater type
+type ExtendedConnectionGater struct {
mock.Mock
}
@@ -25,12 +25,12 @@ type BlockingConnectionGater_Expecter struct {
mock *mock.Mock
}
-func (_m *BlockingConnectionGater) EXPECT() *BlockingConnectionGater_Expecter {
+func (_m *ExtendedConnectionGater) EXPECT() *BlockingConnectionGater_Expecter {
return &BlockingConnectionGater_Expecter{mock: &_m.Mock}
}
// BlockAddr provides a mock function with given fields: ip
-func (_m *BlockingConnectionGater) BlockAddr(ip net.IP) error {
+func (_m *ExtendedConnectionGater) BlockAddr(ip net.IP) error {
ret := _m.Called(ip)
if len(ret) == 0 {
@@ -76,7 +76,7 @@ func (_c *BlockingConnectionGater_BlockAddr_Call) RunAndReturn(run func(net.IP)
}
// BlockPeer provides a mock function with given fields: p
-func (_m *BlockingConnectionGater) BlockPeer(p peer.ID) error {
+func (_m *ExtendedConnectionGater) BlockPeer(p peer.ID) error {
ret := _m.Called(p)
if len(ret) == 0 {
@@ -122,7 +122,7 @@ func (_c *BlockingConnectionGater_BlockPeer_Call) RunAndReturn(run func(peer.ID)
}
// BlockSubnet provides a mock function with given fields: ipnet
-func (_m *BlockingConnectionGater) BlockSubnet(ipnet *net.IPNet) error {
+func (_m *ExtendedConnectionGater) BlockSubnet(ipnet *net.IPNet) error {
ret := _m.Called(ipnet)
if len(ret) == 0 {
@@ -168,7 +168,7 @@ func (_c *BlockingConnectionGater_BlockSubnet_Call) RunAndReturn(run func(*net.I
}
// InterceptAccept provides a mock function with given fields: _a0
-func (_m *BlockingConnectionGater) InterceptAccept(_a0 network.ConnMultiaddrs) bool {
+func (_m *ExtendedConnectionGater) InterceptAccept(_a0 network.ConnMultiaddrs) bool {
ret := _m.Called(_a0)
if len(ret) == 0 {
@@ -214,7 +214,7 @@ func (_c *BlockingConnectionGater_InterceptAccept_Call) RunAndReturn(run func(ne
}
// InterceptAddrDial provides a mock function with given fields: _a0, _a1
-func (_m *BlockingConnectionGater) InterceptAddrDial(_a0 peer.ID, _a1 multiaddr.Multiaddr) bool {
+func (_m *ExtendedConnectionGater) InterceptAddrDial(_a0 peer.ID, _a1 multiaddr.Multiaddr) bool {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
@@ -261,7 +261,7 @@ func (_c *BlockingConnectionGater_InterceptAddrDial_Call) RunAndReturn(run func(
}
// InterceptPeerDial provides a mock function with given fields: p
-func (_m *BlockingConnectionGater) InterceptPeerDial(p peer.ID) bool {
+func (_m *ExtendedConnectionGater) InterceptPeerDial(p peer.ID) bool {
ret := _m.Called(p)
if len(ret) == 0 {
@@ -307,7 +307,7 @@ func (_c *BlockingConnectionGater_InterceptPeerDial_Call) RunAndReturn(run func(
}
// InterceptSecured provides a mock function with given fields: _a0, _a1, _a2
-func (_m *BlockingConnectionGater) InterceptSecured(_a0 network.Direction, _a1 peer.ID, _a2 network.ConnMultiaddrs) bool {
+func (_m *ExtendedConnectionGater) InterceptSecured(_a0 network.Direction, _a1 peer.ID, _a2 network.ConnMultiaddrs) bool {
ret := _m.Called(_a0, _a1, _a2)
if len(ret) == 0 {
@@ -355,7 +355,7 @@ func (_c *BlockingConnectionGater_InterceptSecured_Call) RunAndReturn(run func(n
}
// InterceptUpgraded provides a mock function with given fields: _a0
-func (_m *BlockingConnectionGater) InterceptUpgraded(_a0 network.Conn) (bool, control.DisconnectReason) {
+func (_m *ExtendedConnectionGater) InterceptUpgraded(_a0 network.Conn) (bool, control.DisconnectReason) {
ret := _m.Called(_a0)
if len(ret) == 0 {
@@ -411,7 +411,7 @@ func (_c *BlockingConnectionGater_InterceptUpgraded_Call) RunAndReturn(run func(
}
// ListBlockedAddrs provides a mock function with given fields:
-func (_m *BlockingConnectionGater) ListBlockedAddrs() []net.IP {
+func (_m *ExtendedConnectionGater) ListBlockedAddrs() []net.IP {
ret := _m.Called()
if len(ret) == 0 {
@@ -458,7 +458,7 @@ func (_c *BlockingConnectionGater_ListBlockedAddrs_Call) RunAndReturn(run func()
}
// ListBlockedPeers provides a mock function with given fields:
-func (_m *BlockingConnectionGater) ListBlockedPeers() []peer.ID {
+func (_m *ExtendedConnectionGater) ListBlockedPeers() []peer.ID {
ret := _m.Called()
if len(ret) == 0 {
@@ -505,7 +505,7 @@ func (_c *BlockingConnectionGater_ListBlockedPeers_Call) RunAndReturn(run func()
}
// ListBlockedSubnets provides a mock function with given fields:
-func (_m *BlockingConnectionGater) ListBlockedSubnets() []*net.IPNet {
+func (_m *ExtendedConnectionGater) ListBlockedSubnets() []*net.IPNet {
ret := _m.Called()
if len(ret) == 0 {
@@ -552,7 +552,7 @@ func (_c *BlockingConnectionGater_ListBlockedSubnets_Call) RunAndReturn(run func
}
// UnblockAddr provides a mock function with given fields: ip
-func (_m *BlockingConnectionGater) UnblockAddr(ip net.IP) error {
+func (_m *ExtendedConnectionGater) UnblockAddr(ip net.IP) error {
ret := _m.Called(ip)
if len(ret) == 0 {
@@ -598,7 +598,7 @@ func (_c *BlockingConnectionGater_UnblockAddr_Call) RunAndReturn(run func(net.IP
}
// UnblockPeer provides a mock function with given fields: p
-func (_m *BlockingConnectionGater) UnblockPeer(p peer.ID) error {
+func (_m *ExtendedConnectionGater) UnblockPeer(p peer.ID) error {
ret := _m.Called(p)
if len(ret) == 0 {
@@ -644,7 +644,7 @@ func (_c *BlockingConnectionGater_UnblockPeer_Call) RunAndReturn(run func(peer.I
}
// UnblockSubnet provides a mock function with given fields: ipnet
-func (_m *BlockingConnectionGater) UnblockSubnet(ipnet *net.IPNet) error {
+func (_m *ExtendedConnectionGater) UnblockSubnet(ipnet *net.IPNet) error {
ret := _m.Called(ipnet)
if len(ret) == 0 {
@@ -689,13 +689,13 @@ func (_c *BlockingConnectionGater_UnblockSubnet_Call) RunAndReturn(run func(*net
return _c
}
-// NewBlockingConnectionGater creates a new instance of BlockingConnectionGater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// NewBlockingConnectionGater creates a new instance of ExtendedConnectionGater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewBlockingConnectionGater(t interface {
mock.TestingT
Cleanup(func())
-}) *BlockingConnectionGater {
- mock := &BlockingConnectionGater{}
+}) *ExtendedConnectionGater {
+ mock := &ExtendedConnectionGater{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
diff --git a/p2p/gating/scoring.go b/p2p/gating/scoring.go
index 90bf188c8c..c0759a3184 100644
--- a/p2p/gating/scoring.go
+++ b/p2p/gating/scoring.go
@@ -13,13 +13,13 @@ type Scores interface {
// ScoringConnectionGater enhances a ConnectionGater by enforcing a minimum score for peer connections
type ScoringConnectionGater struct {
- BlockingConnectionGater
+ ExtendedConnectionGater
scores Scores
minScore float64
}
-func AddScoring(gater BlockingConnectionGater, scores Scores, minScore float64) *ScoringConnectionGater {
- return &ScoringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore}
+func AddScoring(gater ExtendedConnectionGater, scores Scores, minScore float64) *ScoringConnectionGater {
+ return &ScoringConnectionGater{ExtendedConnectionGater: gater, scores: scores, minScore: minScore}
}
func (g *ScoringConnectionGater) checkScore(p peer.ID) (allow bool, score float64) {
@@ -32,7 +32,7 @@ func (g *ScoringConnectionGater) checkScore(p peer.ID) (allow bool, score float6
}
func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
- if !g.BlockingConnectionGater.InterceptPeerDial(p) {
+ if !g.ExtendedConnectionGater.InterceptPeerDial(p) {
return false
}
check, score := g.checkScore(p)
@@ -43,7 +43,7 @@ func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {
}
func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) {
- if !g.BlockingConnectionGater.InterceptAddrDial(id, ma) {
+ if !g.ExtendedConnectionGater.InterceptAddrDial(id, ma) {
return false
}
check, score := g.checkScore(id)
@@ -54,7 +54,7 @@ func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Mult
}
func (g *ScoringConnectionGater) InterceptSecured(dir network.Direction, id peer.ID, mas network.ConnMultiaddrs) (allow bool) {
- if !g.BlockingConnectionGater.InterceptSecured(dir, id, mas) {
+ if !g.ExtendedConnectionGater.InterceptSecured(dir, id, mas) {
return false
}
check, score := g.checkScore(id)
@@ -65,7 +65,7 @@ func (g *ScoringConnectionGater) InterceptSecured(dir network.Direction, id peer
}
func (g *ScoringConnectionGater) InterceptAccept(mas network.ConnMultiaddrs) (allow bool) {
- if !g.BlockingConnectionGater.InterceptAccept(mas) {
+ if !g.ExtendedConnectionGater.InterceptAccept(mas) {
return false
}
utils.Logger().Info().Str("multi_addr", mas.RemoteMultiaddr().String()).Msg("connection accepted")
diff --git a/p2p/host.go b/p2p/host.go
index 530446fbfa..db0948fbe4 100644
--- a/p2p/host.go
+++ b/p2p/host.go
@@ -181,11 +181,15 @@ func NewHost(cfg HostConfig) (Host, error) {
if err := ps.AddPubKey(peerID, pub); err != nil {
return nil, fmt.Errorf("failed to set up peerstore with pub key: %w", err)
}
- var connGtr gating.BlockingConnectionGater
- connGtr, err = gating.NewBlockingConnectionGater(datastore)
+ var connGtr gating.ExtendedConnectionGater
+ connGtr, err = gating.NewExtendedConnectionGater(datastore)
if err != nil {
return nil, fmt.Errorf("failed to open connection gater: %w", err)
}
+ if cfg.DisablePrivateIPScan {
+ // Prevent dialing of public addresses
+ connGtr = gating.AddBlocking(connGtr, cfg.DisablePrivateIPScan)
+ }
connGtr = gating.AddBanExpiry(connGtr, ps, clock.SystemClock)
connGtr = gating.AddMetering(connGtr)
@@ -302,12 +306,6 @@ func NewHost(cfg HostConfig) (Host, error) {
p2pHostConfig = append(p2pHostConfig, libp2p.ForceReachabilityPublic())
}
- // TODO: this should be moved to main gater
- if cfg.DisablePrivateIPScan {
- // Prevent dialing of public addresses
- p2pHostConfig = append(p2pHostConfig, libp2p.ConnectionGater(NewGater(cfg.DisablePrivateIPScan)))
- }
-
// create p2p host
p2pHost, err := libp2p.New(p2pHostConfig...)
if err != nil {
diff --git a/rosetta/services/block.go b/rosetta/services/block.go
index d187f2ea6d..40c33c6a0d 100644
--- a/rosetta/services/block.go
+++ b/rosetta/services/block.go
@@ -318,7 +318,7 @@ func (s *BlockAPI) getTransactionTrace(
var blockError *types.Error
var foundResult []*tracers.RosettaLogItem
var tracer = "RosettaBlockTracer"
- err := s.hmy.ComputeTxEnvEachBlockWithoutApply(blk, defaultTraceReExec, func(txIndex int, tx *coreTypes.Transaction, msg core.Message, vmctx vm.Context, statedb *state.DB) bool {
+ err := s.hmy.ComputeTxEnvEachBlockWithoutApply(blk, defaultTraceReExec, func(txIndex int, tx *coreTypes.Transaction, msg core.Message, vmctx vm.BlockContext, statedb *state.DB) bool {
execResultInterface, err := s.hmy.TraceTx(ctx, msg, vmctx, statedb, &hmy.TraceConfig{
Tracer: &tracer,
LogConfig: &vm.LogConfig{
diff --git a/rpc/harmony/tracer.go b/rpc/harmony/tracer.go
index c8ab490ad0..5aba086b78 100644
--- a/rpc/harmony/tracer.go
+++ b/rpc/harmony/tracer.go
@@ -205,7 +205,7 @@ func (s *PublicTracerService) TraceCall(ctx context.Context, args CallArgs, bloc
// Execute the trace
msg := args.ToMessage(s.hmy.RPCGasCap)
- vmctx := core.NewEVMContext(msg, header, s.hmy.BlockChain, nil)
+ vmctx := core.NewEVMBlockContext(msg, header, s.hmy.BlockChain, nil)
// Apply overrides customization if required
if config != nil {
@@ -223,7 +223,6 @@ func (s *PublicTracerService) TraceCall(ctx context.Context, args CallArgs, bloc
config.Stateoverrides.Apply(statedb, precompiles)
}
}
-
// Trace the transaction and return
return s.hmy.TraceTx(ctx, msg, vmctx, statedb, config)
}