Skip to content

Commit

Permalink
miner: support interop block building
Browse files Browse the repository at this point in the history
Co-authored-by: axelKingsley <axel.kingsley@gmail.com>
Co-authored-by: Tyler Smith <mail@tcry.pt>
  • Loading branch information
3 people committed Oct 31, 2024
1 parent 969069e commit c4d09bc
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ var (
utils.RollupSequencerTxConditionalCostRateLimitFlag,
utils.RollupHistoricalRPCFlag,
utils.RollupHistoricalRPCTimeoutFlag,
utils.RollupInteropRPCFlag,
utils.RollupDisableTxPoolGossipFlag,
utils.RollupComputePendingBlock,
utils.RollupHaltOnIncompatibleProtocolVersionFlag,
Expand Down
9 changes: 9 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,12 @@ var (
Category: flags.RollupCategory,
}

RollupInteropRPCFlag = &cli.StringFlag{
Name: "rollup.interoprpc",
Usage: "RPC endpoint for interop message verification (experimental).",
Category: flags.RollupCategory,
}

RollupDisableTxPoolGossipFlag = &cli.BoolFlag{
Name: "rollup.disabletxpoolgossip",
Usage: "Disable transaction pool gossip.",
Expand Down Expand Up @@ -1941,6 +1947,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
if ctx.IsSet(RollupHistoricalRPCTimeoutFlag.Name) {
cfg.RollupHistoricalRPCTimeout = ctx.Duration(RollupHistoricalRPCTimeoutFlag.Name)
}
if ctx.IsSet(RollupInteropRPCFlag.Name) {
cfg.InteropMessageRPC = ctx.String(RollupInteropRPCFlag.Name)
}
cfg.RollupDisableTxPoolGossip = ctx.Bool(RollupDisableTxPoolGossipFlag.Name)
cfg.RollupDisableTxPoolAdmission = cfg.RollupSequencerHTTP != "" && !ctx.Bool(RollupEnableTxPoolAdmissionFlag.Name)
cfg.RollupHaltOnIncompatibleProtocolVersion = ctx.String(RollupHaltOnIncompatibleProtocolVersionFlag.Name)
Expand Down
11 changes: 11 additions & 0 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,21 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
return ApplyTransactionExtended(config, bc, author, gp, statedb, header, tx, usedGas, cfg, nil)
}

type ApplyTransactionOpts struct {
PostValidation func(evm *vm.EVM, result *ExecutionResult) error
}

func ApplyTransactionExtended(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, extraOpts *ApplyTransactionOpts) (*types.Receipt, error) {
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
if err != nil {
return nil, err
}
if extraOpts != nil {
msg.PostValidation = extraOpts.PostValidation
}
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author, config, statedb)
txContext := NewEVMTxContext(msg)
Expand Down
9 changes: 9 additions & 0 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ type Message struct {
IsDepositTx bool // IsDepositTx indicates the message is force-included and can persist a mint.
Mint *big.Int // Mint is the amount to mint before EVM processing, or nil if there is no minting.
RollupCostData types.RollupCostData // RollupCostData caches data to compute the fee we charge for data availability

PostValidation func(evm *vm.EVM, result *ExecutionResult) error
}

// TransactionToMessage converts a transaction into a Message.
Expand Down Expand Up @@ -447,6 +449,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
}
err = nil
}

if st.msg.PostValidation != nil {
if err := st.msg.PostValidation(st.evm, result); err != nil {
return nil, err
}
}

return result, err
}

Expand Down
162 changes: 162 additions & 0 deletions core/types/interoptypes/interop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package interoptypes

import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"

"github.com/holiman/uint256"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)

var ExecutingMessageEventTopic = crypto.Keccak256Hash([]byte("ExecutingMessage(bytes32,(address,uint256,uint256,uint256,uint256))"))

type Message struct {
Identifier Identifier `json:"identifier"`
PayloadHash common.Hash `json:"payloadHash"`
}

func (m *Message) DecodeEvent(topics []common.Hash, data []byte) error {
if len(topics) != 2 { // event hash, indexed payloadHash
return fmt.Errorf("unexpected number of event topics: %d", len(topics))
}
if topics[0] != ExecutingMessageEventTopic {
return fmt.Errorf("unexpected event topic %q", topics[0])
}
if len(data) != 32*5 {
return fmt.Errorf("unexpected identifier data length: %d", len(data))
}
take := func(length uint) []byte {
taken := data[:length]
data = data[length:]
return taken
}
takeZeroes := func(length uint) error {
for _, v := range take(length) {
if v != 0 {
return errors.New("expected zero")
}
}
return nil
}
if err := takeZeroes(12); err != nil {
return fmt.Errorf("invalid address padding: %w", err)
}
m.Identifier.Origin = common.Address(take(20))
if err := takeZeroes(32 - 8); err != nil {
return fmt.Errorf("invalid block number padding: %w", err)
}
m.Identifier.BlockNumber = binary.BigEndian.Uint64(take(8))
if err := takeZeroes(32 - 8); err != nil {
return fmt.Errorf("invalid log index padding: %w", err)
}
m.Identifier.LogIndex = binary.BigEndian.Uint64(take(8))
if err := takeZeroes(32 - 8); err != nil {
return fmt.Errorf("invalid timestamp padding: %w", err)
}
m.Identifier.Timestamp = binary.BigEndian.Uint64(take(8))
m.Identifier.ChainID.SetBytes32(take(32))
m.PayloadHash = topics[1]
return nil
}

func ExecutingMessagesFromLogs(logs []*types.Log) ([]Message, error) {
var executingMessages []Message
for i, l := range logs {
if l.Address == params.InteropCrossL2InboxAddress {
// ignore events that do not match this
if len(l.Topics) == 0 || l.Topics[0] != ExecutingMessageEventTopic {
continue
}
var msg Message
if err := msg.DecodeEvent(l.Topics, l.Data); err != nil {
return nil, fmt.Errorf("invalid executing message %d, tx-log %d: %w", len(executingMessages), i, err)
}
executingMessages = append(executingMessages, msg)
}
}
return executingMessages, nil
}

type Identifier struct {
Origin common.Address
BlockNumber uint64
LogIndex uint64
Timestamp uint64
ChainID uint256.Int // flat, not a pointer, to make Identifier safe as map key
}

type identifierMarshaling struct {
Origin common.Address `json:"origin"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
LogIndex hexutil.Uint64 `json:"logIndex"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ChainID hexutil.U256 `json:"chainID"`
}

func (id Identifier) MarshalJSON() ([]byte, error) {
var enc identifierMarshaling
enc.Origin = id.Origin
enc.BlockNumber = hexutil.Uint64(id.BlockNumber)
enc.LogIndex = hexutil.Uint64(id.LogIndex)
enc.Timestamp = hexutil.Uint64(id.Timestamp)
enc.ChainID = (hexutil.U256)(id.ChainID)
return json.Marshal(&enc)
}

func (id *Identifier) UnmarshalJSON(input []byte) error {
var dec identifierMarshaling
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
id.Origin = dec.Origin
id.BlockNumber = uint64(dec.BlockNumber)
id.LogIndex = uint64(dec.LogIndex)
id.Timestamp = uint64(dec.Timestamp)
id.ChainID = (uint256.Int)(dec.ChainID)
return nil
}

type SafetyLevel string

func (lvl SafetyLevel) String() string {
return string(lvl)
}

func (lvl SafetyLevel) Valid() bool {
switch lvl {
case Finalized, Safe, CrossUnsafe, Unsafe:
return true
default:
return false
}
}

func (lvl SafetyLevel) MarshalText() ([]byte, error) {
return []byte(lvl), nil
}

func (lvl *SafetyLevel) UnmarshalText(text []byte) error {
if lvl == nil {
return errors.New("cannot unmarshal into nil SafetyLevel")
}
x := SafetyLevel(text)
if !x.Valid() {
return fmt.Errorf("unrecognized safety level: %q", text)
}
*lvl = x
return nil
}

const (
Finalized SafetyLevel = "finalized"
Safe SafetyLevel = "safe"
CrossUnsafe SafetyLevel = "cross-unsafe"
Unsafe SafetyLevel = "unsafe"
)
22 changes: 22 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package eth
import (
"context"
"encoding/json"
"errors"
"fmt"
"math/big"
"runtime"
Expand All @@ -38,10 +39,12 @@ import (
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types/interoptypes"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/interop"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/eth/tracers"
Expand Down Expand Up @@ -79,6 +82,8 @@ type Ethereum struct {
seqRPCService *rpc.Client
historicalRPCService *rpc.Client

interopRPC *interop.InteropClient

// DB interfaces
chainDb ethdb.Database // Block chain database

Expand Down Expand Up @@ -320,6 +325,10 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
eth.historicalRPCService = client
}

if config.InteropMessageRPC != "" {
eth.interopRPC = interop.NewInteropClient(config.InteropMessageRPC)
}

// Start the RPC service
eth.netRPCService = ethapi.NewNetAPI(eth.p2pServer, networkID)

Expand Down Expand Up @@ -483,6 +492,12 @@ func (s *Ethereum) Stop() error {
if s.historicalRPCService != nil {
s.historicalRPCService.Close()
}
if s.interopRPC != nil {
s.interopRPC.Close()
}
if s.miner != nil {
s.miner.Close()
}

// Clean shutdown marker as the last thing before closing db
s.shutdownTracker.Stop()
Expand Down Expand Up @@ -548,3 +563,10 @@ func (s *Ethereum) HandleRequiredProtocolVersion(required params.ProtocolVersion
}
return nil
}

func (s *Ethereum) CheckMessages(ctx context.Context, messages []interoptypes.Message, minSafety interoptypes.SafetyLevel) error {
if s.interopRPC == nil {
return errors.New("cannot check interop messages, no RPC available")
}
return s.interopRPC.CheckMessages(ctx, messages, minSafety)
}
2 changes: 2 additions & 0 deletions eth/ethconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ type Config struct {
RollupDisableTxPoolGossip bool
RollupDisableTxPoolAdmission bool
RollupHaltOnIncompatibleProtocolVersion string

InteropMessageRPC string `toml:",omitempty"`
}

// CreateConsensusEngine creates a consensus engine for the given chain config.
Expand Down
6 changes: 6 additions & 0 deletions eth/ethconfig/gen_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c4d09bc

Please sign in to comment.