Skip to content

Commit

Permalink
Merge pull request #16 from berachain/add-evm-precompile
Browse files Browse the repository at this point in the history
feat(precompile): Add EVM to Precompiles
  • Loading branch information
calbera committed Mar 20, 2023
2 parents 4b0f68e + 6a08dd2 commit db20350
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 44 deletions.
44 changes: 22 additions & 22 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type PrecompiledContract interface {
// RequiredGas calculates the contract static gas use.
RequiredGas(input []byte) uint64
// Run runs the precompiled contract with the given context.
Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error)
Run(ctx context.Context, evm PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error)
}

// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
Expand Down Expand Up @@ -156,7 +156,7 @@ func (c *ecrecover) RequiredGas(input []byte) uint64 {
return params.EcrecoverGas
}

func (c *ecrecover) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *ecrecover) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
const ecRecoverInputLength = 128

input = common.RightPadBytes(input, ecRecoverInputLength)
Expand Down Expand Up @@ -201,7 +201,7 @@ func (c *sha256hash) RegistryKey() common.Address {
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *sha256hash) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
Expand All @@ -220,7 +220,7 @@ func (c *ripemd160hash) RegistryKey() common.Address {
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *ripemd160hash) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
Expand All @@ -240,7 +240,7 @@ func (c *dataCopy) RegistryKey() common.Address {
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *dataCopy) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return input, nil
}

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

func (c *bigModExp) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bigModExp) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
var (
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
Expand Down Expand Up @@ -455,7 +455,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}

func (c *bn256AddIstanbul) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256AddIstanbul) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256Add(input)
}

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

func (c *bn256AddByzantium) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256AddByzantium) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256Add(input)
}

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

func (c *bn256ScalarMulIstanbul) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256ScalarMulIstanbul) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256ScalarMul(input)
}

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

func (c *bn256ScalarMulByzantium) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256ScalarMulByzantium) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256ScalarMul(input)
}

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

func (c *bn256PairingIstanbul) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256PairingIstanbul) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256Pairing(input)
}

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

func (c *bn256PairingByzantium) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bn256PairingByzantium) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
return runBn256Pairing(input)
}

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

func (c *blake2F) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *blake2F) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
// Make sure the input is valid (correct length and final flag)
if len(input) != blake2FInputLength {
return nil, errBlake2FInvalidInputLength
Expand Down Expand Up @@ -682,7 +682,7 @@ func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G1AddGas
}

func (c *bls12381G1Add) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G1Add) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]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 @@ -724,7 +724,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G1MulGas
}

func (c *bls12381G1Mul) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G1Mul) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
// Implements EIP-2537 G1Mul precompile.
// > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
Expand Down Expand Up @@ -778,7 +778,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
}

func (c *bls12381G1MultiExp) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G1MultiExp) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]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 @@ -825,7 +825,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G2AddGas
}

func (c *bls12381G2Add) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G2Add) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]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 @@ -867,7 +867,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G2MulGas
}

func (c *bls12381G2Mul) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G2Mul) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
// Implements EIP-2537 G2MUL precompile logic.
// > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
// > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
Expand Down Expand Up @@ -921,7 +921,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
}

func (c *bls12381G2MultiExp) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381G2MultiExp) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]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 @@ -968,7 +968,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
}

func (c *bls12381Pairing) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381Pairing) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]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 @@ -1051,7 +1051,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas
}

func (c *bls12381MapG1) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381MapG1) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
// Implements EIP-2537 Map_To_G1 precompile.
// > Field-to-curve call expects `64` bytes an an input that is interpreted as a an element of the base field.
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
Expand Down Expand Up @@ -1090,7 +1090,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG2Gas
}

func (c *bls12381MapG2) Run(ctx context.Context, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
func (c *bls12381MapG2) Run(ctx context.Context, _ PrecompileEVM, input []byte, caller common.Address, value *big.Int, readonly bool) ([]byte, error) {
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
// > Field-to-curve call expects `128` bytes an an input that is interpreted as a an element of the quadratic extension field.
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
Expand Down
2 changes: 1 addition & 1 deletion core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin
return nil, 0, ErrOutOfGas
}
suppliedGas -= gasCost
output, err := p.Run(context.Background(), input, common.Address{}, new(big.Int), true)
output, err := p.Run(context.Background(), nil, input, common.Address{}, new(big.Int), true)
return output, suppliedGas, err
}

Expand Down
27 changes: 8 additions & 19 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,6 @@ type (
GetHashFunc func(uint64) common.Hash
)

// `PrecompileManager` allows the EVM to execute a precompiled contract.
type PrecompileManager interface {
// `Has` returns if a precompiled contract was found at `addr`.
Has(addr common.Address) bool

// `Get` returns the precompiled contract at `addr`. Returns nil if no
// contract is found at `addr`.
Get(addr common.Address) PrecompiledContract

// `Run` runs a precompiled contract and returns the remaining gas.
Run(sdb StateDB, p PrecompiledContract, input []byte, caller common.Address,
value *big.Int, suppliedGas uint64, readonly bool,
) (ret []byte, remainingGas uint64, err error)
}

// BlockContext provides the EVM with auxiliary information. Once provided
// it shouldn't be modified.
type BlockContext struct {
Expand Down Expand Up @@ -171,6 +156,10 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
return evm.interpreter
}

func (evm *EVM) GetStateDB() StateDB {
return evm.StateDB
}

// 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
Expand Down Expand Up @@ -223,7 +212,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas

if isPrecompile {
ret, gas, err = evm.PrecompileManager.Run(
evm.StateDB, evm.PrecompileManager.Get(addr), input, caller.Address(), value, gas, false,
evm, evm.PrecompileManager.Get(addr), input, caller.Address(), value, gas, false,
)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
Expand Down Expand Up @@ -288,7 +277,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// It is allowed to call precompiles, even via delegatecall
if isPrecompile := evm.PrecompileManager.Has(addr); isPrecompile {
ret, gas, err = evm.PrecompileManager.Run(
evm.StateDB, evm.PrecompileManager.Get(addr), input, caller.Address(), value, gas, true,
evm, evm.PrecompileManager.Get(addr), input, caller.Address(), value, gas, true,
)
} else {
addrCopy := addr
Expand Down Expand Up @@ -336,7 +325,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if isPrecompile := evm.PrecompileManager.Has(addr); isPrecompile {
parent := caller.(*Contract)
ret, gas, err = evm.PrecompileManager.Run(
evm.StateDB, evm.PrecompileManager.Get(addr), input, parent.CallerAddress, parent.value, gas, false,
evm, evm.PrecompileManager.Get(addr), input, parent.CallerAddress, parent.value, gas, false,
)
} else {
addrCopy := addr
Expand Down Expand Up @@ -387,7 +376,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte

if isPrecompile := evm.PrecompileManager.Has(addr); isPrecompile {
ret, gas, err = evm.PrecompileManager.Run(
evm.StateDB, evm.PrecompileManager.Get(addr), input, caller.Address(), new(big.Int), gas, true,
evm, evm.PrecompileManager.Get(addr), input, caller.Address(), new(big.Int), gas, true,
)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
Expand Down
28 changes: 28 additions & 0 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/holiman/uint256"
)

// StateDB is an EVM database for full state querying.
Expand All @@ -38,3 +39,30 @@ type CallContext interface {
// Create creates a new contract
Create(env *EVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}

type (
// PrecompileManager allows the EVM to execute a precompiled contract.
PrecompileManager interface {
// `Has` returns if a precompiled contract was found at `addr`.
Has(addr common.Address) bool

// `Get` returns the precompiled contract at `addr`. Returns nil if no
// contract is found at `addr`.
Get(addr common.Address) PrecompiledContract

// `Run` runs a precompiled contract and returns the remaining gas.
Run(evm PrecompileEVM, p PrecompiledContract, input []byte, caller common.Address,
value *big.Int, suppliedGas uint64, readonly bool,
) (ret []byte, remainingGas uint64, err error)
}

// PrecompileEVM is the interface through which stateful precompiles can call back into the EVM.
PrecompileEVM interface {
GetStateDB() StateDB

Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error)
StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error)
Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error)
Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error)
}
)
4 changes: 2 additions & 2 deletions core/vm/precompile_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (pm *precompileManager) Get(addr common.Address) PrecompiledContract {

// Run runs the given precompiled contract with the given input data and returns the remaining gas.
func (pm *precompileManager) Run(
_ StateDB, p PrecompiledContract, input []byte,
evm PrecompileEVM, p PrecompiledContract, input []byte,
caller common.Address, value *big.Int, suppliedGas uint64, readonly bool,
) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
Expand All @@ -65,7 +65,7 @@ func (pm *precompileManager) Run(
}

suppliedGas -= gasCost
output, err := p.Run(context.Background(), input, caller, value, readonly)
output, err := p.Run(context.Background(), evm, input, caller, value, readonly)

return output, suppliedGas, err
}
Expand Down

0 comments on commit db20350

Please sign in to comment.