Skip to content

Commit

Permalink
Add framework for stateful precompiles
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronbuchwald committed Feb 19, 2022
1 parent 2a5c1b0 commit c22fb97
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 50 deletions.
88 changes: 44 additions & 44 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,66 +54,66 @@ type PrecompiledContract interface {

// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
// contracts used in the Frontier and Homestead releases.
var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
var PrecompiledContractsHomestead = map[common.Address]StatefulPrecompiledContract{
common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
}

// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
// contracts used in the Byzantium release.
var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
var PrecompiledContractsByzantium = map[common.Address]StatefulPrecompiledContract{
common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{eip2565: false}),
common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddByzantium{}),
common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulByzantium{}),
common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingByzantium{}),
}

// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
// contracts used in the Istanbul release.
var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
var PrecompiledContractsIstanbul = map[common.Address]StatefulPrecompiledContract{
common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{eip2565: false}),
common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}),
common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}),
common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}),
}

// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
common.BytesToAddress([]byte{4}): &dataCopy{},
common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{9}): &blake2F{},
var PrecompiledContractsBerlin = map[common.Address]StatefulPrecompiledContract{
common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{eip2565: true}),
common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}),
common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}),
common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}),
}

// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
// contracts specified in EIP-2537. These are exported for testing purposes.
var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{10}): &bls12381G1Add{},
common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
common.BytesToAddress([]byte{13}): &bls12381G2Add{},
common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
common.BytesToAddress([]byte{16}): &bls12381Pairing{},
common.BytesToAddress([]byte{17}): &bls12381MapG1{},
common.BytesToAddress([]byte{18}): &bls12381MapG2{},
var PrecompiledContractsBLS = map[common.Address]StatefulPrecompiledContract{
common.BytesToAddress([]byte{10}): newWrappedPrecompiledContract(&bls12381G1Add{}),
common.BytesToAddress([]byte{11}): newWrappedPrecompiledContract(&bls12381G1Mul{}),
common.BytesToAddress([]byte{12}): newWrappedPrecompiledContract(&bls12381G1MultiExp{}),
common.BytesToAddress([]byte{13}): newWrappedPrecompiledContract(&bls12381G2Add{}),
common.BytesToAddress([]byte{14}): newWrappedPrecompiledContract(&bls12381G2Mul{}),
common.BytesToAddress([]byte{15}): newWrappedPrecompiledContract(&bls12381G2MultiExp{}),
common.BytesToAddress([]byte{16}): newWrappedPrecompiledContract(&bls12381Pairing{}),
common.BytesToAddress([]byte{17}): newWrappedPrecompiledContract(&bls12381MapG1{}),
common.BytesToAddress([]byte{18}): newWrappedPrecompiledContract(&bls12381MapG2{}),
}

var (
Expand Down
33 changes: 33 additions & 0 deletions core/vm/contracts_stateful.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package vm

import (
"github.com/ethereum/go-ethereum/common"
)

// StatefulPrecompiledContract is the interface for executing a precompiled contract
// This wraps the PrecompiledContracts native to Ethereum and allows adding in stateful
// precompiled contracts to support native Avalanche asset transfers.
type StatefulPrecompiledContract interface {
// Run executes a precompiled contract in the current state
// assumes that it has already been verified that [caller] can
// transfer [value].
Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error)
}

// wrappedPrecompiledContract implements StatefulPrecompiledContract by wrapping stateless native precompiled contracts
// in Ethereum.
type wrappedPrecompiledContract struct {
p PrecompiledContract
}

func newWrappedPrecompiledContract(p PrecompiledContract) StatefulPrecompiledContract {
return &wrappedPrecompiledContract{p: p}
}

// Run implements the StatefulPrecompiledContract interface
func (w *wrappedPrecompiledContract) Run(evm *EVM, caller ContractRef, addr common.Address, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
return RunPrecompiledContract(w.p, input, suppliedGas)
}
12 changes: 6 additions & 6 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ type (
GetHashFunc func(uint64) common.Hash
)

func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
func (evm *EVM) precompile(addr common.Address) (StatefulPrecompiledContract, bool) {
var precompiles map[common.Address]StatefulPrecompiledContract
switch {
case evm.chainRules.IsSubnetEVM:
precompiles = PrecompiledContractsBerlin
Expand Down Expand Up @@ -224,7 +224,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}

if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
ret, gas, err = p.Run(evm, caller, addr, input, gas, evm.interpreter.readOnly)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
Expand Down Expand Up @@ -290,7 +290,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,

// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
ret, gas, err = p.Run(evm, caller, addr, input, gas, evm.interpreter.readOnly)
} else {
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
Expand Down Expand Up @@ -331,7 +331,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by

// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
ret, gas, err = p.Run(evm, caller, addr, input, gas, evm.interpreter.readOnly)
} else {
addrCopy := addr
// Initialise a new contract and make initialise the delegate values
Expand Down Expand Up @@ -380,7 +380,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
}

if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
ret, gas, err = p.Run(evm, caller, addr, input, gas, true)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
// leak the 'contract' to the outer scope, and make allocation for 'contract'
Expand Down

0 comments on commit c22fb97

Please sign in to comment.