Refactor the SyncService to more closely implement the specification#552
Refactor the SyncService to more closely implement the specification#552
Conversation
🦋 Changeset detectedLatest commit: 112f0e8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
2480b2d to
570b79a
Compare
There was a problem hiding this comment.
I've rebased this to master and resolved the conflicts.
High level logic looks good - I think we need more unit tests before we merge this, e.g. checks that batches get correctly applied, that the tip gets synced, that the timestamps are correct after a mix of batch/queue transactions becoming available
| if err != nil { | ||
| return fmt.Errorf("Canot get enqueue transaction; %w", err) | ||
| } | ||
| if err := s.applyTransaction(tx); err != nil { |
There was a problem hiding this comment.
Should there be any validation on the transaction here, similar to the ValidateAndApplySequencerTransaction?
There was a problem hiding this comment.
The enqueue transactions are validated by the L1 contracts so we can assume that they have already been validated by this point
There was a problem hiding this comment.
Got it, let's add this as a comment for clarity. Something like "We do not need to do validation checks here because they've already been done in the CTC, link to code"
| s.SetLatestL1Timestamp(ts) | ||
| s.SetLatestL1BlockNumber(bn.Uint64()) |
There was a problem hiding this comment.
Is this desired behavior? Why? Maybe obvious, but should be noted in the comment that there's side effects to the blocknumber and timestamp.
There was a problem hiding this comment.
If the incoming transaction's timestamp is greater than the latest timestamp, increase the latest timestamp to the incoming transaction's timestamp. This is to prevent monotonicity errors
| s.txFeed.Send(core.NewTxsEvent{Txs: txs}) | ||
| // Block until the transaction has been added to the chain | ||
| log.Debug("Waiting for transaction to be added to chain", "hash", tx.Hash().Hex()) | ||
| <-s.chainHeadCh |
There was a problem hiding this comment.
Just to clarify: This is the reason transactions go through the full txpool / miner codepath?
There was a problem hiding this comment.
They do not go through the txpool but they do go through the miner. This is to block until the transaction sent over the feed is added to the chain. Without this, calling reorganize breaks with an unknown ancestor error. This is due to many transactions sitting in the channel - a reorg would need to empty the channel before reorg'ing if we went with that approach. There may be other complications, for example when shutting down we would need to wait for the channel to fully empty, otherwise transactions may be skipped. This PR does not include the reorganize logic but it needs to be added in the future
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
| } | ||
|
|
||
| // ReadHeadBatchIndex will read the known tip of the processed batches | ||
| func ReadHeadBatchIndex(db ethdb.KeyValueReader) *uint64 { |
There was a problem hiding this comment.
The name (Read/Write)HeadBatchIndex is a bit confusing, might consider updating it.
There was a problem hiding this comment.
{Read/Write}HeadTransactionBatchIndex ?
Open to a suggestion
| // If the timestamp of the transaction is greater than the sync | ||
| // service's locally maintained timestamp, update the timestamp and | ||
| // blocknumber to equal that of the transaction's. This should happen | ||
| // with `enqueue` transactions. |
There was a problem hiding this comment.
You say that this should happen with enqueue transactions. Should we add a sanity check that the tx.QueueOrigin().Uint64() == uint64(types.QueueOriginL1ToL2) in this case then? Or is it guaranteed that tx.L1Timestamp() != 0 only in the case that the transaction is an enqueue?
There was a problem hiding this comment.
It is guaranteed that tx.L1Timestamp() == 0 only for types.QueueOriginSequencer transactions, assuming no bugs in the upstream software
| if tx.GetMeta().Index == nil { | ||
| index := s.GetLatestIndex() | ||
| if index == nil { | ||
| tx.SetIndex(0) |
There was a problem hiding this comment.
TBH I'm somewhat averse to having the client mutate transactions directly like this. It feels prone to error. For instance, verifiers should never be mutating transactions, right? Could that be an invariant that we try to enforce?
There was a problem hiding this comment.
The transaction index must be set by the SyncService (both sequencer and verifier) as the index is dependent on the current blockchain. It is set and then indexed
| s.txFeed.Send(core.NewTxsEvent{Txs: txs}) | ||
| // Block until the transaction has been added to the chain | ||
| log.Trace("Waiting for transaction to be added to chain", "hash", tx.Hash().Hex()) | ||
| <-s.chainHeadCh |
There was a problem hiding this comment.
Will this cause sendTransaction RPC requests to hang until all previous transactions have been fully executed and included in the chain?
There was a problem hiding this comment.
Good question - I believe it will, which is not ideal at all but we need to implement a queue asap that transactions are added to as they come in. We will currently block once the txFeed channel is full and on shutdown, all of the txs are lost that are waiting to get into the channel or are in the channel. Short term we need a queue that txs are added to from the RPC handler and the tx hash is returned to the user and then the sync service pulls from that queue continuously. Our longer term plan is to have a single database be the source of truth where transactions are indexed and pulled in one by one.
Right now there isn't a good way to get around having this synchronization mechanism if we want to do reorgs and I do have a follow up PR that removes this by removing the miner from the hot code path and using a custom consensus.Engine
There was a problem hiding this comment.
I'm OK with this change (removed here) as long as we include the miner change ASAP.
| // the L1 block that holds the transaction. This should never happen but is | ||
| // a sanity check to prevent fraudulent execution. |
There was a problem hiding this comment.
Should the be a panic if it should never happen?
There was a problem hiding this comment.
We cannot panic here - it would be ideal to but we need a db healer after it panics
|
🥳🥳🥳🥳 |
…end (#552) * feat(book): Custom backend, `kona-executor` extensions, and FPVM backend * updates * README rm telegram ref
<!-- Thank you for your Pull Request. Please provide a description above and review the requirements below. Bug fixes and new features should include tests. Contributors guide: https://github.com/alloy-rs/core/blob/main/CONTRIBUTING.md The contributors guide includes instructions for running rustfmt and building the documentation. --> <!-- ** Please select "Allow edits from maintainers" in the PR Options ** --> ## Motivation <!-- Explain the context and why you're making that change. What is the problem you're trying to solve? In some cases there is not a problem and this can be thought of as being the motivation for your change. --> ## Solution <!-- Summarize the solution and provide any necessary context needed to understand the code change. --> ## PR Checklist - [ ] Added Tests - [ ] Added Documentation - [ ] Breaking changes
Description
Replaces #477
Description
Implements the
SyncServicespechttps://github.com/ethereum-optimism/specs/blob/main/l2-geth/transaction-ingestor.md
The spec was designed to prevent double ingestion or skipping of ingestion of transactions. The function names in the spec should match 1:1 the methods on the
SyncService. The actual logic implemented differs slightly from the spec in how it implements its break conditions, but is functionally equivalent.BatchIndexbecause that was more simple and less bug proneAdds a new config option:
ROLLUP_BACKEND- it must be set to L1 or L2. This sets the query param that is used by theRollupClientwhen querying the DTLAdditional context
#477 was broken up and this is the PR that includes most of the logic around the
SyncServicerefactor