aboutsummaryrefslogtreecommitdiff
path: root/core/vm
diff options
context:
space:
mode:
Diffstat (limited to 'core/vm')
-rw-r--r--core/vm/common.go31
-rw-r--r--core/vm/contract.go36
-rw-r--r--core/vm/contracts.go487
-rw-r--r--core/vm/eips.go82
-rw-r--r--core/vm/gas.go10
-rw-r--r--core/vm/gas_table.go76
-rw-r--r--core/vm/gen_structlog.go26
-rw-r--r--core/vm/int_pool_verifier.go31
-rw-r--r--core/vm/int_pool_verifier_empty.go23
-rw-r--r--core/vm/intpool.go106
-rw-r--r--core/vm/logger.go189
-rw-r--r--core/vm/logger_json.go19
-rw-r--r--core/vm/memory.go9
-rw-r--r--core/vm/stack.go84
14 files changed, 869 insertions, 340 deletions
diff --git a/core/vm/common.go b/core/vm/common.go
index ead30fc..90ba4a4 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -17,15 +17,14 @@
package vm
import (
- "math/big"
-
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
+ "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 ed17402..915193d 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -19,7 +19,8 @@ package vm
import (
"math/big"
- "github.com/ava-labs/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// ContractRef is a reference to the contract's backing object
@@ -81,18 +82,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]
@@ -102,6 +128,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
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 54eab4e..9827bac 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -23,11 +23,14 @@ import (
"math/big"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
- "github.com/ava-labs/go-ethereum/crypto"
- "github.com/ava-labs/go-ethereum/crypto/blake2b"
- "github.com/ava-labs/go-ethereum/crypto/bn256"
+ "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/bls12381"
+ "github.com/ethereum/go-ethereum/crypto/bn256"
+
+ //lint:ignore SA1019 Needed for precompile
"golang.org/x/crypto/ripemd160"
)
@@ -75,13 +78,42 @@ var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{9}): &blake2F{},
}
+// PrecompiledContractsYoloV1 contains the default set of pre-compiled Ethereum
+// contracts used in the Yolo v1 test release.
+var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+ common.BytesToAddress([]byte{10}): &bls12381G1Add{},
+ common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
+ common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
+ common.BytesToAddress([]byte{13}): &bls12381G2Add{},
+ common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
+ common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
+ common.BytesToAddress([]byte{16}): &bls12381Pairing{},
+ common.BytesToAddress([]byte{17}): &bls12381MapG1{},
+ common.BytesToAddress([]byte{18}): &bls12381MapG2{},
+}
+
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
-func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
- gas := p.RequiredGas(input)
- if contract.UseGas(gas) {
- return p.Run(input)
+// It returns
+// - the returned bytes,
+// - the _remaining_ gas,
+// - any error that occurred
+func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+ gasCost := p.RequiredGas(input)
+ if suppliedGas < gasCost {
+ return nil, 0, ErrOutOfGas
}
- return nil, ErrOutOfGas
+ suppliedGas -= gasCost
+ output, err := p.Run(input)
+ return output, suppliedGas, err
}
// ECRECOVER implemented as a native contract.
@@ -106,8 +138,13 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
return nil, nil
}
+ // We must make sure not to modify the 'input', so placing the 'v' along with
+ // the signature needs to be done on a new allocation
+ sig := make([]byte, 65)
+ copy(sig, input[64:128])
+ sig[64] = v
// v needs to be at the end for libsecp256k1
- pubKey, err := crypto.Ecrecover(input[:32], append(input[64:128], v))
+ pubKey, err := crypto.Ecrecover(input[:32], sig)
// make sure the public key is a valid one
if err != nil {
return nil, nil
@@ -166,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
type bigModExp struct{}
var (
+ big0 = big.NewInt(0)
big1 = big.NewInt(1)
big4 = big.NewInt(4)
big8 = big.NewInt(8)
@@ -458,7 +496,7 @@ var (
)
func (c *blake2F) Run(input []byte) ([]byte, error) {
- // Make sure the input is valid (correct lenth and final flag)
+ // Make sure the input is valid (correct length and final flag)
if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
}
@@ -495,3 +533,428 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
}
return output, nil
}
+
+var (
+ errBLS12381InvalidInputLength = errors.New("invalid input length")
+ errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes")
+ errBLS12381G1PointSubgroup = errors.New("g1 point is not on correct subgroup")
+ errBLS12381G2PointSubgroup = errors.New("g2 point is not on correct subgroup")
+)
+
+// bls12381G1Add implements EIP-2537 G1Add precompile.
+type bls12381G1Add struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G1AddGas
+}
+
+func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G1Add precompile.
+ // > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
+ // > Output is an encoding of addition operation result - single G1 point (`128` bytes).
+ if len(input) != 256 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point p_0
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode G1 point p_1
+ if p1, err = g.DecodePoint(input[128:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ r := g.New()
+ g.Add(r, p0, p1)
+
+ // Encode the G1 point result into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1Mul implements EIP-2537 G1Mul precompile.
+type bls12381G1Mul struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G1MulGas
+}
+
+func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G1Mul precompile.
+ // > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
+ // > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
+ if len(input) != 160 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[128:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G1 point into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
+type bls12381G1MultiExp struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
+ // Calculate G1 point, scalar value pair length
+ k := len(input) / 160
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G1 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
+}
+
+func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G1MultiExp precompile.
+ // G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
+ // Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
+ k := len(input) / 160
+ if len(input) == 0 || len(input)%160 != 0 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG1, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ off := 160 * i
+ t0, t1, t2 := off, off+128, off+160
+ // Decode G1 point
+ if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ g.MultiExp(r, points, scalars)
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Add implements EIP-2537 G2Add precompile.
+type bls12381G2Add struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G2AddGas
+}
+
+func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G2Add precompile.
+ // > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
+ // > Output is an encoding of addition operation result - single G2 point (`256` bytes).
+ if len(input) != 512 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+ r := g.New()
+
+ // Decode G2 point p_0
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode G2 point p_1
+ if p1, err = g.DecodePoint(input[256:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ g.Add(r, p0, p1)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Mul implements EIP-2537 G2Mul precompile.
+type bls12381G2Mul struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G2MulGas
+}
+
+func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G2MUL precompile logic.
+ // > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
+ // > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
+ if len(input) != 288 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode G2 point
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[256:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
+type bls12381G2MultiExp struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
+ // Calculate G2 point, scalar value pair length
+ k := len(input) / 288
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G2 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
+}
+
+func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G2MultiExp precompile logic
+ // > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
+ // > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
+ k := len(input) / 288
+ if len(input) == 0 || len(input)%288 != 0 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG2, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ off := 288 * i
+ t0, t1, t2 := off, off+256, off+288
+ // Decode G1 point
+ if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ g.MultiExp(r, points, scalars)
+
+ // Encode the G2 point to 256 bytes.
+ return g.EncodePoint(r), nil
+}
+
+// bls12381Pairing implements EIP-2537 Pairing precompile.
+type bls12381Pairing struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
+ return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
+}
+
+func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 Pairing precompile logic.
+ // > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
+ // > - `128` bytes of G1 point encoding
+ // > - `256` bytes of G2 point encoding
+ // > Output is a `32` bytes where last single byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise
+ // > (which is equivalent of Big Endian encoding of Solidity values `uint256(1)` and `uin256(0)` respectively).
+ k := len(input) / 384
+ if len(input) == 0 || len(input)%384 != 0 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Initialize BLS12-381 pairing engine
+ e := bls12381.NewPairingEngine()
+ g1, g2 := e.G1, e.G2
+
+ // Decode pairs
+ for i := 0; i < k; i++ {
+ off := 384 * i
+ t0, t1, t2 := off, off+128, off+384
+
+ // Decode G1 point
+ p1, err := g1.DecodePoint(input[t0:t1])
+ if err != nil {
+ return nil, err
+ }
+ // Decode G2 point
+ p2, err := g2.DecodePoint(input[t1:t2])
+ if err != nil {
+ return nil, err
+ }
+
+ // 'point is on curve' check already done,
+ // Here we need to apply subgroup checks.
+ if !g1.InCorrectSubgroup(p1) {
+ return nil, errBLS12381G1PointSubgroup
+ }
+ if !g2.InCorrectSubgroup(p2) {
+ return nil, errBLS12381G2PointSubgroup
+ }
+
+ // Update pairing engine with G1 and G2 ponits
+ e.AddPair(p1, p2)
+ }
+ // Prepare 32 byte output
+ out := make([]byte, 32)
+
+ // Compute pairing and set the result
+ if e.Check() {
+ out[31] = 1
+ }
+ return out, nil
+}
+
+// decodeBLS12381FieldElement decodes BLS12-381 elliptic curve field element.
+// Removes top 16 bytes of 64 byte input.
+func decodeBLS12381FieldElement(in []byte) ([]byte, error) {
+ if len(in) != 64 {
+ return nil, errors.New("invalid field element length")
+ }
+ // check top bytes
+ for i := 0; i < 16; i++ {
+ if in[i] != byte(0x00) {
+ return nil, errBLS12381InvalidFieldElementTopBytes
+ }
+ }
+ out := make([]byte, 48)
+ copy(out[:], in[16:])
+ return out, nil
+}
+
+// bls12381MapG1 implements EIP-2537 MapG1 precompile.
+type bls12381MapG1 struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
+ return params.Bls12381MapG1Gas
+}
+
+func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 Map_To_G1 precompile.
+ // > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field.
+ // > Output of this call is `128` bytes and is G1 point following respective encoding rules.
+ if len(input) != 64 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe, err := decodeBLS12381FieldElement(input)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381MapG2 implements EIP-2537 MapG2 precompile.
+type bls12381MapG2 struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
+ return params.Bls12381MapG2Gas
+}
+
+func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
+ // > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field.
+ // > Output of this call is `256` bytes and is G2 point following respective encoding rules.
+ if len(input) != 128 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe := make([]byte, 96)
+ c0, err := decodeBLS12381FieldElement(input[:64])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[48:], c0)
+ c1, err := decodeBLS12381FieldElement(input[64:])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[:48], c1)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G2 point to 256 bytes
+ return g.EncodePoint(r), nil
+}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 75e5fb1..5ec7587 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -18,27 +18,44 @@ package vm
import (
"fmt"
+ "sort"
"github.com/ava-labs/coreth/params"
+ "github.com/holiman/uint256"
)
+var activators = map[int]func(*JumpTable){
+ 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)
- 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
@@ -46,23 +63,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
}
@@ -70,23 +86,51 @@ 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) {
- 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
}
+
+// 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),
+ }
+ // 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,
+ }
+}
diff --git a/core/vm/gas.go b/core/vm/gas.go
index bd8b4f1..5cf1d85 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -17,7 +17,7 @@
package vm
import (
- "math/big"
+ "github.com/holiman/uint256"
)
// Gas costs
@@ -30,23 +30,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 2adaf85..0182892 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -20,8 +20,8 @@ import (
"errors"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
)
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
@@ -36,7 +36,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
// overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
// without overflowing the gas calculation.
if newMemSize > 0x1FFFFFFFE0 {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
newMemSizeWords := toWordSize(newMemSize)
newMemSize = newMemSizeWords * 32
@@ -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(), common.Hash(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(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
@@ -183,14 +183,14 @@ 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(), common.Hash(x.Bytes32()))
)
- value := common.BigToHash(y)
+ value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.SstoreNoopGasEIP2200, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
+ original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreInitGasEIP2200, nil
@@ -219,9 +219,9 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
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
}
@@ -307,7 +307,7 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
overflow bool
)
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -320,7 +320,7 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
overflow bool
)
if gas, overflow = math.SafeAdd(gas, params.ExpGas); 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.IsEIP158 {
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.IsEIP150, 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
}
@@ -373,14 +373,14 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
gas += params.CallValueTransferGas
}
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
if err != nil {
return 0, err
}
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -396,7 +396,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
}
var overflow bool
if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
- return 0, errGasUintOverflow
+ return 0, ErrGasUintOverflow
}
return gas, nil
}
@@ -412,7 +412,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
}
var overflow bool
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.IsEIP150 {
gas = params.SelfdestructGasEIP150
- var address = common.BigToAddress(stack.Back(0))
+ var address = common.Address(stack.Back(0).Bytes20())
if evm.chainRules.IsEIP158 {
// if empty and transfers value
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 038841c..ac1a907 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -6,9 +6,9 @@ import (
"encoding/json"
"math/big"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
)
var _ = (*structLogMarshaling)(nil)
@@ -23,6 +23,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 []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth int `json:"depth"`
RefundCounter uint64 `json:"refund"`
@@ -43,6 +45,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
@@ -62,6 +71,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 []byte `json:"returnData"`
Storage map[common.Hash]common.Hash `json:"-"`
Depth *int `json:"depth"`
RefundCounter *uint64 `json:"refund"`
@@ -95,6 +106,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/int_pool_verifier.go b/core/vm/int_pool_verifier.go
deleted file mode 100644
index 82fbfed..0000000
--- 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 <http://www.gnu.org/licenses/>.
-
-// +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 a5f1dc0..0000000
--- 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 <http://www.gnu.org/licenses/>.
-
-// +build !VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-const verifyPool = false
-
-func verifyIntegerPool(ip *intPool) {}
diff --git a/core/vm/intpool.go b/core/vm/intpool.go
deleted file mode 100644
index 917a78d..0000000
--- 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 <http://www.gnu.org/licenses/>.
-
-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/logger.go b/core/vm/logger.go
index 95143f1..ea5d6f2 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -18,17 +18,21 @@ package vm
import (
"encoding/hex"
+ "errors"
"fmt"
"io"
"math/big"
+ "strings"
"time"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/hexutil"
- "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
)
+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,17 +42,17 @@ func (s Storage) Copy() Storage {
for key, value := range s {
cpy[key] = value
}
-
return cpy
}
// 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
+ 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
}
//go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
@@ -63,6 +67,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"`
@@ -72,6 +78,7 @@ type StructLog struct {
// overrides for gencodec
type structLogMarshaling struct {
Stack []*math.HexOrDecimal256
+ ReturnStack []math.HexOrDecimal64
Gas math.HexOrDecimal64
GasCost math.HexOrDecimal64
Memory hexutil.Bytes
@@ -98,9 +105,9 @@ func (s *StructLog) ErrorString() string {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
- CaptureStart(from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error
- CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
- CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+ CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
+ CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, 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
}
@@ -112,16 +119,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
@@ -136,27 +143,11 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
// 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) 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 ErrTraceLimitReached
- }
-
- // 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
@@ -169,24 +160,54 @@ 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)
}
// create a new snapshot of the EVM.
- log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, storage, depth, env.StateDB.GetRefund(), err}
-
+ log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err}
l.logs = append(l.logs, log)
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
}
@@ -227,6 +248,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))
@@ -237,6 +264,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)
}
}
@@ -254,3 +285,79 @@ 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 |
+|-------|-------------|------|-----------|-----------|
+`)
+ 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("%d", elem))
+ }
+ b := fmt.Sprintf("[%v]", strings.Join(a, ","))
+ fmt.Fprintf(t.out, "%10v |", b)
+ }
+ if !t.cfg.DisableStack { // format return stack
+ var a []string
+ 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.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, `
+Output: 0x%x
+Consumed gas: %d
+Error: %v
+`,
+ output, gasUsed, err)
+ return nil
+}
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 5068343..5f3f2c4 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -22,8 +22,8 @@ import (
"math/big"
"time"
- "github.com/ava-labs/go-ethereum/common"
- "github.com/ava-labs/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
)
type JSONLogger struct {
@@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
}
// 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) 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,
@@ -62,13 +62,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
+ }
+ 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/memory.go b/core/vm/memory.go
index 5408707..ba5f848 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -18,9 +18,8 @@ package vm
import (
"fmt"
- "math/big"
- "github.com/ava-labs/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
@@ -70,7 +69,7 @@ func (m *Memory) Resize(size uint64) {
}
// Get returns offset + size as a new slice
-func (m *Memory) Get(offset, size int64) (cpy []byte) {
+func (m *Memory) GetCopy(offset, size int64) (cpy []byte) {
if size == 0 {
return nil
}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index 4c1b9e8..af27d65 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,24 +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) require(n int) error {
- if st.len() < n {
- return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
- }
- return nil
+func (st *Stack) Back(n int) *uint256.Int {
+ return &st.data[st.len()-n-1]
}
// Print dumps the content of the stack
@@ -93,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
+}