Skip to content

Commit

Permalink
[RIP-7728] merge rollup-geth into op-geth
Browse files Browse the repository at this point in the history
commit ee58cfe
Author: mralj <[email protected]>
Date:   Mon Oct 7 13:00:42 2024 +0200

    missed cleanup of ActivePrecompiles

commit d409ef8
Author: mralj <[email protected]>
Date:   Mon Oct 7 12:02:42 2024 +0200

    bugfixes - l1rpc activated at proper point and precompile address

commit bdd7b7d
Author: mralj <[email protected]>
Date:   Mon Oct 7 10:57:48 2024 +0200

    ethclient moved to node.Node

commit bd56bdc
Author: mralj <[email protected]>
Date:   Fri Oct 4 17:36:36 2024 +0200

    code cleanup after trying to merge into arb/op-geth

commit 76a2339
Author: mralj <[email protected]>
Date:   Tue Oct 1 10:43:04 2024 +0200

    internal/ethapi and tracers use pre-existing function call

commit b72098e
Author: mralj <[email protected]>
Date:   Mon Sep 30 10:20:37 2024 +0200

    added missing "," - fixed comptime bug

commit 1ccbc95
Author: mralj <[email protected]>
Date:   Mon Sep 30 10:13:45 2024 +0200

    simplified the code

commit 0f74390
Author: mralj <[email protected]>
Date:   Sun Sep 29 13:12:00 2024 +0200

    cleaned up code & created more rollup specific files

commit 2a7b7d7
Author: mralj <[email protected]>
Date:   Sun Sep 29 11:45:34 2024 +0200

    cmd - rollup specific files

commit ef91bcd
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:10:01 2024 +0200

    implements L1SLOAD contract

commit 6a98534
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 5f039c5
Merge: 204ef24 56c1f67
Author: mralj <[email protected]>
Date:   Mon Oct 7 13:09:06 2024 +0200

    Merge pull request #4 from NethermindEth/core/rip/7728-precompile-impl

    [P2] Implements RIP-7728

commit 56c1f67
Author: mralj <[email protected]>
Date:   Sat Sep 28 13:00:06 2024 +0200

    added missing mocks for tests

commit e9a5c28
Author: mralj <[email protected]>
Date:   Fri Sep 27 22:44:44 2024 +0200

    added test for too long input edgecase

commit bea23a3
Author: mralj <[email protected]>
Date:   Fri Sep 27 22:40:37 2024 +0200

    added batch call for StoragesAt as well as tests

commit c4b24af
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:32:52 2024 +0200

    added rpc call timeout strategy

commit f0dd217
Author: mralj <[email protected]>
Date:   Fri Sep 27 13:10:01 2024 +0200

    implements L1SLOAD contract

commit 759dda7
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 204ef24
Author: mralj <[email protected]>
Date:   Thu Sep 26 20:21:47 2024 +0200

    added example how code in overrides would change

commit a24608e
Author: mralj <[email protected]>
Date:   Thu Sep 26 19:48:47 2024 +0200

    added ability to activate rollup precompiles from eth/internal and eth/tracers

commit 99ccaf7
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:26:08 2024 +0200

    bugfix

commit 1974d92
Author: mralj <[email protected]>
Date:   Thu Sep 26 13:22:58 2024 +0200

    added L1SLoad sekelton

commit 0ae7e7b
Author: mralj <[email protected]>
Date:   Wed Sep 25 19:08:40 2024 +0200

    dial L1 RPC client passed via required flag

# Conflicts:
#	core/vm/interpreter.go
#	eth/backend.go
#	eth/tracers/api.go
#	params/config.go
  • Loading branch information
mralj committed Oct 7, 2024
1 parent f7a3a10 commit e2e629c
Show file tree
Hide file tree
Showing 34 changed files with 490 additions and 26 deletions.
4 changes: 4 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
}
applyMetricConfig(ctx, &cfg)

//[rollup-geth]
activateL1RPCEndpoint(ctx, stack)

return stack, cfg
}

Expand Down Expand Up @@ -281,6 +284,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
utils.Fatalf("failed to register catalyst service: %v", err)
}
}

return stack
}

Expand Down
20 changes: 20 additions & 0 deletions cmd/geth/config_rollup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/urfave/cli/v2"
)

// TODO: when we have clearer picture of how do we want rollup "features" (EIPs/RIPs) to be activated
// make this "rule" activated (ie. if not "rule activated" then L1 client can simply be nil)
func activateL1RPCEndpoint(ctx *cli.Context, stack *node.Node) {
if !ctx.IsSet(utils.L1NodeRPCEndpointFlag.Name) {
log.Error("L1 node RPC endpoint URL not set", "flag", utils.L1NodeRPCEndpointFlag.Name)
return
}

l1RPCEndpoint := ctx.String(utils.L1NodeRPCEndpointFlag.Name)
stack.RegisterEthClient(l1RPCEndpoint)
}
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ func init() {
consoleFlags,
debug.Flags,
metricsFlags,
utils.RollupFlags, //[rollup-geth]
)
flags.AutoEnvVars(app.Flags, "GETH")

Expand Down
21 changes: 21 additions & 0 deletions cmd/utils/flags_rollup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package utils

import (
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/urfave/cli/v2"
)

var (
L1NodeRPCEndpointFlag = &cli.StringFlag{
Name: "rollup.l1.rpc_endpoint",
Usage: "L1 node RPC endpoint eg. http://0.0.0.0:8545",
Category: flags.RollupCategory,
Required: true,
}
)

var (
RollupFlags = []cli.Flag{
L1NodeRPCEndpointFlag,
}
)
2 changes: 1 addition & 1 deletion core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ func (st *StateTransition) innerTransitionDb() (*ExecutionResult, error) {
// Execute the preparatory steps for state transition which includes:
// - prepare accessList(post-berlin)
// - reset transient storage(eip 1153)
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompilesIncludingRollups(rules), msg.AccessList)

var (
ret []byte
Expand Down
6 changes: 5 additions & 1 deletion core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"maps"
"math/big"
"slices"

"github.com/consensys/gnark-crypto/ecc"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
Expand Down Expand Up @@ -330,6 +331,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) {
h := sha256.Sum256(input)
return h[:], nil
Expand All @@ -345,6 +347,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) {
ripemd := ripemd160.New()
ripemd.Write(input)
Expand All @@ -361,6 +364,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) {
return common.CopyBytes(in), nil
}
Expand Down Expand Up @@ -510,7 +514,7 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
// Modulo 0 is undefined, return zero
return common.LeftPadBytes([]byte{}, int(modLen)), nil
case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
//If base == 1, then we can just return base % mod (if mod >= 1, which it is)
// If base == 1, then we can just return base % mod (if mod >= 1, which it is)
v = base.Mod(base, mod).Bytes()
default:
v = base.Exp(base, exp, mod).Bytes()
Expand Down
145 changes: 145 additions & 0 deletions core/vm/contracts_rollup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//[rollup-geth]
// These are rollup-geth specific precompiled contracts

package vm

import (
"context"
"errors"
"math/big"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)

type RollupPrecompileActivationConfig struct {
L1SLoad
}

type L1RpcClient interface {
StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error)
}

var (
rollupL1SloadAddress = common.BytesToAddress([]byte{0x01, 0x01})
precompiledContractsRollupR0 = PrecompiledContracts{
rollupL1SloadAddress: &L1SLoad{},
}
)

func activeRollupPrecompiledContracts(rules params.Rules) PrecompiledContracts {
switch rules.IsR0 {
case rules.IsR0:
return precompiledContractsRollupR0
default:
return nil
}
}

// ActivateRollupPrecompiledContracts activates rollup-specific precompiles
func (pc PrecompiledContracts) ActivateRollupPrecompiledContracts(rules params.Rules, config *RollupPrecompileActivationConfig) {
if config == nil {
return
}

activeRollupPrecompiles := activeRollupPrecompiledContracts(rules)
for k, v := range activeRollupPrecompiles {
pc[k] = v
}

// NOTE: if L1SLoad was not activated via chain rules this is no-op
pc.activateL1SLoad(config.L1RpcClient, config.GetLatestL1BlockNumber)
}

func ActivePrecompilesIncludingRollups(rules params.Rules) []common.Address {
activePrecompiles := ActivePrecompiles(rules)
activeRollupPrecompiles := activeRollupPrecompiledContracts(rules)

for k := range activeRollupPrecompiles {
activePrecompiles = append(activePrecompiles, k)
}

return activePrecompiles
}

//INPUT SPECS:
//Byte range Name Description
//------------------------------------------------------------
//[0: 19] (20 bytes) address The contract address
//[20: 51] (32 bytes) key1 The storage key
//... ... ...
//[k*32-12: k*32+19] (32 bytes)key_k The storage key

type L1SLoad struct {
L1RpcClient L1RpcClient
GetLatestL1BlockNumber func() *big.Int
}

func (c *L1SLoad) RequiredGas(input []byte) uint64 {
storageSlotsToLoad := len(input[common.AddressLength-1:]) / common.HashLength
storageSlotsToLoad = min(storageSlotsToLoad, params.L1SLoadMaxNumStorageSlots)

return params.L1SLoadBaseGas + uint64(storageSlotsToLoad)*params.L1SLoadPerLoadGas
}

func (c *L1SLoad) Run(input []byte) ([]byte, error) {
if !c.isL1SLoadActive() {
log.Error("L1SLOAD called, but not activated", "client", c.L1RpcClient, "and latest block number function", c.GetLatestL1BlockNumber)
return nil, errors.New("L1SLOAD precompile not active")
}

if len(input) < common.AddressLength+common.HashLength {
return nil, errors.New("L1SLOAD input too short")
}

countOfStorageKeysToRead := (len(input) - common.AddressLength) / common.HashLength
thereIsAtLeast1StorageKeyToRead := countOfStorageKeysToRead > 0
allStorageKeysAreExactly32Bytes := countOfStorageKeysToRead*common.HashLength == len(input)-common.AddressLength

if inputIsInvalid := !(thereIsAtLeast1StorageKeyToRead && allStorageKeysAreExactly32Bytes); inputIsInvalid {
return nil, errors.New("L1SLOAD input is malformed")
}

contractAddress := common.BytesToAddress(input[:common.AddressLength])
input = input[common.AddressLength-1:]
contractStorageKeys := make([]common.Hash, countOfStorageKeysToRead)
for k := 0; k < countOfStorageKeysToRead; k++ {
contractStorageKeys[k] = common.BytesToHash(input[k*common.HashLength : (k+1)*common.HashLength])
}

var ctx context.Context
if params.L1SLoadRPCTimeoutInSec > 0 {
c, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(params.L1SLoadRPCTimeoutInSec))
ctx = c
defer cancel()
} else {
ctx = context.Background()
}

res, err := c.L1RpcClient.StoragesAt(ctx, contractAddress, contractStorageKeys, c.GetLatestL1BlockNumber())
if err != nil {
return nil, err
}

return res, nil
}

func (c *L1SLoad) isL1SLoadActive() bool {
return c.GetLatestL1BlockNumber != nil && c.L1RpcClient != nil
}

func (pc PrecompiledContracts) activateL1SLoad(l1RpcClient L1RpcClient, getLatestL1BlockNumber func() *big.Int) {
if paramsAreNil := l1RpcClient == nil || getLatestL1BlockNumber == nil; paramsAreNil {
return
}
if precompileNotRuleActivated := pc[rollupL1SloadAddress] == nil; precompileNotRuleActivated {
return
}

pc[rollupL1SloadAddress] = &L1SLoad{
L1RpcClient: l1RpcClient,
GetLatestL1BlockNumber: getLatestL1BlockNumber,
}
}
38 changes: 38 additions & 0 deletions core/vm/contracts_rollup_overrides.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//[rollup-geth]
// These are rollup-geth specific precompiled contracts

package vm

import (
"math/big"
)

// generateRollupPrecompiledContractsOverrides generates rollup precompile config including L2 specific overrides
func generateRollupPrecompiledContractsOverrides(evm *EVM) *RollupPrecompileActivationConfig {
return &RollupPrecompileActivationConfig{
L1SLoad{
L1RpcClient: evm.Config.L1RpcClient,
GetLatestL1BlockNumber: LetRPCDecideLatestL1Number,
},
}
}

// [OVERRIDE] LetRPCDecideLatestL1Number
// Each rollup should override this function so that it returns
// correct latest L1 block number
func LetRPCDecideLatestL1Number() *big.Int {
return nil
}

// [OVERRIDE] getLatestL1BlockNumber
// Each rollup should override this function so that it returns
// correct latest L1 block number
//
// EXAMPLE 2
// func GetLatestL1BlockNumber(state *state.StateDB) func() *big.Int {
// return func() *big.Int {
// addressOfL1BlockContract := common.Address{}
// slotInContractRepresentingL1BlockNumber := common.Hash{}
// return state.GetState(addressOfL1BlockContract, slotInContractRepresentingL1BlockNumber).Big()
// }
// }
32 changes: 32 additions & 0 deletions core/vm/contracts_rollup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package vm

import (
"context"
"math/big"
"testing"

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

type MockL1RPCClient struct{}

func (m MockL1RPCClient) StoragesAt(ctx context.Context, account common.Address, keys []common.Hash, blockNumber *big.Int) ([]byte, error) {
// testcase is in format "abab", this makes output lenght 2 bytes
const mockedRespValueSize = 2
mockResp := make([]byte, mockedRespValueSize*len(keys))
for i := range keys {
copy(mockResp[mockedRespValueSize*i:], common.Hex2Bytes("abab"))
}

return mockResp, nil
}

func TestPrecompiledL1SLOAD(t *testing.T) {
mockL1RPCClient := MockL1RPCClient{}

allPrecompiles[rollupL1SloadAddress] = &L1SLoad{}
allPrecompiles.activateL1SLoad(mockL1RPCClient, func() *big.Int { return big1 })

testJson("l1sload", rollupL1SloadAddress.Hex(), t)
testJsonFail("l1sload", rollupL1SloadAddress.Hex(), t)
}
4 changes: 2 additions & 2 deletions core/vm/contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type precompiledFailureTest struct {

// allPrecompiles does not map to the actual set of precompiles, as it also contains
// repriced versions of precompiles at certain slots
var allPrecompiles = map[common.Address]PrecompiledContract{
var allPrecompiles = PrecompiledContracts{
common.BytesToAddress([]byte{1}): &ecrecover{},
common.BytesToAddress([]byte{2}): &sha256hash{},
common.BytesToAddress([]byte{3}): &ripemd160hash{},
Expand Down Expand Up @@ -184,7 +184,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
// Keep it as uint64, multiply 100 to get two digit float later
mgasps := (100 * 1000 * gasUsed) / elapsed
bench.ReportMetric(float64(mgasps)/100, "mgas/s")
//Check if it is correct
// Check if it is correct
if err != nil {
bench.Error(err)
return
Expand Down
Loading

0 comments on commit e2e629c

Please sign in to comment.