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

feat(admin): getL1ToL2MessageByTxHash(l1DepositTxHash) #280

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

s4rv4d
Copy link
Contributor

@s4rv4d s4rv4d commented Nov 25, 2024

Description
added getL1ToL2MessageByTxHash(l1DepositTxHash), essentially letting devs fetch L1ToL2Message via SourceHash

Metadata

@s4rv4d s4rv4d requested a review from a team as a code owner November 25, 2024 19:45
@s4rv4d s4rv4d marked this pull request as draft November 25, 2024 19:45
@@ -36,7 +36,7 @@ type LogSubscriber interface {
}

// transforms Deposit event logs into DepositTx
func SubscribeDepositTx(ctx context.Context, logSub LogSubscriber, depositContractAddr common.Address, ch chan<- *types.DepositTx) (ethereum.Subscription, error) {
func SubscribeDepositTx(ctx context.Context, logSub LogSubscriber, depositContractAddr common.Address, ch chan<- *types.DepositTx, chLog chan<- *types.Log) (ethereum.Subscription, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

high level it would be cleaner to just pass a single channel here.

if you need other info from the depositTx like some of the log data, you can create a wrapper that includes the deposit tx along with the log

portalAddress := common.Address(opSim.Config().L2Config.L1Addresses.OptimismPortalProxy)
sub, err := SubscribeDepositTx(context.Background(), opSim.l1Chain.EthClient(), portalAddress, depositTxCh)
sub, err := SubscribeDepositTx(context.Background(), opSim.l1Chain.EthClient(), portalAddress, depositTxCh, l1DepositTxnLogCh)
Copy link
Contributor

@jakim929 jakim929 Nov 25, 2024

Choose a reason for hiding this comment

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

similar to the indexer/relayer design for l2 to l2 messages, it will be more maintainable to

have the indexer have a start function and run separately from the opsimulator. the op simulator then "listens" to the indexer to get the stream of deposit tx

all of the setting new logs logic should happen inside the deposit indexer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jakim929 just clarifying the new indexer will be subscribing to deposit tx and storing, while the opsim would just be used to start the L1ToL2Indexer service correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

while indexer listens for new tranction a transaction event should also be sent from indexer to opsim to SendTransaction to done while the indexer store ref to the initial depTxn sub message

Copy link
Contributor Author

Choose a reason for hiding this comment

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

or another way could be both opsim and indexer can both sub to deposit txn, the op sim executes transaction while the indexer just stores ref?

)

type L1DepositStore struct {
entryByHash map[common.Hash]*types.DepositTx
Copy link
Contributor

Choose a reason for hiding this comment

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

some high level thoughts

  1. entryByHash should be indexed by something that globally & uniquely identifies a depositTX. the l1 txhash is insufficient because
  2. you should prob maybe store more than just the deposit tx here since the indexer likely wants more info than that (ie. the log where this was emitted for instance)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

update:

  1. using the source hash as the key value, since it uniquely identifies the source of the deposit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. updated the message to contain both the depositTxn and the initiating log in the form of a struct

@@ -168,6 +174,9 @@ func (opSim *OpSimulator) startBackgroundTasks() {
}

opSim.log.Info("OptimismPortal#depositTransaction", "l2TxHash", depTx.Hash().String())
if err := opSim.l1DepositStoreManager.Set(l1Log.TxHash, dep); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

a single l1 transaction can have multiple deposit tx inside it so it cannot be used as the index. instead the deposit tx's own transaction hash might be a better one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

got it makes sense updating this

@s4rv4d s4rv4d marked this pull request as ready for review December 12, 2024 10:01
@s4rv4d
Copy link
Contributor Author

s4rv4d commented Dec 12, 2024

sharing a excalidraw explanation on how it works

Screenshot 2024-12-12 at 6 48 38 pm

@s4rv4d
Copy link
Contributor Author

s4rv4d commented Dec 12, 2024

HLD (high level design):

  1. each opsim will listen to its own l1ToL2Indexer for deposit events via the deposit contract via a sub key ("DepositMessageKey:destination:chainId")

  2. each indexer will filter the events for the specific deposit contract associated to each l2chain

  3. once an event comes in and it is a deposit event, we process the event

  4. processing contains to steps:

  • storing the event in via the store manager with source hash as key - since its a unique identifer for a deposit
  • publish the event so that the linked opsim can create a transaction

@s4rv4d
Copy link
Contributor Author

s4rv4d commented Dec 12, 2024

Responsibilities:

Indexer - store/update deposit txn in store
Opsim - listen to indexer and execute deposit txn

@jakim929
Copy link
Contributor

jakim929 commented Dec 23, 2024

Sorry for the delay here. This is looking pretty good!

I really like how opsim can subscribe to a subset of all events using the chainId using SubscribeDepositMessage.

A few suggestions for the high level architecture

  1. A single instance of the indexer ideally should handle all chains (not 1 chain per indexer).
  2. The indexer should be initialized at the orchestrator for ALL chains.
  3. The indexer instance is passed to the opsim, where it listens to events for a specific chain.

I think both the admin endpoint and the opsim should query the indexer (not the store manager).

This provides better separation of responsibility because the only one that is in charge of managing the lifecycle of the indexer, and opsim / admin only use it on a readonly basis. This is similar to the approach for the l2 to l2 indexer.

Will leave comments on specific lines for this!

Copy link
Contributor

@jakim929 jakim929 left a comment

Choose a reason for hiding this comment

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

Left a few high level comments about the design, please let me know if they require clarification!

Comment on lines +131 to +133
if err := opSim.indexer.Start(ctx, opSim.l1Chain.EthClient(), opSim.Chain); err != nil {
return fmt.Errorf("L1ToL2Indexer failed to start: %w", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Orchestrator should start the indexer so remove logic here

}
}

func (i *L1ToL2MessageIndexer) Start(ctx context.Context, client *ethclient.Client, l2Chain config.Chain) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

should accept an array of l2 chains - maybe this method can be come a private helper method startForChain?

and the new Start can loop through the array of chains and call this. Just a suggestion - many ways to do this


i.eb.Publish(depositMessageInfoKey(chainID), depTx)
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We should add a test at the indexer level in indexer_test.go. it should confirm that sending a tx gets correctly picked up, and the subscribe methods work correctly.

Comment on lines +25 to +28
type DepositChannels struct {
DepositTxCh chan<- *types.DepositTx
LogCh chan<- types.Log
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We should ideally just remove this file altogether, and in the indexer logic just subscribe to the log.

The indexer can subscribe to the log, then manually convert it to a deposit tx.

}
}

storeEntry, err := m.l1DepositStore.Get(*args)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should query the indexer not the store directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants