Skip to content
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
23 changes: 9 additions & 14 deletions core/vm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ type Contract struct {
IsDeployment bool
IsSystemCall bool

Gas uint64
value *uint256.Int
isPrecompile bool
Gas uint64
value *uint256.Int

// Precompile is exclusive with Code
Precompile PrecompiledContract
}

// NewContract returns a new contract environment for the execution of EVM.
Expand All @@ -63,10 +65,6 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
}

func (c *Contract) validJumpdest(dest *uint256.Int) bool {
if c.isPrecompile {
return false
}

udest, overflow := dest.Uint64WithOverflow()
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
Expand All @@ -83,10 +81,6 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
// isCode returns true if the provided PC location is an actual opcode, as
// opposed to a data-segment following a PUSHN operation.
func (c *Contract) isCode(udest uint64) bool {
if c.isPrecompile {
return false
}

// Do we already have an analysis laying around?
if c.analysis != nil {
return c.analysis.codeSegment(udest)
Expand Down Expand Up @@ -169,9 +163,10 @@ func (c *Contract) Value() *uint256.Int {

// SetCallCode sets the code of the contract,
func (c *Contract) SetCallCode(hash common.Hash, code []byte) {
if c.isPrecompile {
return
}
c.Code = code
c.CodeHash = hash
}

func (c *Contract) SetPrecompile(pc PrecompiledContract) {
c.Precompile = pc
}
83 changes: 23 additions & 60 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@ import (
"github.com/consensys/gnark-crypto/ecc/bls12-381/fp"
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/tracing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/blake2b"
"github.com/ethereum/go-ethereum/crypto/bn256"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/crypto/secp256r1"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"golang.org/x/crypto/ripemd160"
)

Expand All @@ -46,8 +44,8 @@ import (
// contract.
type PrecompiledContract interface {
Address() common.Address
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) // Run runs the precompiled contract
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(evm *EVM, contract *Contract) ([]byte, error) // Run runs the precompiled contract
}

// PrecompiledContracts contains the precompiled contracts supported at the given fork.
Expand Down Expand Up @@ -254,41 +252,6 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
}
}

// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
// It returns
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func (evm *EVM) RunPrecompiledContract(
p PrecompiledContract,
caller common.Address,
input []byte,
suppliedGas uint64,
value *uint256.Int,
readOnly bool,
logger *tracing.Hooks,
) (ret []byte, remainingGas uint64, err error) {
return runPrecompiledContract(evm, p, caller, input, suppliedGas, value, readOnly, logger)
}

func runPrecompiledContract(evm *EVM, p PrecompiledContract, caller common.Address, input []byte, suppliedGas uint64,
value *uint256.Int, readOnly bool, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
addrCopy := p.Address()
inputCopy := make([]byte, len(input))
copy(inputCopy, input)

contract := NewPrecompile(caller, addrCopy, value, suppliedGas)
contract.Input = inputCopy

gasCost := p.RequiredGas(input)
if !contract.UseGas(gasCost, logger, tracing.GasChangeCallPrecompiledContract) {
return nil, 0, ErrOutOfGas
}

output, err := p.Run(evm, contract, readOnly)
return output, contract.Gas, err
}

// ecrecover implemented as a native contract.
type ecrecover struct{}

Expand All @@ -302,7 +265,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}

func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *ecrecover) Run(evm *EVM, contract *Contract) ([]byte, error) {
const ecRecoverInputLength = 128

contract.Input = common.RightPadBytes(contract.Input, ecRecoverInputLength)
Expand Down Expand Up @@ -350,7 +313,7 @@ func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}

func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *sha256hash) Run(evm *EVM, contract *Contract) ([]byte, error) {
h := sha256.Sum256(contract.Input)
return h[:], nil
}
Expand All @@ -372,7 +335,7 @@ func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}

func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *ripemd160hash) Run(evm *EVM, contract *Contract) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(contract.Input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
Expand All @@ -395,7 +358,7 @@ func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}

func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *dataCopy) Run(evm *EVM, contract *Contract) ([]byte, error) {
return common.CopyBytes(contract.Input), nil
}

Expand Down Expand Up @@ -550,7 +513,7 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
return gas.Uint64()
}

func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bigModExp) Run(evm *EVM, contract *Contract) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(getData(contract.Input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(contract.Input, 32, 32)).Uint64()
Expand Down Expand Up @@ -640,7 +603,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}

func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256Add(contract.Input)
}

Expand All @@ -659,7 +622,7 @@ func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasByzantium
}

func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256Add(contract.Input)
}

Expand Down Expand Up @@ -690,7 +653,7 @@ func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasIstanbul
}

func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256ScalarMul(contract.Input)
}

Expand All @@ -709,7 +672,7 @@ func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256ScalarMulGasByzantium
}

func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256ScalarMul(contract.Input)
}

Expand Down Expand Up @@ -770,7 +733,7 @@ func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
}

func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256Pairing(contract.Input)
}

Expand All @@ -789,7 +752,7 @@ func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
}

func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract) ([]byte, error) {
return runBn256Pairing(contract.Input)
}

Expand Down Expand Up @@ -821,7 +784,7 @@ var (
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
)

func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *blake2F) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Make sure the input is valid (correct length and final flag)
if len(contract.Input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
Expand Down Expand Up @@ -881,7 +844,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G1AddGas
}

func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381G1Add) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 G1Add precompile.
// > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
// > Output is an encoding of addition operation result - single G1 point (`128` bytes).
Expand Down Expand Up @@ -937,7 +900,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
}

func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 G1MultiExp precompile.
// G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
Expand Down Expand Up @@ -989,7 +952,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G2AddGas
}

func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381G2Add) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 G2Add precompile.
// > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
// > Output is an encoding of addition operation result - single G2 point (`256` bytes).
Expand Down Expand Up @@ -1046,7 +1009,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
}

func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 G2MultiExp precompile logic
// > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
Expand Down Expand Up @@ -1098,7 +1061,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
}

func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381Pairing) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 Pairing precompile logic.
// > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
// > - `128` bytes of G1 point encoding
Expand Down Expand Up @@ -1256,7 +1219,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas
}

func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381MapG1) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 Map_To_G1 precompile.
// > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field.
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
Expand Down Expand Up @@ -1291,7 +1254,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG2Gas
}

func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *bls12381MapG2) Run(evm *EVM, contract *Contract) ([]byte, error) {
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
// > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field.
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
Expand Down Expand Up @@ -1343,7 +1306,7 @@ var (
)

// Run executes the point evaluation precompile.
func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract) ([]byte, error) {
if len(contract.Input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}
Expand Down Expand Up @@ -1396,7 +1359,7 @@ func (c *p256Verify) RequiredGas(input []byte) uint64 {
}

// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas
func (c *p256Verify) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
func (c *p256Verify) Run(evm *EVM, contract *Contract) ([]byte, error) {
const p256VerifyInputLength = 160
if len(contract.Input) != p256VerifyInputLength {
return nil, nil
Expand Down
3 changes: 2 additions & 1 deletion core/vm/contracts_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
}
inWant := string(input)

runPrecompiledContract(nil, p, common.Address{}, input, gas, new(uint256.Int), false, nil)
contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil)
runPrecompiledContract(nil, p, contract, input, false, nil)

Check failure on line 42 in core/vm/contracts_fuzz_test.go

View workflow job for this annotation

GitHub Actions / test-all

undefined: runPrecompiledContract
if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a)
}
Expand Down
12 changes: 8 additions & 4 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil); err != nil {
contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil)
if res, _, err := runPrecompiledContract(nil, p, contract, in, false, nil); err != nil {

Check failure on line 104 in core/vm/contracts_test.go

View workflow job for this annotation

GitHub Actions / test-all

undefined: runPrecompiledContract
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
Expand All @@ -122,7 +123,8 @@
gas := p.RequiredGas(in) - 1

t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil)
contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil)
_, _, err := runPrecompiledContract(nil, p, contract, in, false, nil)

Check failure on line 127 in core/vm/contracts_test.go

View workflow job for this annotation

GitHub Actions / test-all

undefined: runPrecompiledContract
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
Expand All @@ -139,7 +141,8 @@
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) {
_, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil)
contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), gas, nil)
_, _, err := runPrecompiledContract(nil, p, contract, in, false, nil)

Check failure on line 145 in core/vm/contracts_test.go

View workflow job for this annotation

GitHub Actions / test-all

undefined: runPrecompiledContract
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
Expand Down Expand Up @@ -171,7 +174,8 @@
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
copy(data, in)
res, _, err = runPrecompiledContract(nil, p, common.Address{}, in, reqGas, new(uint256.Int), false, nil)
contract := NewContract(common.Address{}, p.Address(), new(uint256.Int), reqGas, nil)
res, _, err = runPrecompiledContract(nil, p, contract, in, false, nil)

Check failure on line 178 in core/vm/contracts_test.go

View workflow job for this annotation

GitHub Actions / test-all

undefined: runPrecompiledContract
}
bench.StopTimer()
elapsed := uint64(time.Since(start))
Expand Down
3 changes: 1 addition & 2 deletions core/vm/custom_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@ func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
// NewPrecompile returns a new instance of a precompiled contract environment for the execution of EVM.
func NewPrecompile(caller, address common.Address, value *uint256.Int, gas uint64) *Contract {
c := NewContract(caller, address, value, gas, nil)
c.isPrecompile = true
return c
}

// IsPrecompile returns true if the contract is a precompiled contract environment
func (c *Contract) IsPrecompile() bool {
return c.isPrecompile
return c.Precompile != nil
}
Loading
Loading