Skip to content

Commit

Permalink
Introducing IP Graph Support (#8)
Browse files Browse the repository at this point in the history
Co-authored-by: Kingter <[email protected]>
  • Loading branch information
AndyBoWu and kingster-will authored Aug 16, 2024
1 parent 820a852 commit fa573fc
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 46 deletions.
1 change: 1 addition & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
common.BytesToAddress([]byte{26}): {Balance: big.NewInt(1)}, // ipGraph
// Pre-deploy EIP-4788 system contract
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
},
Expand Down
78 changes: 41 additions & 37 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"math/big"

"github.com/consensys/gnark-crypto/ecc"
Expand All @@ -30,10 +31,10 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"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/log"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/crypto/ripemd160"
)
Expand All @@ -42,8 +43,8 @@ import (
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(evm *EVM, input []byte) ([]byte, error) // Run runs the precompiled contract
}

// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
Expand Down Expand Up @@ -99,16 +100,17 @@ var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
// PrecompiledContractsCancun contains the default set of pre-compiled Ethereum
// contracts used in the Cancun release.
var PrecompiledContractsCancun = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa}): &kzgPointEvaluation{},
common.BytesToAddress([]byte{0x1a}): &ipGraph{},
}

// PrecompiledContractsPrague contains the set of pre-compiled Ethereum
Expand Down Expand Up @@ -192,7 +194,8 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
func RunPrecompiledContract(evm *EVM, p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
log.Info("RunPrecompiledContract", "input", input, "suppliedGas", suppliedGas)
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
Expand All @@ -201,7 +204,8 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
}
suppliedGas -= gasCost
output, err := p.Run(input)
output, err := p.Run(evm, input)
log.Info("RunPrecompiledContract", "output", output, "err", err)
return output, suppliedGas, err
}

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

func (c *ecrecover) Run(input []byte) ([]byte, error) {
func (c *ecrecover) Run(evm *EVM, input []byte) ([]byte, error) {
const ecRecoverInputLength = 128

input = common.RightPadBytes(input, ecRecoverInputLength)
Expand Down Expand Up @@ -253,7 +257,7 @@ type sha256hash struct{}
func (c *sha256hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
}
func (c *sha256hash) Run(input []byte) ([]byte, error) {
func (c *sha256hash) Run(evm *EVM, input []byte) ([]byte, error) {
h := sha256.Sum256(input)
return h[:], nil
}
Expand All @@ -268,7 +272,7 @@ type ripemd160hash struct{}
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
}
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
func (c *ripemd160hash) Run(evm *EVM, input []byte) ([]byte, error) {
ripemd := ripemd160.New()
ripemd.Write(input)
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
Expand All @@ -284,7 +288,7 @@ type dataCopy struct{}
func (c *dataCopy) RequiredGas(input []byte) uint64 {
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
}
func (c *dataCopy) Run(in []byte) ([]byte, error) {
func (c *dataCopy) Run(evm *EVM, in []byte) ([]byte, error) {
return common.CopyBytes(in), nil
}

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

func (c *bigModExp) Run(input []byte) ([]byte, error) {
func (c *bigModExp) Run(evm *EVM, input []byte) ([]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 @@ -486,7 +490,7 @@ func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
return params.Bn256AddGasIstanbul
}

func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
func (c *bn256AddIstanbul) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256Add(input)
}

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

func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
func (c *bn256AddByzantium) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256Add(input)
}

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

func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
func (c *bn256ScalarMulIstanbul) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}

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

func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
func (c *bn256ScalarMulByzantium) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256ScalarMul(input)
}

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

func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
func (c *bn256PairingIstanbul) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256Pairing(input)
}

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

func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
func (c *bn256PairingByzantium) Run(evm *EVM, input []byte) ([]byte, error) {
return runBn256Pairing(input)
}

Expand All @@ -631,7 +635,7 @@ var (
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
)

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

func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
func (c *bls12381G1Add) Run(evm *EVM, input []byte) ([]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 @@ -721,7 +725,7 @@ func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G1MulGas
}

func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
func (c *bls12381G1Mul) Run(evm *EVM, input []byte) ([]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 @@ -773,7 +777,7 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
}

func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
func (c *bls12381G1MultiExp) Run(evm *EVM, input []byte) ([]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 @@ -819,7 +823,7 @@ func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
return params.Bls12381G2AddGas
}

func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
func (c *bls12381G2Add) Run(evm *EVM, input []byte) ([]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 @@ -856,7 +860,7 @@ func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
return params.Bls12381G2MulGas
}

func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
func (c *bls12381G2Mul) Run(evm *EVM, input []byte) ([]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 @@ -908,7 +912,7 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
}

func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
func (c *bls12381G2MultiExp) Run(evm *EVM, input []byte) ([]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 @@ -954,7 +958,7 @@ func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
}

func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
func (c *bls12381Pairing) Run(evm *EVM, input []byte) ([]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 @@ -1106,7 +1110,7 @@ func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG1Gas
}

func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
func (c *bls12381MapG1) Run(evm *EVM, input []byte) ([]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 @@ -1135,7 +1139,7 @@ func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
return params.Bls12381MapG2Gas
}

func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
func (c *bls12381MapG2) Run(evm *EVM, input []byte) ([]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 @@ -1181,7 +1185,7 @@ var (
)

// Run executes the point evaluation precompile.
func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
func (b *kzgPointEvaluation) Run(evm *EVM, input []byte) ([]byte, error) {
if len(input) != blobVerifyInputLength {
return nil, errBlobVerifyInvalidInputLength
}
Expand Down
12 changes: 11 additions & 1 deletion core/vm/contracts_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
package vm

import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -35,8 +40,13 @@ func FuzzPrecompiledContracts(f *testing.F) {
if gas > 10_000_000 {
return
}
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{})
inWant := string(input)
RunPrecompiledContract(p, input, gas, nil)
RunPrecompiledContract(evm, p, input, gas, nil)
if inHave := string(input); inWant != inHave {
t.Errorf("Precompiled %v modified input data", a)
}
Expand Down
36 changes: 32 additions & 4 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
"os"
"testing"
"time"
Expand Down Expand Up @@ -97,8 +102,13 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{})
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil {
if res, _, err := RunPrecompiledContract(evm, p, in, gas, nil); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
Expand All @@ -119,8 +129,14 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in) - 1

vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{})

t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(evm, p, in, gas, nil)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
Expand All @@ -136,8 +152,15 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
gas := p.RequiredGas(in)

vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{})

t.Run(test.Name, func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas, nil)
_, _, err := RunPrecompiledContract(evm, p, in, gas, nil)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
Expand Down Expand Up @@ -168,8 +191,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
start := time.Now()
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
vmctx := BlockContext{
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
evm := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{})
copy(data, in)
res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
res, _, err = RunPrecompiledContract(evm, p, data, reqGas, nil)
}
bench.StopTimer()
elapsed := uint64(time.Since(start))
Expand Down
Loading

0 comments on commit fa573fc

Please sign in to comment.