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: big expmod - eip#198 progress #3633

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 81 additions & 11 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package vm

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
Expand All @@ -28,21 +30,31 @@ import (
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
type PrecompiledContract interface {
RequiredGas(inputSize int) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) []byte // Run runs the precompiled contract
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) []byte // Run runs the precompiled contract
}

// Precompiled contains the default set of ethereum contracts
// PrecompiledContracts 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{},
}

// PrecompiledContractsEIP198 contains the default set of ethereum contracts
// for EIP198.
var PrecompiledContractsEIP198 = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256{},
common.BytesToAddress([]byte{3}): &ripemd160{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModexp{},
}

// 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))
gas := p.RequiredGas(input)
if contract.UseGas(gas) {
ret = p.Run(input)

Expand All @@ -55,7 +67,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contr
// ECRECOVER implemented as a native contract
type ecrecover struct{}

func (c *ecrecover) RequiredGas(inputSize int) uint64 {
func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}

Expand Down Expand Up @@ -94,8 +106,8 @@ type sha256 struct{}
//
// 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) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256WordGas + params.Sha256Gas
}
func (c *sha256) Run(in []byte) []byte {
return crypto.Sha256(in)
Expand All @@ -108,8 +120,8 @@ type ripemd160 struct{}
//
// 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) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160WordGas + params.Ripemd160Gas
}
func (c *ripemd160) Run(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32)
Expand All @@ -122,9 +134,67 @@ type dataCopy struct{}
//
// 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) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityWordGas + params.IdentityGas
}
func (c *dataCopy) Run(in []byte) []byte {
return in
}

// bigModexp implements a native big integer exponential modular operation.
type bigModexp struct{}

// 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 *bigModexp) RequiredGas(input []byte) uint64 {
// TODO reword required gas to have error reporting and convert arithmetic
// to uint64.
if len(input) < 3*32 {
input = append(input, make([]byte, 3*32-len(input))...)
}
var (
baseLen = common.BytesToBig(input[:31])
expLen = common.BigMax(common.BytesToBig(input[32:64]), big.NewInt(1))
modLen = common.BytesToBig(input[65:97])
)
x := new(big.Int).Set(common.BigMax(baseLen, modLen))
x.Mul(x, x)
x.Mul(x, expLen)
x.Div(x, new(big.Int).SetUint64(params.QuadCoeffDiv))

return x.Uint64()
}

func (c *bigModexp) Run(input []byte) []byte {
if len(input) < 3*32 {
input = append(input, make([]byte, 3*32-len(input))...)
}
// why 32-byte? These values won't fit anyway
var (
baseLen = common.BytesToBig(input[:32]).Uint64()
expLen = common.BytesToBig(input[32:64]).Uint64()
modLen = common.BytesToBig(input[64:96]).Uint64()
)

input = input[96:]
if uint64(len(input)) < baseLen {
input = append(input, make([]byte, baseLen-uint64(len(input)))...)
}
base := common.BytesToBig(input[:baseLen])

input = input[baseLen:]
if uint64(len(input)) < expLen {
input = append(input, make([]byte, expLen-uint64(len(input)))...)
}
exp := common.BytesToBig(input[:expLen])

input = input[expLen:]
if uint64(len(input)) < modLen {
input = append(input, make([]byte, modLen-uint64(len(input)))...)
}
mod := common.BytesToBig(input[:modLen])

return base.Exp(base, exp, mod).Bytes()
}
25 changes: 20 additions & 5 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ type (
GetHashFunc func(uint64) common.Hash
)

// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
if contract.CodeAddr != nil {
precompiledContracts := PrecompiledContracts
if evm.ChainConfig().IsEIP198(evm.BlockNumber) {
precompiledContracts = PrecompiledContractsEIP198
}

if p := precompiledContracts[*contract.CodeAddr]; p != nil {
return RunPrecompiledContract(p, input, contract)
}
}

return evm.interpreter.Run(contract, input)
}

// Context provides the EVM with auxiliary information. Once provided it shouldn't be modified.
type Context struct {
// CanTransfer returns whether the account contains
Expand Down Expand Up @@ -136,7 +152,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

ret, err = evm.interpreter.Run(contract, input)
ret, err = run(evm, contract, input)
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
Expand Down Expand Up @@ -177,7 +193,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

ret, err = evm.interpreter.Run(contract, input)
ret, err = run(evm, contract, input)
if err != nil {
contract.UseGas(contract.Gas)

Expand Down Expand Up @@ -212,7 +228,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

ret, err = evm.interpreter.Run(contract, input)
ret, err = run(evm, contract, input)
if err != nil {
contract.UseGas(contract.Gas)

Expand Down Expand Up @@ -255,8 +271,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)

ret, err = evm.interpreter.Run(contract, nil)

ret, err = run(evm, contract, nil)
// check whether the max code size has been exceeded
maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
// if the contract creation ran successfully and no errors were returned
Expand Down
6 changes: 0 additions & 6 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ func (evm *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err e
evm.env.depth++
defer func() { evm.env.depth-- }()

if contract.CodeAddr != nil {
if p := PrecompiledContracts[*contract.CodeAddr]; p != nil {
return RunPrecompiledContract(p, input, contract)
}
}

// Don't bother with the execution if there's no code.
if len(contract.Code) == 0 {
return nil, nil
Expand Down
23 changes: 18 additions & 5 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var MainnetChainConfig = &ChainConfig{
EIP150Hash: MainNetHomesteadGasRepriceHash,
EIP155Block: MainNetSpuriousDragon,
EIP158Block: MainNetSpuriousDragon,
EIP198Block: MainNetMetropolis,
}

// TestnetChainConfig is the chain parameters to run a node on the test network.
Expand All @@ -45,6 +46,7 @@ var TestnetChainConfig = &ChainConfig{
EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
EIP155Block: big.NewInt(10),
EIP158Block: big.NewInt(10),
EIP198Block: TestNetMetropolis,
}

// ChainConfig is the core config which determines the blockchain settings.
Expand All @@ -65,23 +67,26 @@ type ChainConfig struct {

EIP155Block *big.Int `json:"eip155Block"` // EIP155 HF block
EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block

EIP198Block *big.Int `json:"eip198Block"` // EIP198 HF block
}

// String implements the Stringer interface.
func (c *ChainConfig) String() string {
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}",
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v EIP198: %v}",
c.ChainId,
c.HomesteadBlock,
c.DAOForkBlock,
c.DAOForkSupport,
c.EIP150Block,
c.EIP155Block,
c.EIP158Block,
c.EIP198Block,
)
}

var (
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)}
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int), nil}
TestRules = TestChainConfig.Rules(new(big.Int))
)

Expand Down Expand Up @@ -135,16 +140,24 @@ func (c *ChainConfig) IsEIP158(num *big.Int) bool {

}

func (c *ChainConfig) IsEIP198(num *big.Int) bool {
if c.EIP198Block == nil || num == nil {
return false
}
return num.Cmp(c.EIP198Block) >= 0

}

// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions
// that do not have or require information about the block.
//
// Rules is a one time interface meaning that it shouldn't be used in between transition
// phases.
type Rules struct {
ChainId *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
ChainId *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158, IsEIP198 bool
}

func (c *ChainConfig) Rules(num *big.Int) Rules {
return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)}
return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsEIP198: c.IsEIP198(num)}
}
3 changes: 3 additions & 0 deletions params/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ var (
TestNetSpuriousDragon = big.NewInt(10)
MainNetSpuriousDragon = big.NewInt(2675000)

TestNetMetropolis = big.NewInt(11)
MainNetMetropolis = big.NewInt(10000000)

TestNetChainID = big.NewInt(3) // Test net default chain ID
MainNetChainID = big.NewInt(1) // main net default chain ID
)