Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core/vm: improved EVM run loop & instruction calling #3378

Merged
merged 16 commits into from Jan 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion accounts/abi/bind/backends/simulated.go
Expand Up @@ -229,7 +229,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEnvironment(evmContext, statedb, chainConfig, vm.Config{})
vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return ret, gasUsed, err
Expand Down
21 changes: 9 additions & 12 deletions cmd/evm/main.go
Expand Up @@ -44,14 +44,6 @@ var (
Name: "debug",
Usage: "output full trace logs",
}
ForceJitFlag = cli.BoolFlag{
Name: "forcejit",
Usage: "forces jit compilation",
}
DisableJitFlag = cli.BoolFlag{
Name: "nojit",
Usage: "disabled jit compilation",
}
CodeFlag = cli.StringFlag{
Name: "code",
Usage: "EVM code",
Expand Down Expand Up @@ -95,15 +87,17 @@ var (
Name: "create",
Usage: "indicates the action should be create rather than call",
}
DisableGasMeteringFlag = cli.BoolFlag{
Name: "nogasmetering",
Usage: "disable gas metering",
}
)

func init() {
app.Flags = []cli.Flag{
CreateFlag,
DebugFlag,
VerbosityFlag,
ForceJitFlag,
DisableJitFlag,
SysStatFlag,
CodeFlag,
CodeFileFlag,
Expand All @@ -112,6 +106,7 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
DisableGasMeteringFlag,
}
app.Action = run
}
Expand Down Expand Up @@ -165,7 +160,8 @@ func run(ctx *cli.Context) error {
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
EVMConfig: vm.Config{
Tracer: logger,
Tracer: logger,
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
},
})
} else {
Expand All @@ -179,7 +175,8 @@ func run(ctx *cli.Context) error {
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
EVMConfig: vm.Config{
Tracer: logger,
Tracer: logger,
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
},
})
}
Expand Down
2 changes: 1 addition & 1 deletion core/chain_makers.go
Expand Up @@ -108,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
receipt, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
Expand Down
21 changes: 10 additions & 11 deletions core/state_processor.go
Expand Up @@ -74,37 +74,36 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
for i, tx := range block.Transactions() {
//fmt.Println("tx:", i)
statedb.StartRecord(tx.Hash(), block.Hash(), i)
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
if err != nil {
return nil, nil, nil, err
}
receipts = append(receipts, receipt)
allLogs = append(allLogs, logs...)
allLogs = append(allLogs, receipt.Logs...)
}
AccumulateRewards(statedb, header, block.Uncles())

return receipts, allLogs, totalUsedGas, err
}

// ApplyTransaction attempts to apply a transaction to the given state database
// and uses the input parameters for its environment.
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, *big.Int, error) {
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}
// Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
vmenv := vm.NewEVM(context, statedb, config, vm.Config{})
// Apply the transaction to the current state (included in the env)
_, gas, err := ApplyMessage(vmenv, msg, gp)
if err != nil {
return nil, nil, nil, err
return nil, nil, err
}

// Update the state with pending changes
Expand All @@ -125,7 +124,7 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s

glog.V(logger.Debug).Infoln(receipt)

return receipt, receipt.Logs, gas, err
return receipt, gas, err
}

// AccumulateRewards credits the coinbase of the given block with the
Expand Down
10 changes: 5 additions & 5 deletions core/state_transition.go
Expand Up @@ -57,7 +57,7 @@ type StateTransition struct {
data []byte
state vm.StateDB

env *vm.Environment
env *vm.EVM
}

// Message represents a message sent to a contract.
Expand Down Expand Up @@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
}

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition {
func NewStateTransition(env *vm.EVM, msg Message, gp *GasPool) *StateTransition {
return &StateTransition{
gp: gp,
env: env,
Expand All @@ -127,7 +127,7 @@ func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTra
// 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.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
func ApplyMessage(env *vm.EVM, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
st := NewStateTransition(env, msg, gp)

ret, _, gasUsed, err := st.TransitionDb()
Expand Down Expand Up @@ -159,7 +159,7 @@ func (self *StateTransition) to() vm.Account {

func (self *StateTransition) useGas(amount *big.Int) error {
if self.gas.Cmp(amount) < 0 {
return vm.OutOfGasError
return vm.ErrOutOfGas
}
self.gas.Sub(self.gas, amount)

Expand Down Expand Up @@ -233,7 +233,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
)
if contractCreation {
ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
if homestead && err == vm.CodeStoreOutOfGasError {
if homestead && err == vm.ErrCodeStoreOutOfGas {
self.gas = Big0
}
} else {
Expand Down
110 changes: 62 additions & 48 deletions core/vm/contracts.go
Expand Up @@ -26,64 +26,45 @@ import (
"github.com/ethereum/go-ethereum/params"
)

// PrecompiledAccount represents a native ethereum contract
type PrecompiledAccount struct {
Gas func(l int) *big.Int
fn func(in []byte) []byte
// Precompiled contract is the basic interface for native Go contracts. The implementation
// 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
Run(input []byte) []byte // Run runs the precompiled contract
}

// Call calls the native function
func (self PrecompiledAccount) Call(in []byte) []byte {
return self.fn(in)
// Precompiled contains the default set of ethereum contracts
var PrecompiledContracts = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256{},
common.BytesToAddress([]byte{3}): &ripemd160{},
common.BytesToAddress([]byte{4}): &dataCopy{},
}

// Precompiled contains the default set of ethereum contracts
var Precompiled = PrecompiledContracts()

// PrecompiledContracts returns the default set of precompiled ethereum
// contracts defined by the ethereum yellow paper.
func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{
// ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
return params.EcrecoverGas
}, ecrecoverFunc},

// SHA256
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.Sha256WordGas)
return n.Add(n, params.Sha256Gas)
}, sha256Func},

// RIPEMD160
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.Ripemd160WordGas)
return n.Add(n, params.Ripemd160Gas)
}, ripemd160Func},

string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32)
n.Mul(n, params.IdentityWordGas)

return n.Add(n, params.IdentityGas)
}, memCpy},
// RunPrecompile runs and evaluate the output of a precompiled contract defined in contracts.go
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(len(input))
if contract.UseGas(gas) {
ret = p.Run(input)

return ret, nil
} else {
return nil, ErrOutOfGas
}
}

func sha256Func(in []byte) []byte {
return crypto.Sha256(in)
}
// ECRECOVER implemented as a native contract
type ecrecover struct{}

func ripemd160Func(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
func (c *ecrecover) RequiredGas(inputSize int) *big.Int {
return params.EcrecoverGas
}

const ecRecoverInputLength = 128
func (c *ecrecover) Run(in []byte) []byte {
const ecRecoverInputLength = 128

func ecrecoverFunc(in []byte) []byte {
in = common.RightPadBytes(in, 128)
in = common.RightPadBytes(in, ecRecoverInputLength)
// "in" is (hash, v, r, s), each 32 bytes
// but for ecrecover we want (r, s, v)

Expand Down Expand Up @@ -114,6 +95,39 @@ func ecrecoverFunc(in []byte) []byte {
return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32)
}

func memCpy(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)
}
func (c *sha256) Run(in []byte) []byte {
return crypto.Sha256(in)
}

// 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)
}
func (c *ripemd160) Run(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
}

// 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)
}
func (c *dataCopy) Run(in []byte) []byte {
return in
}