legacy_bridge_processor.go 18.2 KB
Newer Older
Hamdi Allam's avatar
Hamdi Allam committed
1 2 3 4 5 6
package bridge

import (
	"fmt"
	"math/big"

Hamdi Allam's avatar
Hamdi Allam committed
7
	"github.com/ethereum/go-ethereum/common"
Hamdi Allam's avatar
Hamdi Allam committed
8 9
	"github.com/ethereum/go-ethereum/log"

10
	"github.com/ethereum-optimism/optimism/indexer/bigint"
Hamdi Allam's avatar
Hamdi Allam committed
11 12
	"github.com/ethereum-optimism/optimism/indexer/config"
	"github.com/ethereum-optimism/optimism/indexer/database"
13
	"github.com/ethereum-optimism/optimism/indexer/processors/bridge/ovm1"
Hamdi Allam's avatar
Hamdi Allam committed
14
	"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
15

16 17
	"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
	"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
Hamdi Allam's avatar
Hamdi Allam committed
18 19 20 21
)

// Legacy Bridge Initiation

22
// LegacyL1ProcessInitiatedBridgeEvents will query the data for bridge events within the specified block range
23 24 25 26
// according the pre-bedrock protocol. This follows:
//  1. CanonicalTransactionChain
//  2. L1CrossDomainMessenger
//  3. L1StandardBridge
Hamdi Allam's avatar
Hamdi Allam committed
27
func LegacyL1ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
Hamdi Allam's avatar
Hamdi Allam committed
28
	// (1) CanonicalTransactionChain
29
	ctcTxDepositEvents, err := contracts.LegacyCTCDepositEvents(l1Contracts.LegacyCanonicalTransactionChain, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
30 31 32
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
33 34 35
	if len(ctcTxDepositEvents) > 0 {
		log.Info("detected legacy transaction deposits", "size", len(ctcTxDepositEvents))
	}
Hamdi Allam's avatar
Hamdi Allam committed
36

37
	mintedWEI := bigint.Zero
Hamdi Allam's avatar
Hamdi Allam committed
38 39 40 41 42
	ctcTxDeposits := make(map[logKey]*contracts.LegacyCTCDepositEvent, len(ctcTxDepositEvents))
	transactionDeposits := make([]database.L1TransactionDeposit, len(ctcTxDepositEvents))
	for i := range ctcTxDepositEvents {
		deposit := ctcTxDepositEvents[i]
		ctcTxDeposits[logKey{deposit.Event.BlockHash, deposit.Event.LogIndex}] = &deposit
43
		mintedWEI = new(big.Int).Add(mintedWEI, deposit.Tx.Amount)
44

Hamdi Allam's avatar
Hamdi Allam committed
45
		// We re-use the L2 Transaction hash as the source hash to remain consistent in the schema.
Hamdi Allam's avatar
Hamdi Allam committed
46
		transactionDeposits[i] = database.L1TransactionDeposit{
47 48
			SourceHash:           deposit.TxHash,
			L2TransactionHash:    deposit.TxHash,
Hamdi Allam's avatar
Hamdi Allam committed
49 50 51 52 53 54 55 56 57
			InitiatedL1EventGUID: deposit.Event.GUID,
			GasLimit:             deposit.GasLimit,
			Tx:                   deposit.Tx,
		}
	}
	if len(ctcTxDepositEvents) > 0 {
		if err := db.BridgeTransactions.StoreL1TransactionDeposits(transactionDeposits); err != nil {
			return err
		}
58 59

		mintedETH, _ := bigint.WeiToETH(mintedWEI).Float64()
60
		metrics.RecordL1TransactionDeposits(len(transactionDeposits), mintedETH)
Hamdi Allam's avatar
Hamdi Allam committed
61 62 63
	}

	// (2) L1CrossDomainMessenger
64
	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
65 66 67
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
68 69 70 71
	if len(crossDomainSentMessages) > 0 {
		log.Info("detected legacy sent messages", "size", len(crossDomainSentMessages))
	}

Hamdi Allam's avatar
Hamdi Allam committed
72
	sentMessages := make(map[logKey]*contracts.CrossDomainMessengerSentMessageEvent, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
73
	bridgeMessages := make([]database.L1BridgeMessage, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
74 75 76 77 78 79 80
	for i := range crossDomainSentMessages {
		sentMessage := crossDomainSentMessages[i]
		sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = &sentMessage

		// extract the deposit hash from the previous TransactionDepositedEvent
		ctcTxDeposit, ok := ctcTxDeposits[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex - 1}]
		if !ok {
Hamdi Allam's avatar
Hamdi Allam committed
81
			return fmt.Errorf("expected TransactionEnqueued preceding SentMessage event. tx_hash = %s", sentMessage.Event.TransactionHash)
82
		} else if ctcTxDeposit.Event.TransactionHash != sentMessage.Event.TransactionHash {
Hamdi Allam's avatar
Hamdi Allam committed
83
			return fmt.Errorf("correlated events tx hash mismatch. deposit_tx_hash = %s, message_tx_hash = %s", ctcTxDeposit.Event.TransactionHash, sentMessage.Event.TransactionHash)
Hamdi Allam's avatar
Hamdi Allam committed
84 85
		}

Hamdi Allam's avatar
Hamdi Allam committed
86
		bridgeMessages[i] = database.L1BridgeMessage{TransactionSourceHash: ctcTxDeposit.TxHash, BridgeMessage: sentMessage.BridgeMessage}
Hamdi Allam's avatar
Hamdi Allam committed
87
	}
Hamdi Allam's avatar
Hamdi Allam committed
88 89
	if len(bridgeMessages) > 0 {
		if err := db.BridgeMessages.StoreL1BridgeMessages(bridgeMessages); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
90 91
			return err
		}
Hamdi Allam's avatar
Hamdi Allam committed
92
		metrics.RecordL1CrossDomainSentMessages(len(bridgeMessages))
Hamdi Allam's avatar
Hamdi Allam committed
93 94 95
	}

	// (3) L1StandardBridge
96
	initiatedBridges, err := contracts.L1StandardBridgeLegacyDepositInitiatedEvents(l1Contracts.L1StandardBridgeProxy, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
97 98 99
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
100 101 102 103
	if len(initiatedBridges) > 0 {
		log.Info("detected iegacy bridge deposits", "size", len(initiatedBridges))
	}

Hamdi Allam's avatar
Hamdi Allam committed
104 105
	bridgedTokens := make(map[common.Address]int)
	bridgeDeposits := make([]database.L1BridgeDeposit, len(initiatedBridges))
Hamdi Allam's avatar
Hamdi Allam committed
106 107 108 109 110 111 112 113
	for i := range initiatedBridges {
		initiatedBridge := initiatedBridges[i]

		// extract the cross domain message hash & deposit source hash from the following events
		// Unlike bedrock, the bridge events are emitted AFTER sending the cross domain message
		// 	- Event Flow: TransactionEnqueued -> SentMessage -> DepositInitiated
		sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 1}]
		if !ok {
Hamdi Allam's avatar
Hamdi Allam committed
114
			return fmt.Errorf("expected SentMessage preceding DepositInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
115
		} else if sentMessage.Event.TransactionHash != initiatedBridge.Event.TransactionHash {
Hamdi Allam's avatar
Hamdi Allam committed
116
			return fmt.Errorf("correlated events tx hash mismatch. bridge_tx_hash = %s, message_tx_hash = %s", initiatedBridge.Event.TransactionHash, sentMessage.Event.TransactionHash)
Hamdi Allam's avatar
Hamdi Allam committed
117
		}
118

Hamdi Allam's avatar
Hamdi Allam committed
119 120
		ctcTxDeposit, ok := ctcTxDeposits[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 2}]
		if !ok {
Hamdi Allam's avatar
Hamdi Allam committed
121
			return fmt.Errorf("expected TransactionEnqueued preceding BridgeInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
122
		} else if ctcTxDeposit.Event.TransactionHash != initiatedBridge.Event.TransactionHash {
Hamdi Allam's avatar
Hamdi Allam committed
123
			return fmt.Errorf("correlated events tx hash mismatch. bridge_tx_hash = %s, deposit_tx_hash = %s", initiatedBridge.Event.TransactionHash, ctcTxDeposit.Event.TransactionHash)
Hamdi Allam's avatar
Hamdi Allam committed
124 125 126
		}

		initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
Hamdi Allam's avatar
Hamdi Allam committed
127 128
		bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
		bridgeDeposits[i] = database.L1BridgeDeposit{
Hamdi Allam's avatar
Hamdi Allam committed
129 130 131 132
			TransactionSourceHash: ctcTxDeposit.TxHash,
			BridgeTransfer:        initiatedBridge.BridgeTransfer,
		}
	}
Hamdi Allam's avatar
Hamdi Allam committed
133 134
	if len(bridgeDeposits) > 0 {
		if err := db.BridgeTransfers.StoreL1BridgeDeposits(bridgeDeposits); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
135 136
			return err
		}
Hamdi Allam's avatar
Hamdi Allam committed
137 138 139
		for tokenAddr, size := range bridgedTokens {
			metrics.RecordL1InitiatedBridgeTransfers(tokenAddr, size)
		}
Hamdi Allam's avatar
Hamdi Allam committed
140 141 142 143 144 145
	}

	// a-ok!
	return nil
}

146
// LegacyL2ProcessInitiatedBridgeEvents will query the data for bridge events within the specified block range
147
// according the pre-bedrock protocol. This follows:
148
//  1. L2CrossDomainMessenger - The LegacyMessagePasser contract cannot be used as entrypoint to bridge transactions from L2. The protocol
149 150
//     only allows the L2CrossDomainMessenger as the sole sender when relaying a bridged message.
//  2. L2StandardBridge
151
func LegacyL2ProcessInitiatedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, preset int, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
152
	// (1) L2CrossDomainMessenger
153
	crossDomainSentMessages, err := contracts.CrossDomainMessengerSentMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
154 155 156
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
157 158 159 160
	if len(crossDomainSentMessages) > 0 {
		log.Info("detected legacy transaction withdrawals (via L2CrossDomainMessenger)", "size", len(crossDomainSentMessages))
	}

161 162 163 164 165
	type sentMessageEvent struct {
		*contracts.CrossDomainMessengerSentMessageEvent
		WithdrawalHash common.Hash
	}

166
	withdrawnWEI := bigint.Zero
167
	sentMessages := make(map[logKey]sentMessageEvent, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
168
	bridgeMessages := make([]database.L2BridgeMessage, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
169
	versionedMessageHashes := make([]database.L2BridgeMessageVersionedMessageHash, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
170
	transactionWithdrawals := make([]database.L2TransactionWithdrawal, len(crossDomainSentMessages))
Hamdi Allam's avatar
Hamdi Allam committed
171 172
	for i := range crossDomainSentMessages {
		sentMessage := crossDomainSentMessages[i]
173
		withdrawnWEI = new(big.Int).Add(withdrawnWEI, sentMessage.BridgeMessage.Tx.Amount)
Hamdi Allam's avatar
Hamdi Allam committed
174

175 176
		// Since these message can be relayed in bedrock, we utilize the migrated withdrawal hash
		// and also store the v1 version of the message hash such that the bedrock l1 finalization
Hamdi Allam's avatar
Hamdi Allam committed
177 178 179
		// processor works as expected. There will be some entries relayed pre-bedrock that will not
		// require the entry but we'll store it anyways as a large % of these withdrawals are not relayed
		// pre-bedrock.
180

181
		v1MessageHash, err := LegacyBridgeMessageV1MessageHash(&sentMessage.BridgeMessage)
182 183 184 185
		if err != nil {
			return fmt.Errorf("failed to compute versioned message hash: %w", err)
		}

186
		withdrawalHash, err := LegacyBridgeMessageWithdrawalHash(preset, &sentMessage.BridgeMessage)
187 188 189 190
		if err != nil {
			return fmt.Errorf("failed to construct migrated withdrawal hash: %w", err)
		}

Hamdi Allam's avatar
Hamdi Allam committed
191
		transactionWithdrawals[i] = database.L2TransactionWithdrawal{
192
			WithdrawalHash:       withdrawalHash,
Hamdi Allam's avatar
Hamdi Allam committed
193 194 195 196 197 198
			InitiatedL2EventGUID: sentMessage.Event.GUID,
			Nonce:                sentMessage.BridgeMessage.Nonce,
			GasLimit:             sentMessage.BridgeMessage.GasLimit,
			Tx: database.Transaction{
				FromAddress: sentMessage.BridgeMessage.Tx.FromAddress,
				ToAddress:   sentMessage.BridgeMessage.Tx.ToAddress,
199
				Amount:      bigint.Zero,
Hamdi Allam's avatar
Hamdi Allam committed
200 201 202 203
				Data:        sentMessage.BridgeMessage.Tx.Data,
				Timestamp:   sentMessage.Event.Timestamp,
			},
		}
204

205
		sentMessages[logKey{sentMessage.Event.BlockHash, sentMessage.Event.LogIndex}] = sentMessageEvent{&sentMessage, withdrawalHash}
Hamdi Allam's avatar
Hamdi Allam committed
206
		bridgeMessages[i] = database.L2BridgeMessage{TransactionWithdrawalHash: withdrawalHash, BridgeMessage: sentMessage.BridgeMessage}
Hamdi Allam's avatar
Hamdi Allam committed
207
		versionedMessageHashes[i] = database.L2BridgeMessageVersionedMessageHash{MessageHash: sentMessage.BridgeMessage.MessageHash, V1MessageHash: v1MessageHash}
Hamdi Allam's avatar
Hamdi Allam committed
208
	}
Hamdi Allam's avatar
Hamdi Allam committed
209 210
	if len(bridgeMessages) > 0 {
		if err := db.BridgeTransactions.StoreL2TransactionWithdrawals(transactionWithdrawals); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
211 212
			return err
		}
Hamdi Allam's avatar
Hamdi Allam committed
213
		if err := db.BridgeMessages.StoreL2BridgeMessages(bridgeMessages); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
214 215
			return err
		}
Hamdi Allam's avatar
Hamdi Allam committed
216 217 218
		if err := db.BridgeMessages.StoreL2BridgeMessageV1MessageHashes(versionedMessageHashes); err != nil {
			return err
		}
219 220

		withdrawnETH, _ := bigint.WeiToETH(withdrawnWEI).Float64()
221
		metrics.RecordL2TransactionWithdrawals(len(transactionWithdrawals), withdrawnETH)
Hamdi Allam's avatar
Hamdi Allam committed
222
		metrics.RecordL2CrossDomainSentMessages(len(bridgeMessages))
Hamdi Allam's avatar
Hamdi Allam committed
223 224 225
	}

	// (2) L2StandardBridge
226
	initiatedBridges, err := contracts.L2StandardBridgeLegacyWithdrawalInitiatedEvents(l2Contracts.L2StandardBridge, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
227 228 229
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
230 231 232 233
	if len(initiatedBridges) > 0 {
		log.Info("detected legacy bridge withdrawals", "size", len(initiatedBridges))
	}

Hamdi Allam's avatar
Hamdi Allam committed
234
	bridgedTokens := make(map[common.Address]int)
Hamdi Allam's avatar
Hamdi Allam committed
235 236 237 238 239 240 241 242 243
	l2BridgeWithdrawals := make([]database.L2BridgeWithdrawal, len(initiatedBridges))
	for i := range initiatedBridges {
		initiatedBridge := initiatedBridges[i]

		// extract the cross domain message hash & deposit source hash from the following events
		// Unlike bedrock, the bridge events are emitted AFTER sending the cross domain message
		// 	- Event Flow: TransactionEnqueued -> SentMessage -> DepositInitiated
		sentMessage, ok := sentMessages[logKey{initiatedBridge.Event.BlockHash, initiatedBridge.Event.LogIndex - 1}]
		if !ok {
244 245
			return fmt.Errorf("expected SentMessage preceding BridgeInitiated event. tx_hash = %s", initiatedBridge.Event.TransactionHash)
		} else if sentMessage.Event.TransactionHash != initiatedBridge.Event.TransactionHash {
Hamdi Allam's avatar
Hamdi Allam committed
246
			return fmt.Errorf("correlated events tx hash mismatch. bridge_tx_hash = %s, message_tx_hash = %s", initiatedBridge.Event.TransactionHash, sentMessage.Event.TransactionHash)
Hamdi Allam's avatar
Hamdi Allam committed
247
		}
Hamdi Allam's avatar
Hamdi Allam committed
248 249

		bridgedTokens[initiatedBridge.BridgeTransfer.TokenPair.LocalTokenAddress]++
250
		initiatedBridge.BridgeTransfer.CrossDomainMessageHash = &sentMessage.BridgeMessage.MessageHash
Hamdi Allam's avatar
Hamdi Allam committed
251
		l2BridgeWithdrawals[i] = database.L2BridgeWithdrawal{
252
			TransactionWithdrawalHash: sentMessage.WithdrawalHash,
Hamdi Allam's avatar
Hamdi Allam committed
253 254 255 256 257 258 259
			BridgeTransfer:            initiatedBridge.BridgeTransfer,
		}
	}
	if len(l2BridgeWithdrawals) > 0 {
		if err := db.BridgeTransfers.StoreL2BridgeWithdrawals(l2BridgeWithdrawals); err != nil {
			return err
		}
Hamdi Allam's avatar
Hamdi Allam committed
260 261 262
		for tokenAddr, size := range bridgedTokens {
			metrics.RecordL2InitiatedBridgeTransfers(tokenAddr, size)
		}
Hamdi Allam's avatar
Hamdi Allam committed
263 264 265 266 267 268 269 270
	}

	// a-ok
	return nil
}

// Legacy Bridge Finalization

271
// LegacyL1ProcessFinalizedBridgeEvents will query for bridge events within the specified block range
Hamdi Allam's avatar
Hamdi Allam committed
272
// according to the pre-bedrock protocol. This follows:
273 274
//  1. L1CrossDomainMessenger
//  2. L1StandardBridge
275
func LegacyL1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L1Metricer, l1Contracts config.L1Contracts, fromHeight, toHeight *big.Int) error {
Hamdi Allam's avatar
Hamdi Allam committed
276 277
	// (1) L1CrossDomainMessenger -> This is the root-most contract from which bridge events are finalized since withdrawals must be initiated from the
	// L2CrossDomainMessenger. Since there's no two-step withdrawal process, we mark the transaction as proven/finalized in the same step
278
	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l1", l1Contracts.L1CrossDomainMessengerProxy, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
279 280 281
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
282 283 284
	if len(crossDomainRelayedMessages) > 0 {
		log.Info("detected relayed messages", "size", len(crossDomainRelayedMessages))
	}
Hamdi Allam's avatar
Hamdi Allam committed
285

286
	skippedOVM1Messages := 0
Hamdi Allam's avatar
Hamdi Allam committed
287 288 289 290 291 292
	for i := range crossDomainRelayedMessages {
		relayedMessage := crossDomainRelayedMessages[i]
		message, err := db.BridgeMessages.L2BridgeMessage(relayedMessage.MessageHash)
		if err != nil {
			return err
		} else if message == nil {
293 294
			if _, ok := ovm1.L1RelayedMessages[relayedMessage.MessageHash]; ok {
				skippedOVM1Messages++
Hamdi Allam's avatar
Hamdi Allam committed
295 296
				continue
			}
297 298

			return fmt.Errorf("missing indexed L2CrossDomainMessenger message! tx_hash %s", relayedMessage.Event.TransactionHash.String())
Hamdi Allam's avatar
Hamdi Allam committed
299 300
		}

301 302 303 304 305 306
		if err := db.BridgeMessages.MarkRelayedL2BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
			return fmt.Errorf("failed to relay cross domain message. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
		}

		// Mark the associated tx withdrawal as proven/finalized with the same event.
		if err := db.BridgeTransactions.MarkL2TransactionWithdrawalProvenEvent(message.TransactionWithdrawalHash, relayedMessage.Event.GUID); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
307
			return fmt.Errorf("failed to mark withdrawal as proven. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
Hamdi Allam's avatar
Hamdi Allam committed
308
		}
309
		if err := db.BridgeTransactions.MarkL2TransactionWithdrawalFinalizedEvent(message.TransactionWithdrawalHash, relayedMessage.Event.GUID, true); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
310
			return fmt.Errorf("failed to mark withdrawal as finalized. tx_hash = %s: %w", relayedMessage.Event.TransactionHash, err)
Hamdi Allam's avatar
Hamdi Allam committed
311 312
		}
	}
Hamdi Allam's avatar
Hamdi Allam committed
313 314
	if len(crossDomainRelayedMessages) > 0 {
		metrics.RecordL1CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
315 316 317 318
		if skippedOVM1Messages > 0 { // Logged as a warning just for visibility
			metrics.RecordL1SkippedOVM1CrossDomainRelayedMessages(skippedOVM1Messages)
			log.Info("skipped OVM 1.0 relayed L2CrossDomainMessenger withdrawals", "size", skippedOVM1Messages)
		}
Hamdi Allam's avatar
Hamdi Allam committed
319 320
	}

321 322 323 324 325 326
	// (2) L1StandardBridge
	// 	- Nothing actionable on the database. Since the StandardBridge is layered ontop of the
	// CrossDomainMessenger, there's no need for any sanity or invariant checks as the previous step
	// ensures a relayed message (finalized bridge) can be linked with a sent message (initiated bridge).

	//  - NOTE: Ignoring metrics for pre-bedrock transfers
Hamdi Allam's avatar
Hamdi Allam committed
327 328 329 330 331

	// a-ok!
	return nil
}

332
// LegacyL2ProcessFinalizedBridgeEvents will query for bridge events within the specified block range
Hamdi Allam's avatar
Hamdi Allam committed
333
// according to the pre-bedrock protocol. This follows:
334 335
//  1. L2CrossDomainMessenger
//  2. L2StandardBridge
Hamdi Allam's avatar
Hamdi Allam committed
336
func LegacyL2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, metrics L2Metricer, l2Contracts config.L2Contracts, fromHeight, toHeight *big.Int) error {
Hamdi Allam's avatar
Hamdi Allam committed
337
	// (1) L2CrossDomainMessenger
338
	crossDomainRelayedMessages, err := contracts.CrossDomainMessengerRelayedMessageEvents("l2", l2Contracts.L2CrossDomainMessenger, db, fromHeight, toHeight)
Hamdi Allam's avatar
Hamdi Allam committed
339 340 341
	if err != nil {
		return err
	}
Hamdi Allam's avatar
Hamdi Allam committed
342 343 344
	if len(crossDomainRelayedMessages) > 0 {
		log.Info("detected relayed legacy messages", "size", len(crossDomainRelayedMessages))
	}
Hamdi Allam's avatar
Hamdi Allam committed
345 346 347 348 349 350 351

	for i := range crossDomainRelayedMessages {
		relayedMessage := crossDomainRelayedMessages[i]
		message, err := db.BridgeMessages.L1BridgeMessage(relayedMessage.MessageHash)
		if err != nil {
			return err
		} else if message == nil {
Hamdi Allam's avatar
Hamdi Allam committed
352
			return fmt.Errorf("missing indexed L1CrossDomainMessager message! tx_hash = %s", relayedMessage.Event.TransactionHash)
Hamdi Allam's avatar
Hamdi Allam committed
353 354 355
		}

		if err := db.BridgeMessages.MarkRelayedL1BridgeMessage(relayedMessage.MessageHash, relayedMessage.Event.GUID); err != nil {
Hamdi Allam's avatar
Hamdi Allam committed
356
			return fmt.Errorf("failed to relay cross domain message: %w", err)
Hamdi Allam's avatar
Hamdi Allam committed
357 358
		}
	}
Hamdi Allam's avatar
Hamdi Allam committed
359 360 361
	if len(crossDomainRelayedMessages) > 0 {
		metrics.RecordL2CrossDomainRelayedMessages(len(crossDomainRelayedMessages))
	}
Hamdi Allam's avatar
Hamdi Allam committed
362

363 364 365 366 367 368
	// (2) L2StandardBridge
	// 	- Nothing actionable on the database. Since the StandardBridge is layered ontop of the
	// CrossDomainMessenger, there's no need for any sanity or invariant checks as the previous step
	// ensures a relayed message (finalized bridge) can be linked with a sent message (initiated bridge).

	//  - NOTE: Ignoring metrics for pre-bedrock transfers
Hamdi Allam's avatar
Hamdi Allam committed
369 370 371 372

	// a-ok!
	return nil
}
373 374 375

// Utils

376
func LegacyBridgeMessageWithdrawalHash(preset int, msg *database.BridgeMessage) (common.Hash, error) {
377 378 379 380 381 382 383 384 385 386
	l1Cdm := config.Presets[preset].ChainConfig.L1Contracts.L1CrossDomainMessengerProxy
	legacyWithdrawal := crossdomain.NewLegacyWithdrawal(predeploys.L2CrossDomainMessengerAddr, msg.Tx.ToAddress, msg.Tx.FromAddress, msg.Tx.Data, msg.Nonce)
	migratedWithdrawal, err := crossdomain.MigrateWithdrawal(legacyWithdrawal, &l1Cdm, big.NewInt(int64(preset)))
	if err != nil {
		return common.Hash{}, err
	}

	return migratedWithdrawal.Hash()
}

387
func LegacyBridgeMessageV1MessageHash(msg *database.BridgeMessage) (common.Hash, error) {
388 389 390 391 392 393 394 395 396
	legacyWithdrawal := crossdomain.NewLegacyWithdrawal(predeploys.L2CrossDomainMessengerAddr, msg.Tx.ToAddress, msg.Tx.FromAddress, msg.Tx.Data, msg.Nonce)
	value, err := legacyWithdrawal.Value()
	if err != nil {
		return common.Hash{}, fmt.Errorf("failed to extract ETH value from legacy bridge message: %w", err)
	}

	// Note: GasLimit is always zero. Only the GasLimit for the withdrawal transaction was migrated
	return crossdomain.HashCrossDomainMessageV1(msg.Nonce, msg.Tx.FromAddress, msg.Tx.ToAddress, value, new(big.Int), msg.Tx.Data)
}