Skip to content

Commit

Permalink
Split state transition into separate method (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdehoog authored Nov 1, 2024
1 parent d5ec28b commit 0f6c0ae
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 101 deletions.
105 changes: 4 additions & 101 deletions op-enclave/enclave/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ import (
"os"
"time"

"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hf/nitrite"
"github.com/hf/nsm"
"github.com/hf/nsm/request"
Expand Down Expand Up @@ -250,115 +247,21 @@ func (s *Server) ExecuteStateless(
messageAccount *eth.AccountResult,
prevMessageAccountHash common.Hash,
) (*Proposal, error) {
config := NewChainConfig(cfg)

l1OriginHash := l1Origin.Hash()
computed := types.DeriveSha(l1Receipts, trie.NewStackTrie(nil))
if computed != l1Origin.ReceiptHash {
return nil, errors.New("invalid receipts")
}

w := &stateless.Witness{}
err := rlp.DecodeBytes(witness, w)
if err != nil {
return nil, fmt.Errorf("failed to decode witness: %w", err)
}

config := NewChainConfig(cfg)
l1OriginHash := l1Origin.Hash()
previousBlockHeader := w.Headers[0]
previousBlockHash := previousBlockHeader.Hash()
if blockHeader.ParentHash != previousBlockHash {
return nil, errors.New("invalid parent hash")
}

unmarshalTxs := func(rlp []hexutil.Bytes) (types.Transactions, error) {
txs := make(types.Transactions, len(rlp))
for i, tx := range rlp {
txs[i] = new(types.Transaction)
if err := txs[i].UnmarshalBinary(tx); err != nil {
return nil, fmt.Errorf("failed to unmarshal transaction: %w", err)
}
}
return txs, nil
}
previousTxs, err := unmarshalTxs(previousBlockTxs)
err = ExecuteStateless(ctx, config.ChainConfig, config.ToRollupConfig(),
l1Origin, l1Receipts, previousBlockTxs, blockHeader, blockTxs, w, messageAccount)
if err != nil {
return nil, err
}
txs, err := unmarshalTxs(blockTxs)
if err != nil {
return nil, err
}

previousTxHash := types.DeriveSha(previousTxs, trie.NewStackTrie(nil))
if previousTxHash != previousBlockHeader.TxHash {
return nil, errors.New("invalid tx hash")
}

previousBlock := types.NewBlockWithHeader(previousBlockHeader).WithBody(types.Body{
Transactions: previousTxs,
})

rollupConfig := config.ToRollupConfig()
l2Parent, err := derive.L2BlockToBlockRef(rollupConfig, previousBlock)
if err != nil {
return nil, fmt.Errorf("failed to convert L2 block to block ref: %w", err)
}

if l2Parent.L1Origin.Hash != l1OriginHash && l2Parent.L1Origin.Hash != l1Origin.ParentHash {
return nil, errors.New("invalid L1 origin")
}

l1Fetcher := NewL1ReceiptsFetcher(l1OriginHash, l1Origin, l1Receipts)
l2Fetcher := NewL2SystemConfigFetcher(rollupConfig, previousBlockHash, previousBlockHeader, previousTxs)
attributeBuilder := derive.NewFetchingAttributesBuilder(rollupConfig, l1Fetcher, l2Fetcher)
payload, err := attributeBuilder.PreparePayloadAttributes(context.Background(), l2Parent, eth.BlockID{
Hash: l1OriginHash,
Number: l1Origin.Number.Uint64(),
})
if err != nil {
return nil, fmt.Errorf("failed to prepare payload attributes: %w", err)
}

if txs.Len() < len(payload.Transactions) {
return nil, errors.New("invalid transaction count")
}

for i, payloadTx := range payload.Transactions {
tx := txs[i]
if !tx.IsDepositTx() {
return nil, errors.New("invalid transaction type")
}
if !bytes.Equal(blockTxs[i], payloadTx) {
return nil, errors.New("invalid deposit transaction")
}
}

// block must only contain deposit transactions if it is outside the sequencer drift
if txs.Len() > len(payload.Transactions) &&
blockHeader.Time > l1Origin.Time+maxSequencerDriftFjord {
return nil, errors.New("L1 origin is too old")
}

expectedRoot := blockHeader.Root
blockHeader.Root = common.Hash{}
blockHeader.ReceiptHash = common.Hash{}
block := types.NewBlockWithHeader(blockHeader).WithBody(types.Body{
Transactions: txs,
})
blockHeader.Root, blockHeader.ReceiptHash, err = core.ExecuteStateless(config.ChainConfig, block, w)
if err != nil {
return nil, fmt.Errorf("failed to execute stateless: %w", err)
}
if blockHeader.Root != expectedRoot {
return nil, errors.New("invalid state root")
}

if messageAccount.Address.Cmp(l2ToL1MessagePasserAddress) != 0 {
return nil, errors.New("invalid message account address")
}
if err = messageAccount.Verify(blockHeader.Root); err != nil {
return nil, fmt.Errorf("failed to verify message account: %w", err)
}

prevOutputRoot := outputRootV0(previousBlockHeader, prevMessageAccountHash)
outputRoot := outputRootV0(blockHeader, messageAccount.StorageHash)
Expand Down
140 changes: 140 additions & 0 deletions op-enclave/enclave/stateless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package enclave

import (
"bytes"
"context"
"errors"
"fmt"

"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/stateless"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)

func ExecuteStateless(
ctx context.Context,
config *params.ChainConfig,
rollupConfig *rollup.Config,
l1Origin *types.Header,
l1Receipts types.Receipts,
previousBlockTxs []hexutil.Bytes,
blockHeader *types.Header,
blockTxs []hexutil.Bytes,
witness *stateless.Witness,
messageAccount *eth.AccountResult,
) error {
l1OriginHash := l1Origin.Hash()
computed := types.DeriveSha(l1Receipts, trie.NewStackTrie(nil))
if computed != l1Origin.ReceiptHash {
return errors.New("invalid receipts")
}

previousBlockHeader := witness.Headers[0]
previousBlockHash := previousBlockHeader.Hash()
if blockHeader.ParentHash != previousBlockHash {
return errors.New("invalid parent hash")
}

unmarshalTxs := func(rlp []hexutil.Bytes) (types.Transactions, error) {
txs := make(types.Transactions, len(rlp))
for i, tx := range rlp {
txs[i] = new(types.Transaction)
if err := txs[i].UnmarshalBinary(tx); err != nil {
return nil, fmt.Errorf("failed to unmarshal transaction: %w", err)
}
}
return txs, nil
}
previousTxs, err := unmarshalTxs(previousBlockTxs)
if err != nil {
return err
}
txs, err := unmarshalTxs(blockTxs)
if err != nil {
return err
}

previousTxHash := types.DeriveSha(previousTxs, trie.NewStackTrie(nil))
if previousTxHash != previousBlockHeader.TxHash {
return errors.New("invalid tx hash")
}

previousBlock := types.NewBlockWithHeader(previousBlockHeader).WithBody(types.Body{
Transactions: previousTxs,
})

l2Parent, err := derive.L2BlockToBlockRef(rollupConfig, previousBlock)
if err != nil {
return fmt.Errorf("failed to convert L2 block to block ref: %w", err)
}

if l2Parent.L1Origin.Hash != l1OriginHash && l2Parent.L1Origin.Hash != l1Origin.ParentHash {
return errors.New("invalid L1 origin")
}

l1Fetcher := NewL1ReceiptsFetcher(l1OriginHash, l1Origin, l1Receipts)
l2Fetcher := NewL2SystemConfigFetcher(rollupConfig, previousBlockHash, previousBlockHeader, previousTxs)
attributeBuilder := derive.NewFetchingAttributesBuilder(rollupConfig, l1Fetcher, l2Fetcher)
payload, err := attributeBuilder.PreparePayloadAttributes(ctx, l2Parent, eth.BlockID{
Hash: l1OriginHash,
Number: l1Origin.Number.Uint64(),
})
if err != nil {
return fmt.Errorf("failed to prepare payload attributes: %w", err)
}

if txs.Len() < len(payload.Transactions) {
return errors.New("invalid transaction count")
}

for i, payloadTx := range payload.Transactions {
tx := txs[i]
if !tx.IsDepositTx() {
return errors.New("invalid transaction type")
}
if !bytes.Equal(blockTxs[i], payloadTx) {
return errors.New("invalid deposit transaction")
}
}

// block must only contain deposit transactions if it is outside the sequencer drift
if txs.Len() > len(payload.Transactions) &&
blockHeader.Time > l1Origin.Time+maxSequencerDriftFjord {
return errors.New("L1 origin is too old")
}

expectedRoot := blockHeader.Root
expectedReceiptHash := blockHeader.ReceiptHash
blockHeader = types.CopyHeader(blockHeader)
blockHeader.Root = common.Hash{}
blockHeader.ReceiptHash = common.Hash{}
block := types.NewBlockWithHeader(blockHeader).WithBody(types.Body{
Transactions: txs,
})
blockHeader.Root, blockHeader.ReceiptHash, err = core.ExecuteStateless(config, block, witness)
if err != nil {
return fmt.Errorf("failed to execute stateless: %w", err)
}
if blockHeader.Root != expectedRoot {
return errors.New("invalid state root")
}
if blockHeader.ReceiptHash != expectedReceiptHash {
return errors.New("invalid receipt hash")
}

if messageAccount.Address.Cmp(l2ToL1MessagePasserAddress) != 0 {
return errors.New("invalid message account address")
}
if err = messageAccount.Verify(blockHeader.Root); err != nil {
return fmt.Errorf("failed to verify message account: %w", err)
}

return nil
}

0 comments on commit 0f6c0ae

Please sign in to comment.