-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split state transition into separate method (#11)
- Loading branch information
Showing
2 changed files
with
144 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |