Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live tracing experimental MVP support for arbitrum nitro #2934

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
[submodule "go-ethereum"]
path = go-ethereum
url = https://github.com/OffchainLabs/go-ethereum.git
[submodule "fastcache"]
path = fastcache
url = https://github.com/OffchainLabs/fastcache.git
Expand Down Expand Up @@ -38,3 +35,7 @@
[submodule "safe-smart-account"]
path = safe-smart-account
url = https://github.com/safe-global/safe-smart-account.git
[submodule "go-ethereum"]
path = go-ethereum
url = https://github.com/Tenderly/arb-go-ethereum.git
branch = live-tracing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this change.
You should have a separate PR to our go-ethereum fork, point to it from your comment ("this pulls in + link"), and add a commit which sets go-ethereum to the specific commit of your geth-change.
We have merge checks that will not allow nitro commit to be merged before the geth PR is also merged.

45 changes: 42 additions & 3 deletions arbos/block_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/core/tracing"
"math"
"math/big"

Expand Down Expand Up @@ -147,6 +148,7 @@ func ProduceBlock(
chainConfig *params.ChainConfig,
isMsgForPrefetch bool,
runMode core.MessageRunMode,
blockChain *core.BlockChain,
) (*types.Block, types.Receipts, error) {
txes, err := ParseL2Transactions(message, chainConfig.ChainID)
if err != nil {
Expand All @@ -156,7 +158,17 @@ func ProduceBlock(

hooks := NoopSequencingHooks()
return ProduceBlockAdvanced(
message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, isMsgForPrefetch, runMode,
message.Header,
txes,
delayedMessagesRead,
lastBlockHeader,
statedb,
chainContext,
chainConfig,
hooks,
isMsgForPrefetch,
runMode,
blockChain,
)
}

Expand All @@ -172,7 +184,21 @@ func ProduceBlockAdvanced(
sequencingHooks *SequencingHooks,
isMsgForPrefetch bool,
runMode core.MessageRunMode,
) (*types.Block, types.Receipts, error) {
blockChain *core.BlockChain,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems better to get a VMConfig than a BlockChain.

) (outBlock *types.Block, outReceipt types.Receipts, outError error) {
// IMPORTANT:
// func to get the VMConfig from the blockChain, or a default one if it's not available
// this is a way how we propagate Tenderly tracer to the VMConfig instead of vm.Config{}
getVMConfig := func() vm.Config {
if blockChain != nil && blockChain.GetVMConfig() != nil {
return *blockChain.GetVMConfig()
}
return vm.Config{}
}

defer func() {
getVMConfig().Tracer.OnBlockEnd(outError)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hook was missing a call, so I've added it manually here

}()

arbState, err := arbosState.OpenSystemArbosState(statedb, nil, true)
if err != nil {
Expand All @@ -192,6 +218,16 @@ func ProduceBlockAdvanced(
}

header := createNewHeader(lastBlockHeader, l1Info, arbState, chainConfig)

getVMConfig().Tracer.OnBlockStart(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hook was missing a call, so I've added it manually here

tracing.BlockEvent{
Block: types.NewBlock(header, &types.Body{Transactions: nil}, nil, trie.NewStackTrie(nil)),
TD: nil,
Finalized: nil,
Safe: nil,
},
)

signer := types.MakeSigner(chainConfig, header.Number, header.Time)
// Note: blockGasLeft will diverge from the actual gas left during execution in the event of invalid txs,
// but it's only used as block-local representation limiting the amount of work done in a block.
Expand Down Expand Up @@ -312,6 +348,9 @@ func ProduceBlockAdvanced(
statedb.SetTxContext(tx.Hash(), len(receipts)) // the number of successful state transitions

gasPool := gethGas

statedb.SetLogger(getVMConfig().Tracer)

receipt, result, err := core.ApplyTransactionWithResultFilter(
chainConfig,
chainContext,
Expand All @@ -321,7 +360,7 @@ func ProduceBlockAdvanced(
header,
tx,
&header.GasUsed,
vm.Config{},
getVMConfig(),
runMode,
func(result *core.ExecutionResult) error {
return hooks.PostTxFilter(header, statedb, arbState, tx, sender, dataGas, result)
Expand Down
29 changes: 26 additions & 3 deletions cmd/nitro/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,18 @@ func rebuildLocalWasm(ctx context.Context, config *gethexec.Config, l2BlockChain
return chainDb, l2BlockChain, nil
}

func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeConfig, chainId *big.Int, cacheConfig *core.CacheConfig, targetConfig *gethexec.StylusTargetConfig, persistentConfig *conf.PersistentConfig, l1Client *ethclient.Client, rollupAddrs chaininfo.RollupAddresses) (ethdb.Database, *core.BlockChain, error) {
func openInitializeChainDb(
ctx context.Context,
stack *node.Node,
config *NodeConfig,
chainId *big.Int,
cacheConfig *core.CacheConfig,
targetConfig *gethexec.StylusTargetConfig,
persistentConfig *conf.PersistentConfig,
l1Client *ethclient.Client,
rollupAddrs chaininfo.RollupAddresses,
tracingConfig json.RawMessage,
) (ethdb.Database, *core.BlockChain, error) {
if !config.Init.Force {
if readOnlyDb, err := stack.OpenDatabaseWithFreezerWithExtraOptions("l2chaindata", 0, 0, config.Persistent.Ancient, "l2chaindata/", true, persistentConfig.Pebble.ExtraOptions("l2chaindata")); err == nil {
if chainConfig := gethexec.TryReadStoredChainConfig(readOnlyDb); chainConfig != nil {
Expand Down Expand Up @@ -575,7 +586,13 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo
if err != nil {
return chainDb, nil, fmt.Errorf("error pruning: %w", err)
}
l2BlockChain, err := gethexec.GetBlockChain(chainDb, cacheConfig, chainConfig, config.Execution.TxLookupLimit)
l2BlockChain, err := gethexec.GetBlockChain(
chainDb,
cacheConfig,
chainConfig,
config.Execution.TxLookupLimit,
tracingConfig,
)
if err != nil {
return chainDb, nil, err
}
Expand Down Expand Up @@ -726,7 +743,13 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo
if chainConfig == nil {
return chainDb, nil, errors.New("no --init.* mode supplied and chain data not in expected directory")
}
l2BlockChain, err = gethexec.GetBlockChain(chainDb, cacheConfig, chainConfig, config.Execution.TxLookupLimit)
l2BlockChain, err = gethexec.GetBlockChain(
chainDb,
cacheConfig,
chainConfig,
config.Execution.TxLookupLimit,
tracingConfig,
)
if err != nil {
return chainDb, nil, err
}
Expand Down
21 changes: 20 additions & 1 deletion cmd/nitro/nitro.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"crypto/ecdsa"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -170,6 +171,13 @@ func mainImpl() int {
defer cancelFunc()

args := os.Args[1:]

//var tracingConfig json.RawMessage
// TODO: We need a better way to pass this config to the tracer
// this is just a temporary solution and
// it is a demonstration of how to pass a config to live tracer as a json.RawMessage via tracingConfig
tracingConfig := json.RawMessage(`{"local_store": "/livetracing/output", "ttl": 14, "network_id": "42161", "enable_code_tracing":false }`)

nodeConfig, l2DevWallet, err := ParseNode(ctx, args)
if err != nil {
confighelpers.PrintErrorAndExit(err, printSampleUsage)
Expand Down Expand Up @@ -437,7 +445,18 @@ func mainImpl() int {
}
}

chainDb, l2BlockChain, err := openInitializeChainDb(ctx, stack, nodeConfig, new(big.Int).SetUint64(nodeConfig.Chain.ID), gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), &nodeConfig.Execution.StylusTarget, &nodeConfig.Persistent, l1Client, rollupAddrs)
chainDb, l2BlockChain, err := openInitializeChainDb(
ctx,
stack,
nodeConfig,
new(big.Int).SetUint64(nodeConfig.Chain.ID),
gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching),
&nodeConfig.Execution.StylusTarget,
&nodeConfig.Persistent,
l1Client,
rollupAddrs,
tracingConfig,
)
if l2BlockChain != nil {
deferFuncs = append(deferFuncs, func() { l2BlockChain.Stop() })
}
Expand Down
12 changes: 11 additions & 1 deletion cmd/replay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,17 @@ func main() {
message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee)

chainContext := WavmChainContext{}
newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, false, core.MessageReplayMode)
newBlock, _, err = arbos.ProduceBlock(
message.Message,
message.DelayedMessagesRead,
lastBlockHeader,
statedb,
chainContext,
chainConfig,
false,
core.MessageReplayMode,
nil, // todo: tenderly
)
if err != nil {
panic(err)
}
Expand Down
1 change: 1 addition & 0 deletions execution/gethexec/block_recorder.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func (r *BlockRecorder) RecordBlockCreation(
chainConfig,
false,
core.MessageReplayMode,
nil, // todo: tenderly
)
if err != nil {
return nil, err
Expand Down
20 changes: 17 additions & 3 deletions execution/gethexec/blockchain.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package gethexec

import (
"encoding/json"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/eth/tracers/live"
"math/big"
"time"

Expand Down Expand Up @@ -202,13 +204,25 @@ func WriteOrTestChainConfig(chainDb ethdb.Database, config *params.ChainConfig)
return nil
}

func GetBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, chainConfig *params.ChainConfig, txLookupLimit uint64) (*core.BlockChain, error) {
func GetBlockChain(
chainDb ethdb.Database,
cacheConfig *core.CacheConfig,
chainConfig *params.ChainConfig,
txLookupLimit uint64,
tracingConfig json.RawMessage,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should not need to chainge blockchain at all. However, if you do - seems better to get here *tracing.Hooks that a json.RawMessage.

) (*core.BlockChain, error) {
engine := arbos.Engine{
IsSequencer: true,
}

tenderlySimpleTracerHooks, err := live.NewTenderlySimpleTracerHooks(tracingConfig)
if err != nil {
return nil, err
}

vmConfig := vm.Config{
EnablePreimageRecording: false,
Tracer: tenderlySimpleTracerHooks,
}

return core.NewBlockChain(chainDb, cacheConfig, chainConfig, nil, nil, engine, vmConfig, shouldPreserveFalse, &txLookupLimit)
Expand All @@ -220,7 +234,7 @@ func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig
// When using path scheme, and the stored state trie is not empty,
// WriteOrTestGenBlock is not able to recover EmptyRootHash state trie node.
// In that case Nitro doesn't test genblock, but just returns the BlockChain.
return GetBlockChain(chainDb, cacheConfig, chainConfig, txLookupLimit)
return GetBlockChain(chainDb, cacheConfig, chainConfig, txLookupLimit, nil)
}

err := WriteOrTestGenblock(chainDb, cacheConfig, initData, chainConfig, initMessage, accountsPerSync)
Expand All @@ -231,7 +245,7 @@ func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig
if err != nil {
return nil, err
}
return GetBlockChain(chainDb, cacheConfig, chainConfig, txLookupLimit)
return GetBlockChain(chainDb, cacheConfig, chainConfig, txLookupLimit, nil)
}

// Don't preserve reorg'd out blocks
Expand Down
22 changes: 14 additions & 8 deletions execution/gethexec/executionengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes.
hooks,
false,
core.MessageCommitMode,
nil,
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -696,6 +697,7 @@ func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWith
s.bc.Config(),
isMsgForPrefetch,
runMode,
s.bc,
)

return block, statedb, receipts, err
Expand Down Expand Up @@ -873,14 +875,18 @@ func (s *ExecutionEngine) digestMessageWithBlockMutex(num arbutil.MessageIndex,
}

startTime := time.Now()
if s.prefetchBlock && msgForPrefetch != nil {
go func() {
_, _, _, err := s.createBlockFromNextMessage(msgForPrefetch, true)
if err != nil {
return
}
}()
}

// TODO: IMPORTANT !!!
// This will cause a tracer to be called multiple times for the same block concurrently
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not the only thing that'll cause tracer to be called multiple times.
In normal case we have no reorgs, but a node might reexecute the same block multiple times for ultiple reasons.

I didn't view geth side yet, but I think a better solution will be to:

  • remove all changes to blockchain
  • Pass a vm.Config to ProduceBlock which could enable tracing
  • have a config that controls tracing and feed it to ExecutionEngine (it currently does not have a config.. have a look how we use configs for e.g. SyncMonitorConfig)
  • Inside func (s *ExecutionEngine) createBlockFromNextMessage - pass to ProduceBlock a vm.Config that enables tracing if it was enabled in config and only if isMsgForPrefetch == false

// Removed for now, but this should be properly addressed
//if s.prefetchBlock && msgForPrefetch != nil {
// go func() {
// _, _, _, err := s.createBlockFromNextMessage(msgForPrefetch, true)
// if err != nil {
// return
// }
// }()
//}

block, statedb, receipts, err := s.createBlockFromNextMessage(msg, false)
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion system_tests/state_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,15 @@ func BuildBlock(
}

block, _, err := arbos.ProduceBlock(
l1Message, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, false, runMode,
l1Message,
delayedMessagesRead,
lastBlockHeader,
statedb,
chainContext,
chainConfig,
false,
runMode,
nil,
)
return block, err
}
Expand Down