Skip to main content

Troubleshooting: Running Nodes

How to verify the integrity of the Nitro database I currently have?

We have an accumulator hash on all messages, which means that a message can't be added to the database without the previous message being correct.

To confirm that everything's working properly, you could just make sure that it's syncing and that the latest block is consistent with other Arbitrum nodes; e.g., you could check it against Arbiscan (note that Arbiscan's search field doesn't support searching by block hash).

How can I check if the node is running properly and diagnose the issue if it is not?

We have trace-level logging RPC request implemented on our node. You could use it to log all requests and responses at the trace level. (The performance impact of this should be negligible compared to the network overhead of an RPC request in the first place, especially considering that the request/response will only be serialized for logging if that log level is enabled.)

How do I run a node?

See instructions here!

Can I run an Arbitrum Node in p2p mode?

Arbitrum doesn't have a consensus mechanism, so "p2p mode" doesn't apply. For nodes to sync to the latest chain state, they connect to an L1 node to sync the chain's history that's been posted in calldata and connect to the Sequencer feed for the transactions that have yet to be posted in batches. In no case do nodes need to peer up and sync with each other.

How do I read messages from the Sequencer feed?

Running an Arbitrum relay locally as a Feed Relay gives users the ability to subscribe to the Sequencer feed at for real-time data as the Sequencer accepts and orders transactions off-chain.

When connected to websocket port 9642 of the local relay, you'll receive feed data that looks something like this:

{
"version": 1,
"messages": [{
"sequenceNumber": 25757171,
"message": {
"message": {
"header": {
"kind": 3,
"sender": "0xa4b000000000000000000073657175656e636572",
"blockNumber": 16238523,
"timestamp": 1671691403,
"requestId": null,
"baseFeeL1": null
},
"l2Msg": "BAL40oKksUiElQL5AISg7rsAgxb6o5SZbYNoIF2DTixsqDpD2xII9GJLG4C4ZAhh6N0AAAAAAAAAAAAAAAC7EQiq1R1VYgL3/oXgvD921hYRyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArAAaAkebuEnSAUvrWVBGTxA7W+ZMNn5uyLlbOH7Nrs0bYOv6AOxQPqAo2UB0Z7vqlugjn+BUl0drDcWejBfDiPEC6jQA=="
},
"delayedMessagesRead": 354560
},
"signature": null
}]
}

Breaking this down a bit: the top-level data structure is defined by the BroadcastMessage struct:


type BroadcastMessage struct {
Version int `json:"version"`
// Note: "Messages" is slightly ambiguous naming since there are different types of messages
Messages []*BroadcastFeedMessage `json:"messages,omitempty"`
ConfirmedSequenceNumberMessage *ConfirmedSequenceNumberMessage `json:"confirmedSequenceNumberMessage,omitempty"`
}

The messages field is the BroadcastFeedMessage struct:


type BroadcastFeedMessage struct {
SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"`
Message arbstate.MessageWithMetadata `json:"message"`
Signature []byte `json:"signature"`
}

Each message conforms to arbstate.MessageWithMetadata:


type MessageWithMetadata struct {
Message *arbos.L1IncomingMessage `json:"message"`
DelayedMessagesRead uint64 `json:"delayedMessagesRead"`
}

Finally, we get the transaction's information in the message subfield as an L1IncomingMessage:

type L1IncomingMessage struct {
Header *L1IncomingMessageHeader `json:"header"`
L2msg []byte `json:"l2Msg"`
// Only used for `L1MessageType_BatchPostingReport`
BatchGasCost *uint64 `json:"batchGasCost,omitempty" rlp:"optional"`
}

In this file, we can find a ParseL2Transactions function, which you can use this to decode the message.

How do I run a node locally for development?

See instructions here.

We recommend running nitro nodes via docker; to compile directly / run without docker, you can follow the steps in this file then run the executable directly.