params: core, core/vm, miner: 64bit gas instructions (#3514)
Reworked the EVM gas instructions to use 64bit integers rather than arbitrary size big ints. All gas operations, be it additions, multiplications or divisions, are checked and guarded against 64 bit integer overflows. In additon, most of the protocol paramaters in the params package have been converted to uint64 and are now constants rather than variables. * common/math: added overflow check ops * core: vmenv, env renamed to evm * eth, internal/ethapi, les: unmetered eth_call and cancel methods * core/vm: implemented big.Int pool for evm instructions * core/vm: unexported intPool methods & verification methods * core/vm: added memoryGasCost overflow check and test
This commit is contained in:
parent
296450451b
commit
8b57c49490
@ -156,7 +156,7 @@ func run(ctx *cli.Context) error {
|
||||
ret, _, err = runtime.Create(input, &runtime.Config{
|
||||
Origin: sender.Address(),
|
||||
State: statedb,
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(),
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
@ -172,7 +172,7 @@ func run(ctx *cli.Context) error {
|
||||
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
|
||||
Origin: sender.Address(),
|
||||
State: statedb,
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)).Uint64(),
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
|
@ -202,7 +202,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
extra = nil
|
||||
|
25
common/math/integer.go
Normal file
25
common/math/integer.go
Normal file
@ -0,0 +1,25 @@
|
||||
package math
|
||||
|
||||
import gmath "math"
|
||||
|
||||
/*
|
||||
* NOTE: The following methods need to be optimised using either bit checking or asm
|
||||
*/
|
||||
|
||||
// SafeSub returns subtraction result and whether overflow occurred.
|
||||
func SafeSub(x, y uint64) (uint64, bool) {
|
||||
return x - y, x < y
|
||||
}
|
||||
|
||||
// SafeAdd returns the result and whether overflow occurred.
|
||||
func SafeAdd(x, y uint64) (uint64, bool) {
|
||||
return x + y, y > gmath.MaxUint64-x
|
||||
}
|
||||
|
||||
// SafeMul returns multiplication result and whether overflow occurred.
|
||||
func SafeMul(x, y uint64) (uint64, bool) {
|
||||
if x == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return x * y, x != 0 && y != 0 && y > gmath.MaxUint64/x
|
||||
}
|
50
common/math/integer_test.go
Normal file
50
common/math/integer_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
gmath "math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type operation byte
|
||||
|
||||
const (
|
||||
sub operation = iota
|
||||
add
|
||||
mul
|
||||
)
|
||||
|
||||
func TestOverflow(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
x uint64
|
||||
y uint64
|
||||
overflow bool
|
||||
op operation
|
||||
}{
|
||||
// add operations
|
||||
{gmath.MaxUint64, 1, true, add},
|
||||
{gmath.MaxUint64 - 1, 1, false, add},
|
||||
|
||||
// sub operations
|
||||
{0, 1, true, sub},
|
||||
{0, 0, false, sub},
|
||||
|
||||
// mul operations
|
||||
{10, 10, false, mul},
|
||||
{gmath.MaxUint64, 2, true, mul},
|
||||
{gmath.MaxUint64, 1, false, mul},
|
||||
} {
|
||||
var overflows bool
|
||||
switch test.op {
|
||||
case sub:
|
||||
_, overflows = SafeSub(test.x, test.y)
|
||||
case add:
|
||||
_, overflows = SafeAdd(test.x, test.y)
|
||||
case mul:
|
||||
_, overflows = SafeMul(test.x, test.y)
|
||||
}
|
||||
|
||||
if test.overflow != overflows {
|
||||
t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows)
|
||||
}
|
||||
}
|
||||
}
|
@ -92,6 +92,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||
var (
|
||||
ringKeys = make([]*ecdsa.PrivateKey, 1000)
|
||||
ringAddrs = make([]common.Address, len(ringKeys))
|
||||
bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -111,8 +112,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
return func(i int, gen *BlockGen) {
|
||||
gas := CalcGasLimit(gen.PrevBlock(i - 1))
|
||||
for {
|
||||
gas.Sub(gas, params.TxGas)
|
||||
if gas.Cmp(params.TxGas) < 0 {
|
||||
gas.Sub(gas, bigTxGas)
|
||||
if gas.Cmp(bigTxGas) < 0 {
|
||||
break
|
||||
}
|
||||
to := (from + 1) % naccounts
|
||||
@ -120,7 +121,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||
gen.TxNonce(ringAddrs[from]),
|
||||
ringAddrs[to],
|
||||
benchRootFunds,
|
||||
params.TxGas,
|
||||
bigTxGas,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
@ -204,7 +204,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
|
||||
//
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||
}
|
||||
|
||||
|
@ -719,7 +719,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||
if i%3 == 2 {
|
||||
for j := 0; j < i%4+1; j++ {
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -883,8 +883,8 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
// Create two transactions shared between the chains:
|
||||
// - postponed: transaction included at a later block in the forked chain
|
||||
// - swapped: transaction included at the same block number in the forked chain
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
|
||||
// Create two transactions that will be dropped by the forked chain:
|
||||
// - pastDrop: transaction dropped retroactively from a past block
|
||||
@ -900,13 +900,13 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
|
||||
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
||||
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
||||
|
||||
case 2:
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
|
||||
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
||||
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
||||
@ -925,18 +925,18 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
||||
|
||||
case 2:
|
||||
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
||||
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
||||
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
||||
|
||||
case 3:
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||
}
|
||||
})
|
||||
|
@ -56,13 +56,13 @@ func ExampleGenerateChain() {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, addr1 sends addr2 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
|
||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1)
|
||||
gen.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, addr1 sends some more ether to addr2.
|
||||
// addr2 passes it on to addr3.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||
gen.AddTx(tx1)
|
||||
gen.AddTx(tx2)
|
||||
case 2:
|
||||
|
@ -51,13 +51,14 @@ The state transitioning model does all all the necessary work to work out a vali
|
||||
type StateTransition struct {
|
||||
gp *GasPool
|
||||
msg Message
|
||||
gas, gasPrice *big.Int
|
||||
gas uint64
|
||||
gasPrice *big.Int
|
||||
initialGas *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state vm.StateDB
|
||||
|
||||
env *vm.EVM
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
||||
// Message represents a message sent to a contract.
|
||||
@ -81,12 +82,14 @@ func MessageCreatesContract(msg Message) bool {
|
||||
|
||||
// IntrinsicGas computes the 'intrinsic gas' for a message
|
||||
// with the given data.
|
||||
//
|
||||
// TODO convert to uint64
|
||||
func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
|
||||
igas := new(big.Int)
|
||||
if contractCreation && homestead {
|
||||
igas.Set(params.TxGasContractCreation)
|
||||
igas.SetUint64(params.TxGasContractCreation)
|
||||
} else {
|
||||
igas.Set(params.TxGas)
|
||||
igas.SetUint64(params.TxGas)
|
||||
}
|
||||
if len(data) > 0 {
|
||||
var nz int64
|
||||
@ -96,27 +99,26 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
|
||||
}
|
||||
}
|
||||
m := big.NewInt(nz)
|
||||
m.Mul(m, params.TxDataNonZeroGas)
|
||||
m.Mul(m, new(big.Int).SetUint64(params.TxDataNonZeroGas))
|
||||
igas.Add(igas, m)
|
||||
m.SetInt64(int64(len(data)) - nz)
|
||||
m.Mul(m, params.TxDataZeroGas)
|
||||
m.Mul(m, new(big.Int).SetUint64(params.TxDataZeroGas))
|
||||
igas.Add(igas, m)
|
||||
}
|
||||
return igas
|
||||
}
|
||||
|
||||
// NewStateTransition initialises and returns a new state transition object.
|
||||
func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition {
|
||||
return &StateTransition{
|
||||
gp: gp,
|
||||
env: env,
|
||||
evm: evm,
|
||||
msg: msg,
|
||||
gas: new(big.Int),
|
||||
gasPrice: msg.GasPrice(),
|
||||
initialGas: new(big.Int),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: env.StateDB,
|
||||
state: evm.StateDB,
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,8 +129,8 @@ func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition
|
||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||
// 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(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
|
||||
st := NewStateTransition(env, msg, gp)
|
||||
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
|
||||
st := NewStateTransition(evm, msg, gp)
|
||||
|
||||
ret, _, gasUsed, err := st.TransitionDb()
|
||||
return ret, gasUsed, err
|
||||
@ -157,21 +159,21 @@ func (self *StateTransition) to() vm.Account {
|
||||
return self.state.GetAccount(*to)
|
||||
}
|
||||
|
||||
func (self *StateTransition) useGas(amount *big.Int) error {
|
||||
if self.gas.Cmp(amount) < 0 {
|
||||
func (self *StateTransition) useGas(amount uint64) error {
|
||||
if self.gas < amount {
|
||||
return vm.ErrOutOfGas
|
||||
}
|
||||
self.gas.Sub(self.gas, amount)
|
||||
self.gas -= amount
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *StateTransition) addGas(amount *big.Int) {
|
||||
self.gas.Add(self.gas, amount)
|
||||
}
|
||||
|
||||
func (self *StateTransition) buyGas() error {
|
||||
mgas := self.msg.Gas()
|
||||
if mgas.BitLen() > 64 {
|
||||
return vm.ErrOutOfGas
|
||||
}
|
||||
|
||||
mgval := new(big.Int).Mul(mgas, self.gasPrice)
|
||||
|
||||
sender := self.from()
|
||||
@ -181,7 +183,8 @@ func (self *StateTransition) buyGas() error {
|
||||
if err := self.gp.SubGas(mgas); err != nil {
|
||||
return err
|
||||
}
|
||||
self.addGas(mgas)
|
||||
self.gas += mgas.Uint64()
|
||||
|
||||
self.initialGas.Set(mgas)
|
||||
sender.SubBalance(mgval)
|
||||
return nil
|
||||
@ -209,7 +212,9 @@ func (self *StateTransition) preCheck() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TransitionDb will move the state by applying the message against the given environment.
|
||||
// TransitionDb will transition the state by applying the current message and returning the result
|
||||
// including the required gas for the operation as well as the used gas. It returns an error if it
|
||||
// failed. An error indicates a consensus issue.
|
||||
func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *big.Int, err error) {
|
||||
if err = self.preCheck(); err != nil {
|
||||
return
|
||||
@ -217,26 +222,32 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||
msg := self.msg
|
||||
sender := self.from() // err checked in preCheck
|
||||
|
||||
homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber)
|
||||
homestead := self.evm.ChainConfig().IsHomestead(self.evm.BlockNumber)
|
||||
contractCreation := MessageCreatesContract(msg)
|
||||
// Pay intrinsic gas
|
||||
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
||||
// TODO convert to uint64
|
||||
intrinsicGas := IntrinsicGas(self.data, contractCreation, homestead)
|
||||
if intrinsicGas.BitLen() > 64 {
|
||||
return nil, nil, nil, InvalidTxError(vm.ErrOutOfGas)
|
||||
}
|
||||
|
||||
if err = self.useGas(intrinsicGas.Uint64()); err != nil {
|
||||
return nil, nil, nil, InvalidTxError(err)
|
||||
}
|
||||
|
||||
var (
|
||||
vmenv = self.env
|
||||
evm = self.evm
|
||||
// vm errors do not effect consensus and are therefor
|
||||
// not assigned to err, except for insufficient balance
|
||||
// error.
|
||||
vmerr error
|
||||
)
|
||||
if contractCreation {
|
||||
ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
|
||||
ret, _, self.gas, vmerr = evm.Create(sender, self.data, self.gas, self.value)
|
||||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
|
||||
ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
|
||||
ret, self.gas, vmerr = evm.Call(sender, self.to().Address(), self.data, self.gas, self.value)
|
||||
}
|
||||
if vmerr != nil {
|
||||
glog.V(logger.Core).Infoln("vm returned with error:", err)
|
||||
@ -251,7 +262,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||
requiredGas = new(big.Int).Set(self.gasUsed())
|
||||
|
||||
self.refundGas()
|
||||
self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
self.state.AddBalance(self.evm.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
|
||||
return ret, requiredGas, self.gasUsed(), err
|
||||
}
|
||||
@ -260,20 +271,21 @@ func (self *StateTransition) refundGas() {
|
||||
// Return eth for remaining gas to the sender account,
|
||||
// exchanged at the original rate.
|
||||
sender := self.from() // err already checked
|
||||
remaining := new(big.Int).Mul(self.gas, self.gasPrice)
|
||||
remaining := new(big.Int).Mul(new(big.Int).SetUint64(self.gas), self.gasPrice)
|
||||
sender.AddBalance(remaining)
|
||||
|
||||
// Apply refund counter, capped to half of the used gas.
|
||||
uhalf := remaining.Div(self.gasUsed(), common.Big2)
|
||||
refund := common.BigMin(uhalf, self.state.GetRefund())
|
||||
self.gas.Add(self.gas, refund)
|
||||
self.gas += refund.Uint64()
|
||||
|
||||
self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
|
||||
|
||||
// Also return remaining gas to the block gas counter so it is
|
||||
// available for the next transaction.
|
||||
self.gp.AddGas(self.gas)
|
||||
self.gp.AddGas(new(big.Int).SetUint64(self.gas))
|
||||
}
|
||||
|
||||
func (self *StateTransition) gasUsed() *big.Int {
|
||||
return new(big.Int).Sub(self.initialGas, self.gas)
|
||||
return new(big.Int).Sub(self.initialGas, new(big.Int).SetUint64(self.gas))
|
||||
}
|
||||
|
@ -21,28 +21,11 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Type is the VM type accepted by **NewVm**
|
||||
type Type byte
|
||||
|
||||
const (
|
||||
StdVmTy Type = iota // Default standard VM
|
||||
JitVmTy // LLVM JIT VM
|
||||
MaxVmTy
|
||||
)
|
||||
|
||||
var (
|
||||
Pow256 = common.BigPow(2, 256) // Pow256 is 2**256
|
||||
|
||||
U256 = common.U256 // Shortcut to common.U256
|
||||
S256 = common.S256 // Shortcut to common.S256
|
||||
|
||||
Zero = common.Big0 // Shortcut to common.Big0
|
||||
One = common.Big1 // Shortcut to common.Big1
|
||||
|
||||
max = big.NewInt(math.MaxInt64) // Maximum 64 bit integer
|
||||
)
|
||||
|
||||
// calculates the memory size required for a step
|
||||
@ -54,48 +37,6 @@ func calcMemSize(off, l *big.Int) *big.Int {
|
||||
return new(big.Int).Add(off, l)
|
||||
}
|
||||
|
||||
// calculates the quadratic gas
|
||||
func quadMemGas(mem *Memory, newMemSize, gas *big.Int) {
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSize.Mul(newMemSizeWords, u256(32))
|
||||
|
||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||
// be careful reusing variables here when changing.
|
||||
// The order has been optimised to reduce allocation
|
||||
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
||||
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
||||
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
|
||||
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||
|
||||
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
||||
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
|
||||
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
|
||||
newTotalFee := linCoef.Add(linCoef, quadCoef)
|
||||
|
||||
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
|
||||
gas.Add(gas, fee)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple helper
|
||||
func u256(n int64) *big.Int {
|
||||
return big.NewInt(n)
|
||||
}
|
||||
|
||||
// Mainly used for print variables and passing to Print*
|
||||
func toValue(val *big.Int) interface{} {
|
||||
// Let's assume a string on right padded zero's
|
||||
b := val.Bytes()
|
||||
if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// getData 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 getData(data []byte, start, size *big.Int) []byte {
|
||||
@ -106,14 +47,17 @@ func getData(data []byte, start, size *big.Int) []byte {
|
||||
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
|
||||
}
|
||||
|
||||
// useGas attempts to subtract the amount of gas and returns whether it was
|
||||
// successful
|
||||
func useGas(gas, amount *big.Int) bool {
|
||||
if gas.Cmp(amount) < 0 {
|
||||
return false
|
||||
// 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.BitLen() > 64
|
||||
}
|
||||
|
||||
// toWordSize returns the ceiled word size required for memory expansion.
|
||||
func toWordSize(size uint64) uint64 {
|
||||
if size > math.MaxUint64-31 {
|
||||
return math.MaxUint64/32 + 1
|
||||
}
|
||||
|
||||
// Sub the amount of gas from the remaining
|
||||
gas.Sub(gas, amount)
|
||||
return true
|
||||
return (size + 31) / 32
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
|
||||
// ContractRef is a reference to the contract's backing object
|
||||
type ContractRef interface {
|
||||
ReturnGas(*big.Int)
|
||||
Address() common.Address
|
||||
Value() *big.Int
|
||||
SetCode(common.Hash, []byte)
|
||||
@ -48,7 +47,8 @@ type Contract struct {
|
||||
CodeAddr *common.Address
|
||||
Input []byte
|
||||
|
||||
value, Gas, UsedGas *big.Int
|
||||
Gas uint64
|
||||
value *big.Int
|
||||
|
||||
Args []byte
|
||||
|
||||
@ -56,7 +56,7 @@ type Contract struct {
|
||||
}
|
||||
|
||||
// NewContract returns a new contract environment for the execution of EVM.
|
||||
func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
|
||||
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
|
||||
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
|
||||
|
||||
if parent, ok := caller.(*Contract); ok {
|
||||
@ -68,9 +68,8 @@ func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *C
|
||||
|
||||
// Gas should be a pointer so it can safely be reduced through the run
|
||||
// This pointer will be off the state transition
|
||||
c.Gas = gas //new(big.Int).Set(gas)
|
||||
c.Gas = gas
|
||||
c.value = new(big.Int).Set(value)
|
||||
c.UsedGas = new(big.Int)
|
||||
|
||||
return c
|
||||
}
|
||||
@ -107,27 +106,13 @@ func (c *Contract) Caller() common.Address {
|
||||
return c.CallerAddress
|
||||
}
|
||||
|
||||
// Finalise finalises the contract and returning any remaining gas to the original
|
||||
// caller.
|
||||
func (c *Contract) Finalise() {
|
||||
// Return the remaining gas to the caller
|
||||
c.caller.ReturnGas(c.Gas)
|
||||
}
|
||||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
func (c *Contract) UseGas(gas *big.Int) (ok bool) {
|
||||
ok = useGas(c.Gas, gas)
|
||||
if ok {
|
||||
c.UsedGas.Add(c.UsedGas, gas)
|
||||
func (c *Contract) UseGas(gas uint64) (ok bool) {
|
||||
if c.Gas < gas {
|
||||
return false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ReturnGas adds the given gas back to itself.
|
||||
func (c *Contract) ReturnGas(gas *big.Int) {
|
||||
// Return the gas to the context
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
c.UsedGas.Sub(c.UsedGas, gas)
|
||||
c.Gas -= gas
|
||||
return true
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
|
@ -17,8 +17,6 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
@ -30,7 +28,7 @@ import (
|
||||
// requires a deterministic gas count based on the input size of the Run method of the
|
||||
// contract.
|
||||
type PrecompiledContract interface {
|
||||
RequiredGas(inputSize int) *big.Int // RequiredPrice calculates the contract gas use
|
||||
RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use
|
||||
Run(input []byte) []byte // Run runs the precompiled contract
|
||||
}
|
||||
|
||||
@ -57,7 +55,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr
|
||||
// ECRECOVER implemented as a native contract
|
||||
type ecrecover struct{}
|
||||
|
||||
func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
|
||||
func (c *ecrecover) RequiredGas(inputSize int) uint64 {
|
||||
return params.EcrecoverGas
|
||||
}
|
||||
|
||||
@ -92,10 +90,12 @@ func (c *ecrecover) Run(in []byte) []byte {
|
||||
// SHA256 implemented as a native contract
|
||||
type sha256 struct{}
|
||||
|
||||
func (c *sha256) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.Sha256WordGas)
|
||||
return n.Add(n, params.Sha256Gas)
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
// required for anything significant is so high it's impossible to pay for.
|
||||
func (c *sha256) RequiredGas(inputSize int) uint64 {
|
||||
return uint64(inputSize+31)/32*params.Sha256WordGas + params.Sha256Gas
|
||||
}
|
||||
func (c *sha256) Run(in []byte) []byte {
|
||||
return crypto.Sha256(in)
|
||||
@ -104,10 +104,12 @@ func (c *sha256) Run(in []byte) []byte {
|
||||
// RIPMED160 implemented as a native contract
|
||||
type ripemd160 struct{}
|
||||
|
||||
func (c *ripemd160) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.Ripemd160WordGas)
|
||||
return n.Add(n, params.Ripemd160Gas)
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
// required for anything significant is so high it's impossible to pay for.
|
||||
func (c *ripemd160) RequiredGas(inputSize int) uint64 {
|
||||
return uint64(inputSize+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas
|
||||
}
|
||||
func (c *ripemd160) Run(in []byte) []byte {
|
||||
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
|
||||
@ -116,11 +118,12 @@ func (c *ripemd160) Run(in []byte) []byte {
|
||||
// data copy implemented as a native contract
|
||||
type dataCopy struct{}
|
||||
|
||||
func (c *dataCopy) RequiredGas(inputSize int) *big.Int {
|
||||
n := big.NewInt(int64(inputSize+31) / 32)
|
||||
n.Mul(n, params.IdentityWordGas)
|
||||
|
||||
return n.Add(n, params.IdentityGas)
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
// required for anything significant is so high it's impossible to pay for.
|
||||
func (c *dataCopy) RequiredGas(inputSize int) uint64 {
|
||||
return uint64(inputSize+31)/32*params.IdentityWordGas + params.IdentityGas
|
||||
}
|
||||
func (c *dataCopy) Run(in []byte) []byte {
|
||||
return in
|
||||
|
@ -17,7 +17,6 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync/atomic"
|
||||
|
||||
@ -102,24 +101,18 @@ func (evm *EVM) Cancel() {
|
||||
// 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 execution error or failed value transfer.
|
||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, ErrDepth
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
}
|
||||
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, ErrInsufficientBalance
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
var (
|
||||
@ -128,8 +121,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas,
|
||||
)
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if PrecompiledContracts[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.BitLen() == 0 {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, nil
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
to = evm.StateDB.CreateAccount(addr)
|
||||
@ -143,7 +135,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas,
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
@ -154,7 +145,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas,
|
||||
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
return ret, err
|
||||
return ret, contract.Gas, err
|
||||
}
|
||||
|
||||
// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any
|
||||
@ -162,24 +153,18 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas,
|
||||
// case of an execution error or failed value transfer.
|
||||
//
|
||||
// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
|
||||
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) (ret []byte, err error) {
|
||||
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, ErrDepth
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
}
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, evm.StateDB.GetBalance(caller.Address()))
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
var (
|
||||
@ -191,7 +176,6 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
if err != nil {
|
||||
@ -200,7 +184,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
return ret, contract.Gas, err
|
||||
}
|
||||
|
||||
// DelegateCall executes the contract associated with the addr with the given input as parameters.
|
||||
@ -208,18 +192,15 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||
//
|
||||
// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
|
||||
// and the caller is set to the caller of the caller.
|
||||
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) (ret []byte, err error) {
|
||||
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, ErrDepth
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
}
|
||||
|
||||
var (
|
||||
@ -230,7 +211,6 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.interpreter.Run(contract, input)
|
||||
if err != nil {
|
||||
@ -239,28 +219,22 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
return ret, contract.Gas, err
|
||||
}
|
||||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (ret []byte, contractAddr common.Address, err error) {
|
||||
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, nil
|
||||
return nil, common.Address{}, gas, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if evm.depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, ErrDepth
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, common.Address{}, gas, ErrDepth
|
||||
}
|
||||
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, ErrInsufficientBalance
|
||||
return nil, common.Address{}, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// Create a new account on the state
|
||||
@ -280,7 +254,6 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.interpreter.Run(contract, nil)
|
||||
|
||||
@ -291,9 +264,8 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
|
||||
// be stored due to not enough gas set an error and let it be handled
|
||||
// by the error checking condition below.
|
||||
if err == nil && !maxCodeSizeExceeded {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if contract.UseGas(dataGas) {
|
||||
createDataGas := uint64(len(ret)) * params.CreateDataGas
|
||||
if contract.UseGas(createDataGas) {
|
||||
evm.StateDB.SetCode(contractAddr, ret)
|
||||
} else {
|
||||
err = ErrCodeStoreOutOfGas
|
||||
@ -305,11 +277,10 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
|
||||
// when we're in homestead this also counts for code storage gas errors.
|
||||
if maxCodeSizeExceeded ||
|
||||
(err != nil && (evm.ChainConfig().IsHomestead(evm.BlockNumber) || err != ErrCodeStoreOutOfGas)) {
|
||||
contract.UseGas(contract.Gas)
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
||||
// Nothing should be returned when an error is thrown.
|
||||
return nil, contractAddr, err
|
||||
return nil, contractAddr, 0, err
|
||||
}
|
||||
// If the vm returned with an error the return value should be set to nil.
|
||||
// This isn't consensus critical but merely to for behaviour reasons such as
|
||||
@ -318,7 +289,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas, value *big.Int) (re
|
||||
ret = nil
|
||||
}
|
||||
|
||||
return ret, contractAddr, err
|
||||
return ret, contractAddr, contract.Gas, err
|
||||
}
|
||||
|
||||
// ChainConfig returns the evmironment's chain configuration
|
157
core/vm/gas.go
157
core/vm/gas.go
@ -17,149 +17,42 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
GasQuickStep = big.NewInt(2)
|
||||
GasFastestStep = big.NewInt(3)
|
||||
GasFastStep = big.NewInt(5)
|
||||
GasMidStep = big.NewInt(8)
|
||||
GasSlowStep = big.NewInt(10)
|
||||
GasExtStep = big.NewInt(20)
|
||||
const (
|
||||
GasQuickStep uint64 = 2
|
||||
GasFastestStep uint64 = 3
|
||||
GasFastStep uint64 = 5
|
||||
GasMidStep uint64 = 8
|
||||
GasSlowStep uint64 = 10
|
||||
GasExtStep uint64 = 20
|
||||
|
||||
GasReturn = big.NewInt(0)
|
||||
GasStop = big.NewInt(0)
|
||||
|
||||
GasContractByte = big.NewInt(200)
|
||||
|
||||
n64 = big.NewInt(64)
|
||||
GasReturn uint64 = 0
|
||||
GasStop uint64 = 0
|
||||
GasContractByte uint64 = 200
|
||||
)
|
||||
|
||||
// calcGas returns the actual gas cost of the call.
|
||||
//
|
||||
// The cost of gas was changed during the homestead price change HF. To allow for EIP150
|
||||
// to be implemented. The returned gas is gas - base * 63 / 64.
|
||||
func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int {
|
||||
if gasTable.CreateBySuicide != nil {
|
||||
availableGas = new(big.Int).Sub(availableGas, base)
|
||||
g := new(big.Int).Div(availableGas, n64)
|
||||
g.Sub(availableGas, g)
|
||||
func callGas(gasTable params.GasTable, availableGas, base uint64, callCost *big.Int) (uint64, error) {
|
||||
if gasTable.CreateBySuicide > 0 {
|
||||
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
|
||||
// of returning an error.
|
||||
if callCost.BitLen() > 64 || gas < callCost.Uint64() {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
if callCost.BitLen() > 64 {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if g.Cmp(callCost) < 0 {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return callCost
|
||||
}
|
||||
|
||||
// baseCheck checks for any stack error underflows
|
||||
func baseCheck(op OpCode, stack *Stack, gas *big.Int) error {
|
||||
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
|
||||
// PUSH is also allowed to calculate the same price for all PUSHes
|
||||
// DUP requirements are handled elsewhere (except for the stack limit check)
|
||||
if op >= PUSH1 && op <= PUSH32 {
|
||||
op = PUSH1
|
||||
}
|
||||
if op >= DUP1 && op <= DUP16 {
|
||||
op = DUP1
|
||||
}
|
||||
|
||||
if r, ok := _baseCheck[op]; ok {
|
||||
err := stack.require(r.stackPop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.stackPush > 0 && stack.len()-r.stackPop+r.stackPush > int(params.StackLimit.Int64()) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
}
|
||||
|
||||
gas.Add(gas, r.gas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// casts a arbitrary number to the amount of words (sets of 32 bytes)
|
||||
func toWordSize(size *big.Int) *big.Int {
|
||||
tmp := new(big.Int)
|
||||
tmp.Add(size, u256(31))
|
||||
tmp.Div(tmp, u256(32))
|
||||
return tmp
|
||||
}
|
||||
|
||||
type req struct {
|
||||
stackPop int
|
||||
gas *big.Int
|
||||
stackPush int
|
||||
}
|
||||
|
||||
var _baseCheck = map[OpCode]req{
|
||||
// opcode | stack pop | gas price | stack push
|
||||
ADD: {2, GasFastestStep, 1},
|
||||
LT: {2, GasFastestStep, 1},
|
||||
GT: {2, GasFastestStep, 1},
|
||||
SLT: {2, GasFastestStep, 1},
|
||||
SGT: {2, GasFastestStep, 1},
|
||||
EQ: {2, GasFastestStep, 1},
|
||||
ISZERO: {1, GasFastestStep, 1},
|
||||
SUB: {2, GasFastestStep, 1},
|
||||
AND: {2, GasFastestStep, 1},
|
||||
OR: {2, GasFastestStep, 1},
|
||||
XOR: {2, GasFastestStep, 1},
|
||||
NOT: {1, GasFastestStep, 1},
|
||||
BYTE: {2, GasFastestStep, 1},
|
||||
CALLDATALOAD: {1, GasFastestStep, 1},
|
||||
CALLDATACOPY: {3, GasFastestStep, 1},
|
||||
MLOAD: {1, GasFastestStep, 1},
|
||||
MSTORE: {2, GasFastestStep, 0},
|
||||
MSTORE8: {2, GasFastestStep, 0},
|
||||
CODECOPY: {3, GasFastestStep, 0},
|
||||
MUL: {2, GasFastStep, 1},
|
||||
DIV: {2, GasFastStep, 1},
|
||||
SDIV: {2, GasFastStep, 1},
|
||||
MOD: {2, GasFastStep, 1},
|
||||
SMOD: {2, GasFastStep, 1},
|
||||
SIGNEXTEND: {2, GasFastStep, 1},
|
||||
ADDMOD: {3, GasMidStep, 1},
|
||||
MULMOD: {3, GasMidStep, 1},
|
||||
JUMP: {1, GasMidStep, 0},
|
||||
JUMPI: {2, GasSlowStep, 0},
|
||||
EXP: {2, GasSlowStep, 1},
|
||||
ADDRESS: {0, GasQuickStep, 1},
|
||||
ORIGIN: {0, GasQuickStep, 1},
|
||||
CALLER: {0, GasQuickStep, 1},
|
||||
CALLVALUE: {0, GasQuickStep, 1},
|
||||
CODESIZE: {0, GasQuickStep, 1},
|
||||
GASPRICE: {0, GasQuickStep, 1},
|
||||
COINBASE: {0, GasQuickStep, 1},
|
||||
TIMESTAMP: {0, GasQuickStep, 1},
|
||||
NUMBER: {0, GasQuickStep, 1},
|
||||
CALLDATASIZE: {0, GasQuickStep, 1},
|
||||
DIFFICULTY: {0, GasQuickStep, 1},
|
||||
GASLIMIT: {0, GasQuickStep, 1},
|
||||
POP: {1, GasQuickStep, 0},
|
||||
PC: {0, GasQuickStep, 1},
|
||||
MSIZE: {0, GasQuickStep, 1},
|
||||
GAS: {0, GasQuickStep, 1},
|
||||
BLOCKHASH: {1, GasExtStep, 1},
|
||||
BALANCE: {1, Zero, 1},
|
||||
EXTCODESIZE: {1, Zero, 1},
|
||||
EXTCODECOPY: {4, Zero, 0},
|
||||
SLOAD: {1, params.SloadGas, 1},
|
||||
SSTORE: {2, Zero, 0},
|
||||
SHA3: {2, params.Sha3Gas, 1},
|
||||
CREATE: {3, params.CreateGas, 1},
|
||||
// Zero is calculated in the gasSwitch
|
||||
CALL: {7, Zero, 1},
|
||||
CALLCODE: {7, Zero, 1},
|
||||
DELEGATECALL: {6, Zero, 1},
|
||||
SUICIDE: {1, Zero, 0},
|
||||
JUMPDEST: {0, params.JumpdestGas, 0},
|
||||
RETURN: {2, Zero, 0},
|
||||
PUSH1: {0, GasFastestStep, 1},
|
||||
DUP1: {0, Zero, 1},
|
||||
return callCost.Uint64(), nil
|
||||
}
|
||||
|
@ -1,56 +1,80 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
gmath "math"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func memoryGasCost(mem *Memory, newMemSize *big.Int) *big.Int {
|
||||
gas := new(big.Int)
|
||||
if newMemSize.Cmp(common.Big0) > 0 {
|
||||
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so
|
||||
// only for the memory region that is expanded, not the total memory.
|
||||
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||
// The maximum that will fit in a uint64 is max_word_count - 1
|
||||
// anything above that will result in an overflow.
|
||||
if newMemSize > gmath.MaxUint64-32 {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if newMemSize == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
newMemSizeWords := toWordSize(newMemSize)
|
||||
newMemSize = newMemSizeWords * 32
|
||||
|
||||
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
|
||||
// be careful reusing variables here when changing.
|
||||
// The order has been optimised to reduce allocation
|
||||
oldSize := toWordSize(big.NewInt(int64(mem.Len())))
|
||||
pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
|
||||
linCoef := oldSize.Mul(oldSize, params.MemoryGas)
|
||||
quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
|
||||
oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
|
||||
if newMemSize > uint64(mem.Len()) {
|
||||
square := newMemSizeWords * newMemSizeWords
|
||||
linCoef := newMemSizeWords * params.MemoryGas
|
||||
quadCoef := square / params.QuadCoeffDiv
|
||||
newTotalFee := linCoef + quadCoef
|
||||
|
||||
pow.Exp(newMemSizeWords, common.Big2, Zero)
|
||||
linCoef = linCoef.Mul(newMemSizeWords, params.MemoryGas)
|
||||
quadCoef = quadCoef.Div(pow, params.QuadCoeffDiv)
|
||||
newTotalFee := linCoef.Add(linCoef, quadCoef)
|
||||
fee := newTotalFee - mem.lastGasCost
|
||||
mem.lastGasCost = newTotalFee
|
||||
|
||||
fee := newTotalFee.Sub(newTotalFee, oldTotalFee)
|
||||
gas.Add(gas, fee)
|
||||
return fee, nil
|
||||
}
|
||||
}
|
||||
return gas
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func constGasFunc(gas *big.Int) gasFunc {
|
||||
return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gas
|
||||
func constGasFunc(gas uint64) gasFunc {
|
||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasCalldataCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, GasFastestStep)
|
||||
words := toWordSize(stack.Back(2))
|
||||
func gasCalldataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
words, overflow := bigUint64(stack.Back(2))
|
||||
if overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, words); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
y, x = stack.Back(1), stack.Back(0)
|
||||
val = env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
)
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
@ -58,189 +82,335 @@ func gasSStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, m
|
||||
// 3. From a non-zero to a non-zero (CHANGE)
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
// 0 => non 0
|
||||
return new(big.Int).Set(params.SstoreSetGas)
|
||||
return params.SstoreSetGas, nil
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SstoreRefundGas))
|
||||
|
||||
return new(big.Int).Set(params.SstoreClearGas)
|
||||
return params.SstoreClearGas, nil
|
||||
} else {
|
||||
// non 0 => non 0 (or 0 => 0)
|
||||
return new(big.Int).Set(params.SstoreResetGas)
|
||||
return params.SstoreResetGas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeGasLog(n uint) gasFunc {
|
||||
return func(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
mSize := stack.Back(1)
|
||||
func makeGasLog(n uint64) gasFunc {
|
||||
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
requestedSize, overflow := bigUint64(stack.Back(1))
|
||||
if overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
gas := new(big.Int).Add(memoryGasCost(mem, memorySize), params.LogGas)
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
|
||||
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
|
||||
return gas
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
var memorySizeGas uint64
|
||||
if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
}
|
||||
|
||||
func gasSha3(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, params.Sha3Gas)
|
||||
words := toWordSize(stack.Back(1))
|
||||
return gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
func gasSha3(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var overflow bool
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, params.Sha3Gas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
wordGas, overflow := bigUint64(stack.Back(1))
|
||||
if overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Sha3WordGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, GasFastestStep)
|
||||
words := toWordSize(stack.Back(2))
|
||||
func gasCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
wordGas, overflow := bigUint64(stack.Back(2))
|
||||
if overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasExtCodeCopy(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := memoryGasCost(mem, memorySize)
|
||||
gas.Add(gas, gt.ExtcodeCopy)
|
||||
words := toWordSize(stack.Back(3))
|
||||
func gasExtCodeCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, gt.ExtcodeCopy); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
wordGas, overflow := bigUint64(stack.Back(3))
|
||||
if overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.CopyGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasMLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
func gasMLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var overflow bool
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasMStore8(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
func gasMStore8(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var overflow bool
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasMStore(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(GasFastestStep, memoryGasCost(mem, memorySize))
|
||||
func gasMStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var overflow bool
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, GasFastestStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCreate(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return new(big.Int).Add(params.CreateGas, memoryGasCost(mem, memorySize))
|
||||
func gasCreate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var overflow bool
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if gas, overflow = math.SafeAdd(gas, params.CreateGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasBalance(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.Balance
|
||||
func gasBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gt.Balance, nil
|
||||
}
|
||||
|
||||
func gasExtCodeSize(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.ExtcodeSize
|
||||
func gasExtCodeSize(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gt.ExtcodeSize, nil
|
||||
}
|
||||
|
||||
func gasSLoad(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return gt.SLoad
|
||||
func gasSLoad(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return gt.SLoad, nil
|
||||
}
|
||||
|
||||
func gasExp(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
gas := big.NewInt(expByteLen)
|
||||
gas.Mul(gas, gt.ExpByte)
|
||||
return gas.Add(gas, GasSlowStep)
|
||||
}
|
||||
func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
|
||||
func gasCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Set(gt.Calls)
|
||||
|
||||
transfersValue := stack.Back(2).BitLen() > 0
|
||||
var (
|
||||
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
|
||||
overflow bool
|
||||
)
|
||||
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var (
|
||||
gas = gt.Calls
|
||||
transfersValue = stack.Back(2).BitLen() > 0
|
||||
address = common.BigToAddress(stack.Back(1))
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
|
||||
)
|
||||
if eip158 {
|
||||
if env.StateDB.Empty(address) && transfersValue {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
if evm.StateDB.Empty(address) && transfersValue {
|
||||
gas += params.CallNewAccountGas
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += params.CallNewAccountGas
|
||||
}
|
||||
if transfersValue {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
gas += params.CallValueTransferGas
|
||||
}
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
gas.Add(gas, memoryGasCost(mem, memorySize))
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasCallCode(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Set(gt.Calls)
|
||||
func gasCallCode(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas := gt.Calls
|
||||
if stack.Back(2).BitLen() > 0 {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
gas += params.CallValueTransferGas
|
||||
}
|
||||
memoryGas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
gas.Add(gas, memoryGasCost(mem, memorySize))
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasReturn(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
func gasReturn(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return memoryGasCost(mem, memorySize)
|
||||
}
|
||||
|
||||
func gasSuicide(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int)
|
||||
func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
var gas uint64
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if env.ChainConfig().IsEIP150(env.BlockNumber) {
|
||||
gas.Set(gt.Suicide)
|
||||
if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
|
||||
gas = gt.Suicide
|
||||
var (
|
||||
address = common.BigToAddress(stack.Back(0))
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
eip158 = evm.ChainConfig().IsEIP158(evm.BlockNumber)
|
||||
)
|
||||
|
||||
if eip158 {
|
||||
// if empty and transfers value
|
||||
if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas.Add(gas, gt.CreateBySuicide)
|
||||
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas += gt.CreateBySuicide
|
||||
}
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, gt.CreateBySuicide)
|
||||
} else if !evm.StateDB.Exist(address) {
|
||||
gas += gt.CreateBySuicide
|
||||
}
|
||||
}
|
||||
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
if !evm.StateDB.HasSuicided(contract.Address()) {
|
||||
evm.StateDB.AddRefund(new(big.Int).SetUint64(params.SuicideRefundGas))
|
||||
}
|
||||
return gas
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasDelegateCall(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
gas := new(big.Int).Add(gt.Calls, memoryGasCost(mem, memorySize))
|
||||
func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
gas, err := memoryGasCost(mem, memorySize)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
|
||||
cg := callGas(gt, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = cg
|
||||
stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
|
||||
|
||||
return gas.Add(gas, cg)
|
||||
if gas, overflow = math.SafeAdd(gas, cg); overflow {
|
||||
return 0, errGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
||||
func gasPush(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasSwap(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
func gasSwap(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
||||
func gasDup(gt params.GasTable, env *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize *big.Int) *big.Int {
|
||||
return GasFastestStep
|
||||
func gasDup(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
return GasFastestStep, nil
|
||||
}
|
||||
|
24
core/vm/gas_table_test.go
Normal file
24
core/vm/gas_table_test.go
Normal file
@ -0,0 +1,24 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMemoryGasCost(t *testing.T) {
|
||||
size := uint64(math.MaxUint64 - 64)
|
||||
_, err := memoryGasCost(&Memory{}, size)
|
||||
if err != nil {
|
||||
t.Error("didn't expect error:", err)
|
||||
}
|
||||
|
||||
_, err = memoryGasCost(&Memory{}, size+32)
|
||||
if err != nil {
|
||||
t.Error("didn't expect error:", err)
|
||||
}
|
||||
|
||||
_, err = memoryGasCost(&Memory{}, size+33)
|
||||
if err == nil {
|
||||
t.Error("expected error")
|
||||
}
|
||||
}
|
@ -27,42 +27,56 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func opAdd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var bigZero = new(big.Int)
|
||||
|
||||
func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Add(x, y)))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSub(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSub(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Sub(x, y)))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMul(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMul(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Mul(x, y)))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opDiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) != 0 {
|
||||
stack.push(U256(x.Div(x, y)))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSdiv(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
return nil, nil
|
||||
} else {
|
||||
n := new(big.Int)
|
||||
if new(big.Int).Mul(x, y).Cmp(common.Big0) < 0 {
|
||||
if evm.interpreter.intPool.get().Mul(x, y).Cmp(common.Big0) < 0 {
|
||||
n.SetInt64(-1)
|
||||
} else {
|
||||
n.SetInt64(1)
|
||||
@ -73,20 +87,22 @@ func opSdiv(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(U256(x.Mod(x, y)))
|
||||
}
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
@ -104,16 +120,20 @@ func opSmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
|
||||
|
||||
stack.push(U256(res))
|
||||
}
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opExp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
base, exponent := stack.pop(), stack.pop()
|
||||
stack.push(math.Exp(base, exponent))
|
||||
|
||||
evm.interpreter.intPool.put(base, exponent)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSignExtend(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
back := stack.pop()
|
||||
if back.Cmp(big.NewInt(31)) < 0 {
|
||||
bit := uint(back.Uint64()*8 + 7)
|
||||
@ -128,198 +148,231 @@ func opSignExtend(pc *uint64, env *EVM, contract *Contract, memory *Memory, stac
|
||||
|
||||
stack.push(U256(num))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(back)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNot(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opNot(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x := stack.pop()
|
||||
stack.push(U256(x.Not(x)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opLt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opLt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSlt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(S256(y)) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSgt(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opEq(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x, y)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opIszero(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x := stack.pop()
|
||||
if x.Cmp(common.Big0) > 0 {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(1))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(x)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAnd(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opAnd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.And(x, y))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
func opOr(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opOr(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Or(x, y))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
func opXor(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opXor(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Xor(x, y))
|
||||
|
||||
evm.interpreter.intPool.put(y)
|
||||
return nil, nil
|
||||
}
|
||||
func opByte(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opByte(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
th, val := stack.pop(), stack.pop()
|
||||
if th.Cmp(big.NewInt(32)) < 0 {
|
||||
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||
byte := evm.interpreter.intPool.get().SetInt64(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||
stack.push(byte)
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(th, val)
|
||||
return nil, nil
|
||||
}
|
||||
func opAddmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opAddmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
if z.Cmp(bigZero) > 0 {
|
||||
add := x.Add(x, y)
|
||||
add.Mod(add, z)
|
||||
stack.push(U256(add))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(y, z)
|
||||
return nil, nil
|
||||
}
|
||||
func opMulmod(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMulmod(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
if z.Cmp(bigZero) > 0 {
|
||||
mul := x.Mul(x, y)
|
||||
mul.Mod(mul, z)
|
||||
stack.push(U256(mul))
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(y, z)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSha3(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSha3(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
data := memory.Get(offset.Int64(), size.Int64())
|
||||
hash := crypto.Keccak256(data)
|
||||
|
||||
if env.vmConfig.EnablePreimageRecording {
|
||||
env.StateDB.AddPreimage(common.BytesToHash(hash), data)
|
||||
if evm.vmConfig.EnablePreimageRecording {
|
||||
evm.StateDB.AddPreimage(common.BytesToHash(hash), data)
|
||||
}
|
||||
|
||||
stack.push(common.BytesToBig(hash))
|
||||
|
||||
evm.interpreter.intPool.put(offset, size)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opAddress(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(common.Bytes2Big(contract.Address().Bytes()))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBalance(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
balance := env.StateDB.GetBalance(addr)
|
||||
balance := evm.StateDB.GetBalance(addr)
|
||||
|
||||
stack.push(new(big.Int).Set(balance))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opOrigin(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(env.Origin.Big())
|
||||
func opOrigin(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.Origin.Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCaller(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opCaller(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(contract.Caller().Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallValue(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(contract.value))
|
||||
func opCallValue(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().Set(contract.value))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataLoad(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opCalldataLoad(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(big.NewInt(int64(len(contract.Input))))
|
||||
func opCalldataSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().SetInt64(int64(len(contract.Input))))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCalldataCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opCalldataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
|
||||
|
||||
evm.interpreter.intPool.put(mOff, cOff, l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
l := big.NewInt(int64(env.StateDB.GetCodeSize(addr)))
|
||||
func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
a := stack.pop()
|
||||
|
||||
addr := common.BigToAddress(a)
|
||||
a.SetInt64(int64(evm.StateDB.GetCodeSize(addr)))
|
||||
stack.push(a)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
l := evm.interpreter.intPool.get().SetInt64(int64(len(contract.Code)))
|
||||
stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeSize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
l := big.NewInt(int64(len(contract.Code)))
|
||||
stack.push(l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
@ -328,113 +381,129 @@ func opCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack
|
||||
codeCopy := getData(contract.Code, cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
|
||||
evm.interpreter.intPool.put(mOff, cOff, l)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opExtCodeCopy(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
addr = common.BigToAddress(stack.pop())
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
|
||||
codeCopy := getData(evm.StateDB.GetCode(addr), cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
|
||||
evm.interpreter.intPool.put(mOff, cOff, l)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasprice(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(env.GasPrice))
|
||||
func opGasprice(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().Set(evm.GasPrice))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opBlockhash(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opBlockhash(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
num := stack.pop()
|
||||
|
||||
n := new(big.Int).Sub(env.BlockNumber, common.Big257)
|
||||
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
|
||||
stack.push(env.GetHash(num.Uint64()).Big())
|
||||
n := evm.interpreter.intPool.get().Sub(evm.BlockNumber, common.Big257)
|
||||
if num.Cmp(n) > 0 && num.Cmp(evm.BlockNumber) < 0 {
|
||||
stack.push(evm.GetHash(num.Uint64()).Big())
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(num, n)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCoinbase(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(env.Coinbase.Big())
|
||||
func opCoinbase(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.Coinbase.Big())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opTimestamp(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.Time)))
|
||||
func opTimestamp(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(evm.Time)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opNumber(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.BlockNumber)))
|
||||
func opNumber(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(evm.BlockNumber)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDifficulty(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.Difficulty)))
|
||||
func opDifficulty(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(evm.Difficulty)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGasLimit(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(env.GasLimit)))
|
||||
func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(U256(new(big.Int).Set(evm.GasLimit)))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.pop()
|
||||
func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
evm.interpreter.intPool.put(stack.pop())
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset := stack.pop()
|
||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
||||
stack.push(val)
|
||||
|
||||
evm.interpreter.intPool.put(offset)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// pop value of the stack
|
||||
mStart, val := stack.pop(), stack.pop()
|
||||
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
|
||||
|
||||
evm.interpreter.intPool.put(mStart, val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMstore8(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
off, val := stack.pop().Int64(), stack.pop().Int64()
|
||||
memory.store[off] = byte(val & 0xff)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSload(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := env.StateDB.GetState(contract.Address(), loc).Big()
|
||||
val := evm.StateDB.GetState(contract.Address(), loc).Big()
|
||||
stack.push(val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSstore(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := stack.pop()
|
||||
env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
|
||||
evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
|
||||
|
||||
evm.interpreter.intPool.put(val)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opJump(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opJump(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
pos := stack.pop()
|
||||
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
|
||||
nop := contract.GetOp(pos.Uint64())
|
||||
return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos)
|
||||
}
|
||||
*pc = pos.Uint64()
|
||||
|
||||
evm.interpreter.intPool.put(pos)
|
||||
return nil, nil
|
||||
}
|
||||
func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opJumpi(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
pos, cond := stack.pop(), stack.pop()
|
||||
if cond.Cmp(common.BigTrue) >= 0 {
|
||||
if !contract.jumpdests.has(contract.CodeHash, contract.Code, pos) {
|
||||
@ -445,57 +514,62 @@ func opJumpi(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *St
|
||||
} else {
|
||||
*pc++
|
||||
}
|
||||
|
||||
evm.interpreter.intPool.put(pos, cond)
|
||||
return nil, nil
|
||||
}
|
||||
func opJumpdest(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opJumpdest(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opPc(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).SetUint64(*pc))
|
||||
func opPc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(*pc))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opMsize(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(big.NewInt(int64(memory.Len())))
|
||||
func opMsize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().SetInt64(int64(memory.Len())))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opGas(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(new(big.Int).Set(contract.Gas))
|
||||
func opGas(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.push(evm.interpreter.intPool.get().SetUint64(contract.Gas))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCreate(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opCreate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
var (
|
||||
value = stack.pop()
|
||||
offset, size = stack.pop(), stack.pop()
|
||||
input = memory.Get(offset.Int64(), size.Int64())
|
||||
gas = new(big.Int).Set(contract.Gas)
|
||||
gas = contract.Gas
|
||||
)
|
||||
if env.ChainConfig().IsEIP150(env.BlockNumber) {
|
||||
gas.Div(gas, n64)
|
||||
gas = gas.Sub(contract.Gas, gas)
|
||||
if evm.ChainConfig().IsEIP150(evm.BlockNumber) {
|
||||
gas -= gas / 64
|
||||
}
|
||||
|
||||
contract.UseGas(gas)
|
||||
_, addr, suberr := env.Create(contract, input, gas, value)
|
||||
_, addr, returnGas, suberr := evm.Create(contract, input, gas, value)
|
||||
// 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 env.ChainConfig().IsHomestead(env.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
|
||||
if evm.ChainConfig().IsHomestead(evm.BlockNumber) && suberr == ErrCodeStoreOutOfGas {
|
||||
stack.push(new(big.Int))
|
||||
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(addr.Big())
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
|
||||
evm.interpreter.intPool.put(value, offset, size)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop()
|
||||
func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop().Uint64()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
value = U256(value)
|
||||
@ -509,25 +583,26 @@ func opCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Sta
|
||||
// Get the arguments from the memory
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if len(value.Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallStipend)
|
||||
if value.BitLen() > 0 {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, err := env.Call(contract, address, args, gas, value)
|
||||
|
||||
ret, returnGas, err := evm.Call(contract, address, args, gas, value)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
|
||||
evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop()
|
||||
func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
gas := stack.pop().Uint64()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
value = U256(value)
|
||||
@ -541,12 +616,11 @@ func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack
|
||||
// Get the arguments from the memory
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
|
||||
if len(value.Bytes()) > 0 {
|
||||
gas.Add(gas, params.CallStipend)
|
||||
if value.BitLen() > 0 {
|
||||
gas += params.CallStipend
|
||||
}
|
||||
|
||||
ret, err := env.CallCode(contract, address, args, gas, value)
|
||||
|
||||
ret, returnGas, err := evm.CallCode(contract, address, args, gas, value)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
|
||||
@ -555,46 +629,54 @@ func opCallCode(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack
|
||||
|
||||
memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
|
||||
evm.interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opDelegateCall(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// if not homestead return an error. DELEGATECALL is not supported
|
||||
// during pre-homestead.
|
||||
if !env.ChainConfig().IsHomestead(env.BlockNumber) {
|
||||
if !evm.ChainConfig().IsHomestead(evm.BlockNumber) {
|
||||
return nil, fmt.Errorf("invalid opcode %x", DELEGATECALL)
|
||||
}
|
||||
|
||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop().Uint64(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
|
||||
toAddr := common.BigToAddress(to)
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
ret, err := env.DelegateCall(contract, toAddr, args, gas)
|
||||
|
||||
ret, returnGas, err := evm.DelegateCall(contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
stack.push(big.NewInt(1))
|
||||
memory.Set(outOffset.Uint64(), outSize.Uint64(), ret)
|
||||
}
|
||||
contract.Gas += returnGas
|
||||
|
||||
evm.interpreter.intPool.put(to, inOffset, inSize, outOffset, outSize)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opReturn(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
ret := memory.GetPtr(offset.Int64(), size.Int64())
|
||||
|
||||
evm.interpreter.intPool.put(offset, size)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func opStop(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
balance := env.StateDB.GetBalance(contract.Address())
|
||||
env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
balance := evm.StateDB.GetBalance(contract.Address())
|
||||
evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
|
||||
env.StateDB.Suicide(contract.Address())
|
||||
evm.StateDB.Suicide(contract.Address())
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@ -603,7 +685,7 @@ func opSuicide(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *
|
||||
|
||||
// make log instruction function
|
||||
func makeLog(size int) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
topics := make([]common.Hash, size)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < size; i++ {
|
||||
@ -611,22 +693,24 @@ func makeLog(size int) executionFunc {
|
||||
}
|
||||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
env.StateDB.AddLog(&types.Log{
|
||||
evm.StateDB.AddLog(&types.Log{
|
||||
Address: 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: env.BlockNumber.Uint64(),
|
||||
BlockNumber: evm.BlockNumber.Uint64(),
|
||||
})
|
||||
|
||||
evm.interpreter.intPool.put(mStart, mSize)
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, bsize *big.Int) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
|
||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
byts := getData(contract.Code, evm.interpreter.intPool.get().SetUint64(*pc+1), bsize)
|
||||
stack.push(common.Bytes2Big(byts))
|
||||
*pc += size
|
||||
return nil, nil
|
||||
@ -635,7 +719,7 @@ func makePush(size uint64, bsize *big.Int) executionFunc {
|
||||
|
||||
// make push instruction function
|
||||
func makeDup(size int64) executionFunc {
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.dup(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
@ -645,7 +729,7 @@ func makeDup(size int64) executionFunc {
|
||||
func makeSwap(size int64) executionFunc {
|
||||
// switch n + 1 otherwise n would be swapped with n
|
||||
size += 1
|
||||
return func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
return func(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
stack.swap(int(size))
|
||||
return nil, nil
|
||||
}
|
||||
|
15
core/vm/int_pool_verifier.go
Normal file
15
core/vm/int_pool_verifier.go
Normal file
@ -0,0 +1,15 @@
|
||||
// +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))
|
||||
}
|
||||
}
|
||||
}
|
7
core/vm/int_pool_verifier_empty.go
Normal file
7
core/vm/int_pool_verifier_empty.go
Normal file
@ -0,0 +1,7 @@
|
||||
// +build !VERIFY_EVM_INTEGER_POOL
|
||||
|
||||
package vm
|
||||
|
||||
const verifyPool = false
|
||||
|
||||
func verifyIntegerPool(ip *intPool) {}
|
@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
@ -60,6 +61,7 @@ type Interpreter struct {
|
||||
env *EVM
|
||||
cfg Config
|
||||
gasTable params.GasTable
|
||||
intPool *intPool
|
||||
}
|
||||
|
||||
// NewInterpreter returns a new instance of the Interpreter.
|
||||
@ -75,6 +77,7 @@ func NewInterpreter(env *EVM, cfg Config) *Interpreter {
|
||||
env: env,
|
||||
cfg: cfg,
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber),
|
||||
intPool: newIntPool(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,14 +109,18 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
|
||||
// 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.
|
||||
pc = uint64(0) // program counter
|
||||
cost *big.Int
|
||||
cost uint64
|
||||
)
|
||||
contract.Input = input
|
||||
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if err != nil && evm.cfg.Debug {
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
// XXX For debugging
|
||||
//fmt.Printf("%04d: %8v cost = %-8d stack = %-8d ERR = %v\n", pc, op, cost, stack.len(), err)
|
||||
// TODO update the tracer
|
||||
g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -126,7 +133,7 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
|
||||
}
|
||||
|
||||
// The Interpreter main run loop (contextual). This loop runs until either an
|
||||
// explicit STOP, RETURN or SUICIDE is executed, an error accured during
|
||||
// explicit STOP, RETURN or SUICIDE is executed, an error occurred during
|
||||
// the execution of one of the operations or until the evm.done is set by
|
||||
// the parent context.Context.
|
||||
for atomic.LoadInt32(&evm.env.abort) == 0 {
|
||||
@ -147,34 +154,47 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var memorySize *big.Int
|
||||
var memorySize uint64
|
||||
// calculate the new memory size and expand the memory to fit
|
||||
// the operation
|
||||
if operation.memorySize != nil {
|
||||
memorySize = operation.memorySize(stack)
|
||||
memSize, overflow := bigUint64(operation.memorySize(stack))
|
||||
if overflow {
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
// memory is expanded in words of 32 bytes. Gas
|
||||
// is also calculated in words.
|
||||
memorySize.Mul(toWordSize(memorySize), big.NewInt(32))
|
||||
if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
|
||||
return nil, errGasUintOverflow
|
||||
}
|
||||
}
|
||||
|
||||
if !evm.cfg.DisableGasMetering {
|
||||
// consume the gas and return an error if not enough gas is available.
|
||||
// cost is explicitly set so that the capture state defer method cas get the proper cost
|
||||
cost = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
|
||||
if !contract.UseGas(cost) {
|
||||
cost, err = operation.gasCost(evm.gasTable, evm.env, contract, stack, mem, memorySize)
|
||||
if err != nil || !contract.UseGas(cost) {
|
||||
return nil, ErrOutOfGas
|
||||
}
|
||||
}
|
||||
if memorySize != nil {
|
||||
mem.Resize(memorySize.Uint64())
|
||||
if memorySize > 0 {
|
||||
mem.Resize(memorySize)
|
||||
}
|
||||
|
||||
if evm.cfg.Debug {
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.depth, err)
|
||||
g, c := new(big.Int).SetUint64(contract.Gas), new(big.Int).SetUint64(cost)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, g, c, mem, stack, contract, evm.env.depth, err)
|
||||
}
|
||||
// XXX For debugging
|
||||
//fmt.Printf("%04d: %8v cost = %-8d stack = %-8d\n", pc, op, cost, stack.len())
|
||||
|
||||
// execute the operation
|
||||
res, err := operation.execute(&pc, evm.env, contract, mem, stack)
|
||||
// verifyPool is a build flag. Pool verification makes sure the integrity
|
||||
// of the integer pool by comparing values to a default value.
|
||||
if verifyPool {
|
||||
verifyIntegerPool(evm.intPool)
|
||||
}
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// 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
|
||||
@ -16,7 +16,34 @@
|
||||
|
||||
package vm
|
||||
|
||||
// VirtualMachine is an EVM interface
|
||||
type VirtualMachine interface {
|
||||
Run(*Contract, []byte) ([]byte, error)
|
||||
import "math/big"
|
||||
|
||||
var checkVal = big.NewInt(-42)
|
||||
|
||||
// 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()}
|
||||
}
|
||||
|
||||
func (p *intPool) get() *big.Int {
|
||||
if p.pool.len() > 0 {
|
||||
return p.pool.pop()
|
||||
}
|
||||
return new(big.Int)
|
||||
}
|
||||
func (p *intPool) put(is ...*big.Int) {
|
||||
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)
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@ -24,11 +25,13 @@ import (
|
||||
|
||||
type (
|
||||
executionFunc func(pc *uint64, env *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, *big.Int) *big.Int
|
||||
gasFunc func(params.GasTable, *EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
stackValidationFunc func(*Stack) error
|
||||
memorySizeFunc func(*Stack) *big.Int
|
||||
)
|
||||
|
||||
var errGasUintOverflow = errors.New("gas uint64 overflow")
|
||||
|
||||
type operation struct {
|
||||
// op is the operation function
|
||||
execute executionFunc
|
||||
@ -431,7 +434,7 @@ func NewJumpTable() [256]operation {
|
||||
},
|
||||
STOP: {
|
||||
execute: opStop,
|
||||
gasCost: constGasFunc(Zero),
|
||||
gasCost: constGasFunc(0),
|
||||
validateStack: makeStackFunc(0, 0),
|
||||
halts: true,
|
||||
valid: true,
|
||||
|
@ -56,7 +56,7 @@ func TestStoreCapture(t *testing.T) {
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
|
||||
)
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(big.NewInt(0))
|
||||
@ -78,7 +78,7 @@ func TestStorageCapture(t *testing.T) {
|
||||
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
|
||||
var (
|
||||
ref = &dummyContractRef{}
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int))
|
||||
contract = NewContract(ref, ref, new(big.Int), 0)
|
||||
env = NewEVM(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
|
@ -21,10 +21,11 @@ import "fmt"
|
||||
// Memory implements a simple memory model for the ethereum virtual machine.
|
||||
type Memory struct {
|
||||
store []byte
|
||||
lastGasCost uint64
|
||||
}
|
||||
|
||||
func NewMemory() *Memory {
|
||||
return &Memory{nil}
|
||||
return &Memory{}
|
||||
}
|
||||
|
||||
// Set sets offset + size to value
|
||||
|
@ -36,7 +36,7 @@ func NewEnv(cfg *Config, state *state.StateDB) *vm.EVM {
|
||||
BlockNumber: cfg.BlockNumber,
|
||||
Time: cfg.Time,
|
||||
Difficulty: cfg.Difficulty,
|
||||
GasLimit: cfg.GasLimit,
|
||||
GasLimit: new(big.Int).SetUint64(cfg.GasLimit),
|
||||
GasPrice: new(big.Int),
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
@ -37,7 +38,7 @@ type Config struct {
|
||||
Coinbase common.Address
|
||||
BlockNumber *big.Int
|
||||
Time *big.Int
|
||||
GasLimit *big.Int
|
||||
GasLimit uint64
|
||||
GasPrice *big.Int
|
||||
Value *big.Int
|
||||
DisableJit bool // "disable" so it's enabled by default
|
||||
@ -68,8 +69,8 @@ func setDefaults(cfg *Config) {
|
||||
if cfg.Time == nil {
|
||||
cfg.Time = big.NewInt(time.Now().Unix())
|
||||
}
|
||||
if cfg.GasLimit == nil {
|
||||
cfg.GasLimit = new(big.Int).Set(common.MaxBig)
|
||||
if cfg.GasLimit == 0 {
|
||||
cfg.GasLimit = math.MaxUint64
|
||||
}
|
||||
if cfg.GasPrice == nil {
|
||||
cfg.GasPrice = new(big.Int)
|
||||
@ -112,7 +113,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
ret, err := vmenv.Call(
|
||||
ret, _, err := vmenv.Call(
|
||||
sender,
|
||||
receiver.Address(),
|
||||
input,
|
||||
@ -140,12 +141,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
|
||||
)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
return vmenv.Create(
|
||||
code, address, _, err := vmenv.Create(
|
||||
sender,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.Value,
|
||||
)
|
||||
return code, address, err
|
||||
}
|
||||
|
||||
// Call executes the code given by the contract's address. It will return the
|
||||
@ -160,7 +162,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
||||
|
||||
sender := cfg.State.GetOrNewStateObject(cfg.Origin)
|
||||
// Call the code with the given configuration.
|
||||
ret, err := vmenv.Call(
|
||||
ret, _, err := vmenv.Call(
|
||||
sender,
|
||||
address,
|
||||
input,
|
||||
|
@ -39,8 +39,8 @@ func TestDefaults(t *testing.T) {
|
||||
if cfg.Time == nil {
|
||||
t.Error("expected time to be non nil")
|
||||
}
|
||||
if cfg.GasLimit == nil {
|
||||
t.Error("expected time to be non nil")
|
||||
if cfg.GasLimit == 0 {
|
||||
t.Error("didn't expect gaslimit to be zero")
|
||||
}
|
||||
if cfg.GasPrice == nil {
|
||||
t.Error("expected time to be non nil")
|
||||
|
@ -12,8 +12,8 @@ func makeStackFunc(pop, push int) stackValidationFunc {
|
||||
return err
|
||||
}
|
||||
|
||||
if push > 0 && int64(stack.len()-pop+push) > params.StackLimit.Int64() {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit.Int64())
|
||||
if push > 0 && stack.len()-pop+push > int(params.StackLimit) {
|
||||
return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -106,14 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
func (b *EthApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
statedb := state.(EthApiState).state
|
||||
from := statedb.GetOrNewStateObject(msg.From())
|
||||
from.SetBalance(common.MaxBig)
|
||||
vmError := func() error { return nil }
|
||||
|
||||
context := core.NewEVMContext(msg, header, b.eth.BlockChain())
|
||||
return vm.NewEVM(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
|
||||
return vm.NewEVM(context, statedb, b.eth.chainConfig, vmCfg), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
|
@ -69,7 +69,7 @@ func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Add
|
||||
// against the pending block, not the stable head of the chain.
|
||||
func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) {
|
||||
out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum))
|
||||
return common.FromHex(out), err
|
||||
return out, err
|
||||
}
|
||||
|
||||
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
||||
@ -77,7 +77,7 @@ func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg
|
||||
// against the pending block, not the stable head of the chain.
|
||||
func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||
out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber)
|
||||
return common.FromHex(out), err
|
||||
return out, err
|
||||
}
|
||||
|
||||
func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs {
|
||||
|
@ -49,7 +49,7 @@ var (
|
||||
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
|
||||
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
|
||||
|
||||
MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
|
||||
MaxForkAncestry = 3 * params.EpochDuration // Maximum chain reorganisation
|
||||
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
|
||||
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
|
||||
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
|
||||
|
@ -119,7 +119,7 @@ func (dl *downloadTester) makeChain(n int, seed byte, parent *types.Block, paren
|
||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||
if parent == dl.genesis && i%3 == 0 {
|
||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common
|
||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||
if parent == genesis && i%3 == 0 {
|
||||
signer := types.MakeSigner(params.TestChainConfig, block.Number())
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, nil, nil), signer, testKey)
|
||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), new(big.Int).SetUint64(params.TxGas), nil, nil), signer, testKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||
|
||||
// Tests that protocol versions and modes of operations are matched up properly.
|
||||
func TestProtocolCompatibility(t *testing.T) {
|
||||
// Define the compatibility chart
|
||||
@ -312,13 +314,13 @@ func testGetNodeData(t *testing.T, protocol int) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
@ -404,13 +406,13 @@ func testGetReceipt(t *testing.T, protocol int) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBank.Address), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(block.TxNonce(acc1Addr), acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
|
||||
block.AddTx(tx1)
|
||||
block.AddTx(tx2)
|
||||
case 2:
|
||||
|
@ -47,6 +47,8 @@ import (
|
||||
|
||||
const defaultGas = 90000
|
||||
|
||||
var emptyHex = "0x"
|
||||
|
||||
// PublicEthereumAPI provides an API to access Ethereum related information.
|
||||
// It offers only methods that operate on public data that is freely available to anyone.
|
||||
type PublicEthereumAPI struct {
|
||||
@ -503,58 +505,76 @@ type CallArgs struct {
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) {
|
||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config) ([]byte, *big.Int, error) {
|
||||
defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now())
|
||||
|
||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
||||
if state == nil || err != nil {
|
||||
return "0x", common.Big0, err
|
||||
return nil, common.Big0, err
|
||||
}
|
||||
|
||||
// Set the account address to interact with
|
||||
var addr common.Address
|
||||
if args.From == (common.Address{}) {
|
||||
// Set sender address or use a default if none specified
|
||||
addr := args.From
|
||||
if addr == (common.Address{}) {
|
||||
accounts := s.b.AccountManager().Accounts()
|
||||
if len(accounts) == 0 {
|
||||
addr = common.Address{}
|
||||
} else {
|
||||
if len(accounts) > 0 {
|
||||
addr = accounts[0].Address
|
||||
}
|
||||
} else {
|
||||
addr = args.From
|
||||
}
|
||||
|
||||
// Assemble the CALL invocation
|
||||
// Set default gas & gas price if none were set
|
||||
gas, gasPrice := args.Gas.ToInt(), args.GasPrice.ToInt()
|
||||
if gas.Cmp(common.Big0) == 0 {
|
||||
if gas.BitLen() == 0 {
|
||||
gas = big.NewInt(50000000)
|
||||
}
|
||||
if gasPrice.Cmp(common.Big0) == 0 {
|
||||
if gasPrice.BitLen() == 0 {
|
||||
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
|
||||
}
|
||||
|
||||
// Create new call message
|
||||
msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
|
||||
|
||||
// Execute the call and return
|
||||
vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header)
|
||||
// Setup context so it may be cancelled the call has completed
|
||||
// or, in case of unmetered gas, setup a context with a timeout.
|
||||
var cancel context.CancelFunc
|
||||
if vmCfg.DisableGasMetering {
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Second*5)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
}
|
||||
// Make sure the context is cancelled when the call has completed
|
||||
// this makes sure resources are cleaned up.
|
||||
defer func() { cancel() }()
|
||||
|
||||
// Get a new instance of the EVM.
|
||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
|
||||
if err != nil {
|
||||
return "0x", common.Big0, err
|
||||
return nil, common.Big0, err
|
||||
}
|
||||
// Wait for the context to be done and cancel the evm. Even if the
|
||||
// EVM has finished, cancelling may be done (repeatedly)
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
evm.Cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
// Setup the gas pool (also for unmetered requests)
|
||||
// and apply the message.
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
res, gas, err := core.ApplyMessage(evm, msg, gp)
|
||||
if err := vmError(); err != nil {
|
||||
return "0x", common.Big0, err
|
||||
return nil, common.Big0, err
|
||||
}
|
||||
if len(res) == 0 { // backwards compatibility
|
||||
return "0x", gas, err
|
||||
}
|
||||
return common.ToHex(res), gas, err
|
||||
return res, gas, err
|
||||
}
|
||||
|
||||
// Call executes the given transaction on the state for the given block number.
|
||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) {
|
||||
result, _, err := s.doCall(ctx, args, blockNr)
|
||||
return result, err
|
||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||
result, _, err := s.doCall(ctx, args, blockNr, vm.Config{DisableGasMetering: true})
|
||||
return (hexutil.Bytes)(result), err
|
||||
}
|
||||
|
||||
// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction.
|
||||
@ -576,7 +596,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*
|
||||
mid := (hi + lo) / 2
|
||||
(*big.Int)(&args.Gas).SetUint64(mid)
|
||||
|
||||
_, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber)
|
||||
_, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber, vm.Config{})
|
||||
|
||||
// If the transaction became invalid or used all the gas (failed), raise the gas limit
|
||||
if err != nil || gas.Cmp((*big.Int)(&args.Gas)) == 0 {
|
||||
|
@ -51,7 +51,7 @@ type Backend interface {
|
||||
GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error)
|
||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||
GetTd(blockHash common.Hash) *big.Int
|
||||
GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (*vm.EVM, func() error, error)
|
||||
GetEVM(ctx context.Context, msg core.Message, state State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error)
|
||||
// TxPool API
|
||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||
RemoveTx(txHash common.Hash)
|
||||
|
@ -45,7 +45,7 @@ func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
func runTrace(tracer *JavascriptTracer) (interface{}, error) {
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), big.NewInt(10000))
|
||||
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
|
||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
|
||||
|
||||
_, err := env.Interpreter().Run(contract, []byte{})
|
||||
@ -134,7 +134,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
||||
}
|
||||
|
||||
env := vm.NewEVM(vm.Context{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
|
||||
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), big.NewInt(0))
|
||||
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
|
||||
|
||||
tracer.CaptureState(env, 0, 0, big.NewInt(0), big.NewInt(0), nil, nil, contract, 0, nil)
|
||||
timeout := errors.New("stahp")
|
||||
|
@ -88,7 +88,7 @@ func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) {
|
||||
stateDb := state.(*light.LightState).Copy()
|
||||
addr := msg.From()
|
||||
from, err := stateDb.GetOrNewStateObject(ctx, addr)
|
||||
@ -99,7 +99,7 @@ func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state et
|
||||
|
||||
vmstate := light.NewVMState(ctx, stateDb)
|
||||
context := core.NewEVMContext(msg, header, b.eth.blockchain)
|
||||
return vm.NewEVM(context, vmstate, b.eth.chainConfig, vm.Config{}), vmstate.Error, nil
|
||||
return vm.NewEVM(context, vmstate, b.eth.chainConfig, vmCfg), vmstate.Error, nil
|
||||
}
|
||||
|
||||
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
|
@ -57,6 +57,8 @@ var (
|
||||
testContractDeployed = uint64(2)
|
||||
|
||||
testBufLimit = uint64(100)
|
||||
|
||||
bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||
)
|
||||
|
||||
/*
|
||||
@ -80,15 +82,15 @@ func testChainGen(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
// acc1Addr creates a test contract.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
nonce := block.TxNonce(acc1Addr)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
|
||||
nonce++
|
||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(200000), big.NewInt(0), testContractCode), signer, acc1Key)
|
||||
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
|
||||
|
@ -49,6 +49,8 @@ var (
|
||||
|
||||
testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
|
||||
testContractAddr common.Address
|
||||
|
||||
bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||
)
|
||||
|
||||
type testOdr struct {
|
||||
@ -205,15 +207,15 @@ func testChainGen(i int, block *core.BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, the test bank sends account #1 some ether.
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
block.AddTx(tx)
|
||||
case 1:
|
||||
// In block 2, the test bank sends some more ether to account #1.
|
||||
// acc1Addr passes it on to account #2.
|
||||
// acc1Addr creates a test contract.
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, testBankKey)
|
||||
nonce := block.TxNonce(acc1Addr)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), bigTxGas, nil, nil), signer, acc1Key)
|
||||
nonce++
|
||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), big.NewInt(1000000), big.NewInt(0), testContractCode), signer, acc1Key)
|
||||
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
|
||||
|
@ -77,7 +77,7 @@ func txPoolTestChainGen(i int, block *core.BlockGen) {
|
||||
|
||||
func TestTxPool(t *testing.T) {
|
||||
for i := range testTx {
|
||||
testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
|
||||
testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), bigTxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -171,7 +171,7 @@ func (self *Miner) HashRate() (tot int64) {
|
||||
}
|
||||
|
||||
func (self *Miner) SetExtra(extra []byte) error {
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
||||
return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
|
||||
}
|
||||
self.worker.setExtra(extra)
|
||||
|
@ -16,41 +16,35 @@
|
||||
|
||||
package params
|
||||
|
||||
import "math/big"
|
||||
|
||||
type GasTable struct {
|
||||
ExtcodeSize *big.Int
|
||||
ExtcodeCopy *big.Int
|
||||
Balance *big.Int
|
||||
SLoad *big.Int
|
||||
Calls *big.Int
|
||||
Suicide *big.Int
|
||||
ExtcodeSize uint64
|
||||
ExtcodeCopy uint64
|
||||
Balance uint64
|
||||
SLoad uint64
|
||||
Calls uint64
|
||||
Suicide uint64
|
||||
|
||||
ExpByte *big.Int
|
||||
ExpByte uint64
|
||||
|
||||
// CreateBySuicide occurs when the
|
||||
// refunded account is one that does
|
||||
// not exist. This logic is similar
|
||||
// to call. May be left nil. Nil means
|
||||
// not charged.
|
||||
CreateBySuicide *big.Int
|
||||
CreateBySuicide uint64
|
||||
}
|
||||
|
||||
var (
|
||||
// GasTableHomestead contain the gas prices for
|
||||
// the homestead phase.
|
||||
GasTableHomestead = GasTable{
|
||||
ExtcodeSize: big.NewInt(20),
|
||||
ExtcodeCopy: big.NewInt(20),
|
||||
Balance: big.NewInt(20),
|
||||
SLoad: big.NewInt(50),
|
||||
Calls: big.NewInt(40),
|
||||
Suicide: big.NewInt(0),
|
||||
ExpByte: big.NewInt(10),
|
||||
|
||||
// explicitly set to nil to indicate
|
||||
// this rule does not apply to homestead.
|
||||
CreateBySuicide: nil,
|
||||
ExtcodeSize: 20,
|
||||
ExtcodeCopy: 20,
|
||||
Balance: 20,
|
||||
SLoad: 50,
|
||||
Calls: 40,
|
||||
Suicide: 0,
|
||||
ExpByte: 10,
|
||||
}
|
||||
|
||||
// GasTableHomestead contain the gas re-prices for
|
||||
@ -58,26 +52,26 @@ var (
|
||||
//
|
||||
// TODO rename to GasTableEIP150
|
||||
GasTableHomesteadGasRepriceFork = GasTable{
|
||||
ExtcodeSize: big.NewInt(700),
|
||||
ExtcodeCopy: big.NewInt(700),
|
||||
Balance: big.NewInt(400),
|
||||
SLoad: big.NewInt(200),
|
||||
Calls: big.NewInt(700),
|
||||
Suicide: big.NewInt(5000),
|
||||
ExpByte: big.NewInt(10),
|
||||
ExtcodeSize: 700,
|
||||
ExtcodeCopy: 700,
|
||||
Balance: 400,
|
||||
SLoad: 200,
|
||||
Calls: 700,
|
||||
Suicide: 5000,
|
||||
ExpByte: 10,
|
||||
|
||||
CreateBySuicide: big.NewInt(25000),
|
||||
CreateBySuicide: 25000,
|
||||
}
|
||||
|
||||
GasTableEIP158 = GasTable{
|
||||
ExtcodeSize: big.NewInt(700),
|
||||
ExtcodeCopy: big.NewInt(700),
|
||||
Balance: big.NewInt(400),
|
||||
SLoad: big.NewInt(200),
|
||||
Calls: big.NewInt(700),
|
||||
Suicide: big.NewInt(5000),
|
||||
ExpByte: big.NewInt(50),
|
||||
ExtcodeSize: 700,
|
||||
ExtcodeCopy: 700,
|
||||
Balance: 400,
|
||||
SLoad: 200,
|
||||
Calls: 700,
|
||||
Suicide: 5000,
|
||||
ExpByte: 50,
|
||||
|
||||
CreateBySuicide: big.NewInt(25000),
|
||||
CreateBySuicide: 25000,
|
||||
}
|
||||
)
|
||||
|
@ -18,56 +18,58 @@ package params
|
||||
|
||||
import "math/big"
|
||||
|
||||
var (
|
||||
MaximumExtraDataSize = big.NewInt(32) // Maximum size extra data may be after Genesis.
|
||||
ExpByteGas = big.NewInt(10) // Times ceil(log256(exponent)) for the EXP instruction.
|
||||
SloadGas = big.NewInt(50) // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
|
||||
CallValueTransferGas = big.NewInt(9000) // Paid for CALL when the value transfer is non-zero.
|
||||
CallNewAccountGas = big.NewInt(25000) // Paid for CALL when the destination address didn't exist prior.
|
||||
TxGas = big.NewInt(21000) // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
|
||||
TxGasContractCreation = big.NewInt(53000) // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
|
||||
TxDataZeroGas = big.NewInt(4) // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
|
||||
DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
|
||||
QuadCoeffDiv = big.NewInt(512) // Divisor for the quadratic particle of the memory cost equation.
|
||||
GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
|
||||
DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
|
||||
SstoreSetGas = big.NewInt(20000) // Once per SLOAD operation.
|
||||
LogDataGas = big.NewInt(8) // Per byte in a LOG* operation's data.
|
||||
CallStipend = big.NewInt(2300) // Free gas given at beginning of call.
|
||||
EcrecoverGas = big.NewInt(3000) //
|
||||
Sha256WordGas = big.NewInt(12) //
|
||||
const (
|
||||
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
|
||||
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
|
||||
SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
|
||||
CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero.
|
||||
CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior.
|
||||
TxGas uint64 = 21000 // Per transaction not creating a contract. NOTE: Not payable on data of calls between transactions.
|
||||
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions.
|
||||
TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
|
||||
QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation.
|
||||
SstoreSetGas uint64 = 20000 // Once per SLOAD operation.
|
||||
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
|
||||
CallStipend uint64 = 2300 // Free gas given at beginning of call.
|
||||
EcrecoverGas uint64 = 3000 //
|
||||
Sha256WordGas uint64 = 12 //
|
||||
|
||||
MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
|
||||
GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block.
|
||||
TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target
|
||||
|
||||
Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
|
||||
Sha256Gas = big.NewInt(60) //
|
||||
IdentityWordGas = big.NewInt(3) //
|
||||
Sha3WordGas = big.NewInt(6) // Once per word of the SHA3 operation's data.
|
||||
SstoreResetGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness changes from zero.
|
||||
SstoreClearGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness doesn't change.
|
||||
SstoreRefundGas = big.NewInt(15000) // Once per SSTORE operation if the zeroness changes to zero.
|
||||
JumpdestGas = big.NewInt(1) // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
|
||||
IdentityGas = big.NewInt(15) //
|
||||
GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
|
||||
EpochDuration = big.NewInt(30000) // Duration between proof-of-work epochs.
|
||||
CallGas = big.NewInt(40) // Once per CALL operation & message call transaction.
|
||||
CreateDataGas = big.NewInt(200) //
|
||||
Ripemd160Gas = big.NewInt(600) //
|
||||
Ripemd160WordGas = big.NewInt(120) //
|
||||
MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
|
||||
CallCreateDepth = big.NewInt(1024) // Maximum depth of call/create stack.
|
||||
ExpGas = big.NewInt(10) // Once per EXP instruction.
|
||||
LogGas = big.NewInt(375) // Per LOG* operation.
|
||||
CopyGas = big.NewInt(3) //
|
||||
StackLimit = big.NewInt(1024) // Maximum size of VM stack allowed.
|
||||
TierStepGas = big.NewInt(0) // Once per operation, for a selection of them.
|
||||
LogTopicGas = big.NewInt(375) // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
||||
CreateGas = big.NewInt(32000) // Once per CREATE operation & contract-creation transaction.
|
||||
SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation.
|
||||
MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||
TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||
Sha3Gas uint64 = 30 // Once per SHA3 operation.
|
||||
Sha256Gas uint64 = 60 //
|
||||
IdentityWordGas uint64 = 3 //
|
||||
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data.
|
||||
SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero.
|
||||
SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change.
|
||||
SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero.
|
||||
JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
|
||||
IdentityGas uint64 = 15 //
|
||||
EpochDuration uint64 = 30000 // Duration between proof-of-work epochs.
|
||||
CallGas uint64 = 40 // Once per CALL operation & message call transaction.
|
||||
CreateDataGas uint64 = 200 //
|
||||
Ripemd160Gas uint64 = 600 //
|
||||
Ripemd160WordGas uint64 = 120 //
|
||||
CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack.
|
||||
ExpGas uint64 = 10 // Once per EXP instruction
|
||||
LogGas uint64 = 375 // Per LOG* operation.
|
||||
CopyGas uint64 = 3 //
|
||||
StackLimit uint64 = 1024 // Maximum size of VM stack allowed.
|
||||
TierStepGas uint64 = 0 // Once per operation, for a selection of them.
|
||||
LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
|
||||
CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction.
|
||||
SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation.
|
||||
MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||
TxDataNonZeroGas uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||
|
||||
MaxCodeSize = 24576
|
||||
)
|
||||
|
||||
var (
|
||||
GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
|
||||
MinGasLimit = big.NewInt(5000) // Minimum the gas limit may ever be.
|
||||
GenesisGasLimit = big.NewInt(4712388) // Gas limit of the Genesis block.
|
||||
TargetGasLimit = new(big.Int).Set(GenesisGasLimit) // The artificial target
|
||||
DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
|
||||
GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
|
||||
MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
|
||||
DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
|
||||
)
|
||||
|
@ -146,7 +146,7 @@ func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[s
|
||||
}
|
||||
|
||||
for name, test := range bt {
|
||||
if skipTest[name] {
|
||||
if skipTest[name] /*|| name != "CallingCanonicalContractFromFork_CALLCODE"*/ {
|
||||
glog.Infoln("Skipping block test", name)
|
||||
continue
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func runStateTests(chainConfig *params.ChainConfig, tests map[string]VmTest, ski
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] {
|
||||
if skipTest[name] /*|| name != "JUMPDEST_Attack"*/ {
|
||||
glog.Infoln("Skipping state test", name)
|
||||
continue
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ func runVmTests(tests map[string]VmTest, skipTests []string) error {
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
if skipTest[name] /*|| name != "loop_stacklimit_1021"*/ {
|
||||
if skipTest[name] /*|| name != "exp0"*/ {
|
||||
glog.Infoln("Skipping VM test", name)
|
||||
continue
|
||||
}
|
||||
@ -229,6 +229,6 @@ func RunVm(statedb *state.StateDB, env, exec map[string]string) ([]byte, []*type
|
||||
vm.PrecompiledContracts = make(map[common.Address]vm.PrecompiledContract)
|
||||
|
||||
environment, _ := NewEVMEnvironment(true, chainConfig, statedb, env, exec)
|
||||
ret, err := environment.Call(caller, to, data, gas, value)
|
||||
return ret, statedb.Logs(), gas, err
|
||||
ret, g, err := environment.Call(caller, to, data, gas.Uint64(), value)
|
||||
return ret, statedb.Logs(), new(big.Int).SetUint64(g), err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user