Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
e552edee
Unverified
Commit
e552edee
authored
Sep 06, 2023
by
mergify[bot]
Committed by
GitHub
Sep 06, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into aj/cannon-invalid-output-root
parents
fdfa5df3
eb1d3f50
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
299 additions
and
487 deletions
+299
-487
api.go
indexer/api/api.go
+84
-12
api_test.go
indexer/api/api_test.go
+13
-3
cli.go
indexer/cmd/indexer/cli.go
+2
-2
blocks.go
indexer/database/blocks.go
+1
-1
bridge_transactions.go
indexer/database/bridge_transactions.go
+56
-0
l1_etl.go
indexer/etl/l1_etl.go
+2
-2
indexer.toml
indexer/indexer.toml
+9
-5
bridge.go
indexer/processors/bridge.go
+39
-31
l1_bridge_processor.go
indexer/processors/bridge/l1_bridge_processor.go
+0
-78
l2_bridge_processor.go
indexer/processors/bridge/l2_bridge_processor.go
+0
-70
schema.prisma
indexer/ui/schema.prisma
+73
-74
pnpm-lock.yaml
pnpm-lock.yaml
+20
-209
No files found.
indexer/api/api.go
View file @
e552edee
...
...
@@ -4,42 +4,114 @@ import (
"context"
"fmt"
"net/http"
"runtime/debug"
"sync"
"github.com/ethereum-optimism/optimism/indexer/api/routes"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-service/httputil"
"github.com/ethereum-optimism/optimism/op-service/metrics"
"github.com/ethereum/go-ethereum/log"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/prometheus/client_golang/prometheus"
)
const
ethereumAddressRegex
=
`^0x[a-fA-F0-9]{40}$`
type
Api
struct
{
log
log
.
Logger
Router
*
chi
.
Mux
log
log
.
Logger
Router
*
chi
.
Mux
serverConfig
config
.
ServerConfig
metricsConfig
config
.
ServerConfig
metricsRegistry
*
prometheus
.
Registry
}
func
NewApi
(
logger
log
.
Logger
,
bv
database
.
BridgeTransfersView
)
*
Api
{
r
:=
chi
.
NewRouter
()
h
:=
routes
.
NewRoutes
(
logger
,
bv
,
r
)
const
(
MetricsNamespace
=
"op_indexer"
)
func
chiMetricsMiddleware
(
rec
metrics
.
HTTPRecorder
)
func
(
http
.
Handler
)
http
.
Handler
{
return
func
(
next
http
.
Handler
)
http
.
Handler
{
return
metrics
.
NewHTTPRecordingMiddleware
(
rec
,
next
)
}
}
func
NewApi
(
logger
log
.
Logger
,
bv
database
.
BridgeTransfersView
,
serverConfig
config
.
ServerConfig
,
metricsConfig
config
.
ServerConfig
)
*
Api
{
apiRouter
:=
chi
.
NewRouter
()
h
:=
routes
.
NewRoutes
(
logger
,
bv
,
apiRouter
)
mr
:=
metrics
.
NewRegistry
()
promRecorder
:=
metrics
.
NewPromHTTPRecorder
(
mr
,
MetricsNamespace
)
apiRouter
.
Use
(
chiMetricsMiddleware
(
promRecorder
))
apiRouter
.
Use
(
middleware
.
Recoverer
)
apiRouter
.
Use
(
middleware
.
Heartbeat
(
"/healthz"
))
apiRouter
.
Get
(
fmt
.
Sprintf
(
"/api/v0/deposits/{address:%s}"
,
ethereumAddressRegex
),
h
.
L1DepositsHandler
)
apiRouter
.
Get
(
fmt
.
Sprintf
(
"/api/v0/withdrawals/{address:%s}"
,
ethereumAddressRegex
),
h
.
L2WithdrawalsHandler
)
return
&
Api
{
log
:
logger
,
Router
:
apiRouter
,
metricsRegistry
:
mr
,
serverConfig
:
serverConfig
,
metricsConfig
:
metricsConfig
}
}
func
(
a
*
Api
)
Start
(
ctx
context
.
Context
)
error
{
var
wg
sync
.
WaitGroup
errCh
:=
make
(
chan
error
,
2
)
processCtx
,
processCancel
:=
context
.
WithCancel
(
ctx
)
runProcess
:=
func
(
start
func
(
ctx
context
.
Context
)
error
)
{
wg
.
Add
(
1
)
go
func
()
{
defer
func
()
{
if
err
:=
recover
();
err
!=
nil
{
a
.
log
.
Error
(
"halting api on panic"
,
"err"
,
err
)
debug
.
PrintStack
()
errCh
<-
fmt
.
Errorf
(
"panic: %v"
,
err
)
}
processCancel
()
wg
.
Done
()
}()
r
.
Use
(
middleware
.
Heartbeat
(
"/healthz"
))
errCh
<-
start
(
processCtx
)
}()
}
runProcess
(
a
.
startServer
)
runProcess
(
a
.
startMetricsServer
)
wg
.
Wait
()
err
:=
<-
errCh
if
err
!=
nil
{
a
.
log
.
Error
(
"api stopped"
,
"err"
,
err
)
}
else
{
a
.
log
.
Info
(
"api stopped"
)
}
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/deposits/{address:%s}"
,
ethereumAddressRegex
),
h
.
L1DepositsHandler
)
r
.
Get
(
fmt
.
Sprintf
(
"/api/v0/withdrawals/{address:%s}"
,
ethereumAddressRegex
),
h
.
L2WithdrawalsHandler
)
return
&
Api
{
log
:
logger
,
Router
:
r
}
return
err
}
func
(
a
*
Api
)
Listen
(
ctx
context
.
Context
,
port
in
t
)
error
{
a
.
log
.
Info
(
"api server listening..."
,
"port"
,
p
ort
)
server
:=
http
.
Server
{
Addr
:
fmt
.
Sprintf
(
":%d"
,
p
ort
),
Handler
:
a
.
Router
}
func
(
a
*
Api
)
startServer
(
ctx
context
.
Contex
t
)
error
{
a
.
log
.
Info
(
"api server listening..."
,
"port"
,
a
.
serverConfig
.
P
ort
)
server
:=
http
.
Server
{
Addr
:
fmt
.
Sprintf
(
":%d"
,
a
.
serverConfig
.
P
ort
),
Handler
:
a
.
Router
}
err
:=
httputil
.
ListenAndServeContext
(
ctx
,
&
server
)
if
err
!=
nil
{
a
.
log
.
Error
(
"api server stopped"
,
"err"
,
err
)
}
else
{
a
.
log
.
Info
(
"api server stopped"
)
}
return
err
}
func
(
a
*
Api
)
startMetricsServer
(
ctx
context
.
Context
)
error
{
a
.
log
.
Info
(
"starting metrics server..."
,
"port"
,
a
.
metricsConfig
.
Port
)
err
:=
metrics
.
ListenAndServe
(
ctx
,
a
.
metricsRegistry
,
a
.
metricsConfig
.
Host
,
a
.
metricsConfig
.
Port
)
if
err
!=
nil
{
a
.
log
.
Error
(
"metrics server stopped"
,
"err"
,
err
)
}
else
{
a
.
log
.
Info
(
"metrics server stopped"
)
}
return
err
}
indexer/api/api_test.go
View file @
e552edee
...
...
@@ -6,6 +6,7 @@ import (
"net/http/httptest"
"testing"
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -18,6 +19,15 @@ type MockBridgeTransfersView struct{}
var
mockAddress
=
"0x4204204204204204204204204204204204204204"
var
apiConfig
=
config
.
ServerConfig
{
Host
:
"localhost"
,
Port
:
8080
,
}
var
metricsConfig
=
config
.
ServerConfig
{
Host
:
"localhost"
,
Port
:
7300
,
}
var
(
deposit
=
database
.
L1BridgeDeposit
{
TransactionSourceHash
:
common
.
HexToHash
(
"abc"
),
...
...
@@ -77,7 +87,7 @@ func (mbv *MockBridgeTransfersView) L2BridgeWithdrawalsByAddress(address common.
}
func
TestHealthz
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{})
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
,
apiConfig
,
metricsConfig
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
"/healthz"
,
nil
)
assert
.
Nil
(
t
,
err
)
...
...
@@ -89,7 +99,7 @@ func TestHealthz(t *testing.T) {
func
TestL1BridgeDepositsHandler
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{})
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
,
apiConfig
,
metricsConfig
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/deposits/%s"
,
mockAddress
),
nil
)
assert
.
Nil
(
t
,
err
)
...
...
@@ -101,7 +111,7 @@ func TestL1BridgeDepositsHandler(t *testing.T) {
func
TestL2BridgeWithdrawalsByAddressHandler
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlInfo
)
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{})
api
:=
NewApi
(
logger
,
&
MockBridgeTransfersView
{}
,
apiConfig
,
metricsConfig
)
request
,
err
:=
http
.
NewRequest
(
"GET"
,
fmt
.
Sprintf
(
"/api/v0/withdrawals/%s"
,
mockAddress
),
nil
)
assert
.
Nil
(
t
,
err
)
...
...
indexer/cmd/indexer/cli.go
View file @
e552edee
...
...
@@ -62,8 +62,8 @@ func runApi(ctx *cli.Context) error {
}
defer
db
.
Close
()
api
:=
api
.
NewApi
(
log
,
db
.
BridgeTransfers
)
return
api
.
Listen
(
ctx
.
Context
,
cfg
.
HTTPServer
.
Por
t
)
api
:=
api
.
NewApi
(
log
,
db
.
BridgeTransfers
,
cfg
.
HTTPServer
,
cfg
.
MetricsServer
)
return
api
.
Start
(
ctx
.
Contex
t
)
}
func
runAll
(
ctx
*
cli
.
Context
)
error
{
...
...
indexer/database/blocks.go
View file @
e552edee
...
...
@@ -233,7 +233,7 @@ func (db *blocksDB) LatestEpoch() (*Epoch, error) {
// L2 for a faster query. Per the protocol, the L2 block that starts a new epoch
// will have a matching timestamp with the L1 origin.
query
:=
db
.
gorm
.
Table
(
"l1_block_headers"
)
.
Order
(
"l1_block_headers.timestamp DESC"
)
query
=
query
.
Joins
(
"INNER JOIN l2_block_headers ON l
1_block_headers.timestamp = l2
_block_headers.timestamp"
)
query
=
query
.
Joins
(
"INNER JOIN l2_block_headers ON l
2_block_headers.timestamp = l1
_block_headers.timestamp"
)
query
=
query
.
Select
(
"*"
)
var
epoch
Epoch
...
...
indexer/database/bridge_transactions.go
View file @
e552edee
...
...
@@ -47,7 +47,10 @@ type L2TransactionWithdrawal struct {
type
BridgeTransactionsView
interface
{
L1TransactionDeposit
(
common
.
Hash
)
(
*
L1TransactionDeposit
,
error
)
L1LatestBlockHeader
()
(
*
L1BlockHeader
,
error
)
L2TransactionWithdrawal
(
common
.
Hash
)
(
*
L2TransactionWithdrawal
,
error
)
L2LatestBlockHeader
()
(
*
L2BlockHeader
,
error
)
}
type
BridgeTransactionsDB
interface
{
...
...
@@ -94,6 +97,37 @@ func (db *bridgeTransactionsDB) L1TransactionDeposit(sourceHash common.Hash) (*L
return
&
deposit
,
nil
}
func
(
db
*
bridgeTransactionsDB
)
L1LatestBlockHeader
()
(
*
L1BlockHeader
,
error
)
{
// Markers for an indexed bridge event
// L1: Latest Transaction Deposit, Latest Proven/Finalized Withdrawal
l1DepositQuery
:=
db
.
gorm
.
Table
(
"l1_transaction_deposits"
)
.
Order
(
"l1_transaction_deposits.timestamp DESC"
)
.
Limit
(
1
)
l1DepositQuery
=
l1DepositQuery
.
Joins
(
"INNER JOIN l1_contract_events ON l1_contract_events.guid = l1_transaction_deposits.initiated_l1_event_guid"
)
l1DepositQuery
=
l1DepositQuery
.
Select
(
"l1_contract_events.*"
)
l1ProvenQuery
:=
db
.
gorm
.
Table
(
"l2_transaction_withdrawals"
)
l1ProvenQuery
=
l1ProvenQuery
.
Joins
(
"INNER JOIN l1_contract_events ON l1_contract_events.guid = l2_transaction_withdrawals.proven_l1_event_guid"
)
l1ProvenQuery
=
l1ProvenQuery
.
Order
(
"l1_contract_events.timestamp DESC"
)
.
Select
(
"l1_contract_events.*"
)
.
Limit
(
1
)
l1FinalizedQuery
:=
db
.
gorm
.
Table
(
"l2_transaction_withdrawals"
)
l1FinalizedQuery
=
l1FinalizedQuery
.
Joins
(
"INNER JOIN l1_contract_events ON l1_contract_events.guid = l2_transaction_withdrawals.proven_l1_event_guid"
)
l1FinalizedQuery
=
l1FinalizedQuery
.
Order
(
"l1_contract_events.timestamp DESC"
)
.
Select
(
"l1_contract_events.*"
)
.
Limit
(
1
)
l1Query
:=
db
.
gorm
.
Table
(
"((?) UNION (?) UNION (?)) AS latest_bridge_events"
,
l1DepositQuery
.
Limit
(
1
),
l1ProvenQuery
,
l1FinalizedQuery
)
l1Query
=
l1Query
.
Joins
(
"INNER JOIN l1_block_headers ON l1_block_headers.hash = latest_bridge_events.block_hash"
)
l1Query
=
l1Query
.
Order
(
"l1_block_headers.number DESC"
)
.
Select
(
"l1_block_headers.*"
)
var
l1Header
L1BlockHeader
result
:=
l1Query
.
Take
(
&
l1Header
)
if
result
.
Error
!=
nil
{
if
errors
.
Is
(
result
.
Error
,
gorm
.
ErrRecordNotFound
)
{
return
nil
,
nil
}
return
nil
,
result
.
Error
}
return
&
l1Header
,
nil
}
/**
* Transactions withdrawn from L2
*/
...
...
@@ -149,3 +183,25 @@ func (db *bridgeTransactionsDB) MarkL2TransactionWithdrawalFinalizedEvent(withdr
result
:=
db
.
gorm
.
Save
(
&
withdrawal
)
return
result
.
Error
}
func
(
db
*
bridgeTransactionsDB
)
L2LatestBlockHeader
()
(
*
L2BlockHeader
,
error
)
{
// L2: Inclusion of the latest deposit
l1DepositQuery
:=
db
.
gorm
.
Table
(
"l1_transaction_deposits"
)
.
Order
(
"l1_transaction_deposits.timestamp DESC"
)
l1DepositQuery
=
l1DepositQuery
.
Joins
(
"INNER JOIN l1_contract_events ON l1_contract_events.guid = l1_transaction_deposits.initiated_l1_event_guid"
)
l1DepositQuery
=
l1DepositQuery
.
Select
(
"l1_contract_events.*"
)
l2Query
:=
db
.
gorm
.
Table
(
"(?) AS l1_deposit_events"
,
l1DepositQuery
)
l2Query
=
l2Query
.
Joins
(
"INNER JOIN l2_block_headers ON l2_block_headers.timestamp = l1_deposit_events.timestamp"
)
l2Query
=
l2Query
.
Select
(
"l2_block_headers.*"
)
var
l2Header
L2BlockHeader
result
:=
l2Query
.
Take
(
&
l2Header
)
if
result
.
Error
!=
nil
{
if
errors
.
Is
(
result
.
Error
,
gorm
.
ErrRecordNotFound
)
{
return
nil
,
nil
}
return
nil
,
result
.
Error
}
return
&
l2Header
,
nil
}
indexer/etl/l1_etl.go
View file @
e552edee
...
...
@@ -44,7 +44,7 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
fromHeader
=
latestHeader
.
RLPHeader
.
Header
()
}
else
if
cfg
.
StartHeight
.
BitLen
()
>
0
{
log
.
Info
(
"no indexed state
in storage,
starting from supplied L1 height"
,
"height"
,
cfg
.
StartHeight
.
String
())
log
.
Info
(
"no indexed state starting from supplied L1 height"
,
"height"
,
cfg
.
StartHeight
.
String
())
header
,
err
:=
client
.
BlockHeaderByNumber
(
cfg
.
StartHeight
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not fetch starting block header: %w"
,
err
)
...
...
@@ -53,7 +53,7 @@ func NewL1ETL(cfg Config, log log.Logger, db *database.DB, metrics Metricer, cli
fromHeader
=
header
}
else
{
log
.
Info
(
"no indexed state
in storage, starting from L1
genesis"
)
log
.
Info
(
"no indexed state
, starting from
genesis"
)
}
// NOTE - The use of un-buffered channel here assumes that downstream consumers
...
...
indexer/indexer.toml
View file @
e552edee
...
...
@@ -22,11 +22,15 @@ l1-rpc = "${INDEXER_RPC_URL_L1}"
l2-rpc
=
"${INDEXER_RPC_URL_L2}"
[db]
host
=
"postgres"
port
=
5432
user
=
"db_username"
password
=
"db_password"
name
=
"db_name"
host
=
"$INDEXER_DB_HOST"
# this port may be problematic once we depoly
# the DATABASE_URL looks like this for previous services and didn't include a port
# DATABASE_URL="postgresql://${INDEXER_DB_USER}:${INDEXER_DB_PASS}@localhost/${INDEXER_DB_NAME}?host=${INDEXER_DB_HOST}"
# If not problematic delete these comments
port
=
$INDEXER_DB_PORT
user
=
"$INDEXER_DB_USER"
password
=
"$INDEXER_DB_PASS"
name
=
"$INDEXER_DB_NAME"
[http]
host
=
"127.0.0.1"
...
...
indexer/processors/bridge.go
View file @
e552edee
...
...
@@ -29,35 +29,32 @@ type BridgeProcessor struct {
func
NewBridgeProcessor
(
log
log
.
Logger
,
db
*
database
.
DB
,
l1Etl
*
etl
.
L1ETL
,
chainConfig
config
.
ChainConfig
)
(
*
BridgeProcessor
,
error
)
{
log
=
log
.
New
(
"processor"
,
"bridge"
)
latestL1Header
,
err
:=
bridge
.
L1LatestBridgeEventHeader
(
db
,
chainConfig
)
latestL1Header
,
err
:=
db
.
BridgeTransactions
.
L1LatestBlockHeader
(
)
if
err
!=
nil
{
return
nil
,
err
}
latestL2Header
,
err
:=
bridge
.
L2LatestBridgeEventHeader
(
db
)
latestL2Header
,
err
:=
db
.
BridgeTransactions
.
L2LatestBlockHeader
(
)
if
err
!=
nil
{
return
nil
,
err
}
// Since the bridge processor indexes events based on epochs, there's
// no scenario in which we have indexed L2 data with no L1 data.
//
// NOTE: Technically there is an exception if our bridging contracts are
// used to bridges native from L2 and an op-chain happens to launch where
// only L2 native bridge events have occurred. This is a rare situation now
// and it's worth the assertion as an integrity check. We can revisit this
// as more chains launch with primarily L2-native activity.
if
latestL1Header
==
nil
&&
latestL2Header
!=
nil
{
log
.
Error
(
"detected indexed L2 bridge activity with no indexed L1 state"
,
"l2_block_number"
,
latestL2Header
.
Number
)
return
nil
,
errors
.
New
(
"detected indexed L2 bridge activity with no indexed L1 state"
)
}
var
l1Header
,
l2Header
*
types
.
Header
if
latestL1Header
==
nil
&&
latestL2Header
==
nil
{
log
.
Info
(
"no indexed state, starting from genesis"
)
log
.
Info
(
"no indexed state, starting from
rollup
genesis"
)
}
else
{
log
.
Info
(
"detected the latest indexed state"
,
"l1_block_number"
,
latestL1Header
.
Number
,
"l2_block_number"
,
latestL2Header
.
Number
)
l1Height
,
l2Height
:=
big
.
NewInt
(
0
),
big
.
NewInt
(
0
)
if
latestL1Header
!=
nil
{
l1Height
=
latestL1Header
.
Number
l1Header
=
latestL1Header
.
RLPHeader
.
Header
()
}
if
latestL2Header
!=
nil
{
l2Height
=
latestL2Header
.
Number
l2Header
=
latestL2Header
.
RLPHeader
.
Header
()
}
log
.
Info
(
"detected latest indexed state"
,
"l1_block_number"
,
l1Height
,
"l2_block_number"
,
l2Height
)
}
return
&
BridgeProcessor
{
log
,
db
,
l1Etl
,
chainConfig
,
l
atestL1Header
,
latestL
2Header
},
nil
return
&
BridgeProcessor
{
log
,
db
,
l1Etl
,
chainConfig
,
l
1Header
,
l
2Header
},
nil
}
func
(
b
*
BridgeProcessor
)
Start
(
ctx
context
.
Context
)
error
{
...
...
@@ -83,29 +80,40 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
latestEpoch
,
err
:=
b
.
db
.
Blocks
.
LatestEpoch
()
if
err
!=
nil
{
return
err
}
if
latestEpoch
==
nil
{
if
b
.
LatestL1Header
!=
nil
{
// Once we have some state `latestEpoch` should never return nil.
b
.
log
.
Error
(
"started with indexed bridge state, but no latest epoch returned"
,
"latest_bridge_l1_block_number"
,
b
.
LatestL1Header
.
Number
)
return
errors
.
New
(
"started with indexed bridge state, but no blocks epochs returned"
)
}
else
{
b
.
log
.
Warn
(
"no indexed epochs. waiting..."
)
continue
}
else
if
latestEpoch
==
nil
{
if
b
.
LatestL1Header
!=
nil
||
b
.
LatestL2Header
!=
nil
{
// Once we have some indexed state `latestEpoch` can never return nil
b
.
log
.
Error
(
"bridge events indexed, but no indexed epoch returned"
,
"latest_bridge_l1_block_number"
,
b
.
LatestL1Header
.
Number
)
return
errors
.
New
(
"bridge events indexed, but no indexed epoch returned"
)
}
b
.
log
.
Warn
(
"no indexed epochs available. waiting..."
)
continue
}
// Integrity Checks
if
b
.
LatestL1Header
!=
nil
&&
latestEpoch
.
L1BlockHeader
.
Hash
==
b
.
LatestL1Header
.
Hash
()
{
// Marked as a warning since the bridge should always be processing at least 1 new epoch
b
.
log
.
Warn
(
"all available epochs indexed by the bridge"
,
"latest_epoch_number"
,
b
.
LatestL1Header
.
Number
)
b
.
log
.
Warn
(
"all available epochs indexed"
,
"latest_bridge_l1_block_number"
,
b
.
LatestL1Header
.
Number
)
continue
}
if
b
.
LatestL1Header
!=
nil
&&
latestEpoch
.
L1BlockHeader
.
Number
.
Cmp
(
b
.
LatestL1Header
.
Number
)
<=
0
{
b
.
log
.
Error
(
"non-increasing l1 block height observed"
,
"latest_bridge_l1_block_number"
,
b
.
LatestL1Header
.
Number
,
"latest_epoch_number"
,
latestEpoch
.
L1BlockHeader
.
Number
)
return
errors
.
New
(
"non-increasing l1 block heght observed"
)
}
if
b
.
LatestL2Header
!=
nil
&&
latestEpoch
.
L2BlockHeader
.
Number
.
Cmp
(
b
.
LatestL2Header
.
Number
)
<=
0
{
b
.
log
.
Error
(
"non-increasing l2 block height observed"
,
"latest_bridge_l2_block_number"
,
b
.
LatestL2Header
.
Number
,
"latest_epoch_number"
,
latestEpoch
.
L2BlockHeader
.
Number
)
return
errors
.
New
(
"non-increasing l2 block heght observed"
)
}
// Process Bridge Events
toL1Height
,
toL2Height
:=
latestEpoch
.
L1BlockHeader
.
Number
,
latestEpoch
.
L2BlockHeader
.
Number
fromL1Height
,
fromL2Height
:=
big
.
NewInt
(
0
),
big
.
NewInt
(
0
)
if
b
.
LatestL1Header
!=
nil
{
// `NewBridgeProcessor` ensures that LatestL2Header must not be nil if LatestL1Header is set
fromL1Height
=
new
(
big
.
Int
)
.
Add
(
b
.
LatestL1Header
.
Number
,
big
.
NewInt
(
1
))
}
if
b
.
LatestL2Header
!=
nil
{
fromL2Height
=
new
(
big
.
Int
)
.
Add
(
b
.
LatestL2Header
.
Number
,
big
.
NewInt
(
1
))
}
...
...
@@ -139,7 +147,7 @@ func (b *BridgeProcessor) Start(ctx context.Context) error {
// Try again on a subsequent interval
batchLog
.
Error
(
"unable to index new bridge events"
,
"err"
,
err
)
}
else
{
batchLog
.
Info
(
"done indexing
new
bridge events"
,
"latest_l1_block_number"
,
toL1Height
,
"latest_l2_block_number"
,
toL2Height
)
batchLog
.
Info
(
"done indexing bridge events"
,
"latest_l1_block_number"
,
toL1Height
,
"latest_l2_block_number"
,
toL2Height
)
b
.
LatestL1Header
=
latestEpoch
.
L1BlockHeader
.
RLPHeader
.
Header
()
b
.
LatestL2Header
=
latestEpoch
.
L2BlockHeader
.
RLPHeader
.
Header
()
}
...
...
indexer/processors/bridge/l1_bridge_processor.go
View file @
e552edee
...
...
@@ -8,7 +8,6 @@ import (
"github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
...
...
@@ -233,80 +232,3 @@ func L1ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, chainConfig
// a-ok!
return
nil
}
// L1LatestBridgeEventHeader returns the latest header for which and on-chain event
// has been observed on L1 -- Both initiated L1 events and finalization markers on L2.
func
L1LatestBridgeEventHeader
(
db
*
database
.
DB
,
chainConfig
config
.
ChainConfig
)
(
*
types
.
Header
,
error
)
{
portalAbi
,
err
:=
bindings
.
OptimismPortalMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
err
}
depositEventID
:=
portalAbi
.
Events
[
"TransactionDeposited"
]
.
ID
provenEventID
:=
portalAbi
.
Events
[
"WithdrawalProven"
]
.
ID
finalizedEventID
:=
portalAbi
.
Events
[
"WithdrawalFinalized"
]
.
ID
// (1) Initiated L1 Events
// Since all initaited bridge events eventually reach the OptimismPortal to
// conduct the deposit, we can simply look for the last deposited transaction
// event on L2.
var
latestDepositHeader
*
types
.
Header
contractEventFilter
:=
database
.
ContractEvent
{
ContractAddress
:
chainConfig
.
L1Contracts
.
OptimismPortalProxy
,
EventSignature
:
depositEventID
}
depositEvent
,
err
:=
db
.
ContractEvents
.
L1LatestContractEventWithFilter
(
contractEventFilter
)
if
err
!=
nil
{
return
nil
,
err
}
if
depositEvent
!=
nil
{
l1BlockHeader
,
err
:=
db
.
Blocks
.
L1BlockHeader
(
depositEvent
.
BlockHash
)
if
err
!=
nil
{
return
nil
,
err
}
if
l1BlockHeader
!=
nil
{
latestDepositHeader
=
l1BlockHeader
.
RLPHeader
.
Header
()
}
}
// (2) Finalization markers for L2
// Like initiated L1 events, all withdrawals must flow through the OptimismPortal
// contract. We must look for both proven and finalized withdrawal events.
var
latestWithdrawHeader
*
types
.
Header
contractEventFilter
.
EventSignature
=
finalizedEventID
withdrawEvent
,
err
:=
db
.
ContractEvents
.
L1LatestContractEventWithFilter
(
contractEventFilter
)
if
err
!=
nil
{
return
nil
,
err
}
if
withdrawEvent
!=
nil
{
// Check if a have a later detected proven event
contractEventFilter
.
EventSignature
=
provenEventID
provenEvent
,
err
:=
db
.
ContractEvents
.
L1LatestContractEventWithFilter
(
contractEventFilter
)
if
err
!=
nil
{
return
nil
,
err
}
if
provenEvent
!=
nil
&&
provenEvent
.
Timestamp
>
withdrawEvent
.
Timestamp
{
withdrawEvent
=
provenEvent
}
l1BlockHeader
,
err
:=
db
.
Blocks
.
L1BlockHeader
(
withdrawEvent
.
BlockHash
)
if
err
!=
nil
{
return
nil
,
err
}
latestWithdrawHeader
=
l1BlockHeader
.
RLPHeader
.
Header
()
}
if
latestDepositHeader
==
nil
{
// If there has been no seen deposits yet, there could have been no seen withdrawals
if
latestWithdrawHeader
!=
nil
{
return
nil
,
errors
.
New
(
"detected an indexed withdrawal without any deposits"
)
}
return
nil
,
nil
}
else
if
latestWithdrawHeader
==
nil
{
return
latestDepositHeader
,
nil
}
else
{
// both deposits & withdrawals have occurred
if
latestDepositHeader
.
Time
>
latestWithdrawHeader
.
Time
{
return
latestDepositHeader
,
nil
}
else
{
return
latestWithdrawHeader
,
nil
}
}
}
indexer/processors/bridge/l2_bridge_processor.go
View file @
e552edee
...
...
@@ -7,10 +7,8 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processors/contracts"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
...
...
@@ -182,71 +180,3 @@ func L2ProcessFinalizedBridgeEvents(log log.Logger, db *database.DB, fromHeight
// a-ok!
return
nil
}
// L2LatestBridgeEventHeader returns the latest header for which and on-chain event
// has been observed on L2 -- Both initiated L2 events and finalization markers from L1.
func
L2LatestBridgeEventHeader
(
db
*
database
.
DB
)
(
*
types
.
Header
,
error
)
{
l2ToL1MessagePasserAbi
,
err
:=
bindings
.
L2ToL1MessagePasserMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
err
}
crossDomainMessengerAbi
,
err
:=
bindings
.
CrossDomainMessengerMetaData
.
GetAbi
()
if
err
!=
nil
{
return
nil
,
err
}
messagePassedID
:=
l2ToL1MessagePasserAbi
.
Events
[
"MessagePassed"
]
.
ID
relayedEventID
:=
crossDomainMessengerAbi
.
Events
[
"RelayedMessage"
]
.
ID
// (1) Initiated L2 Events
// Since all initiated bridge events eventually reach the L2ToL1MessagePasser to
// initiate the withdrawal, we can simply look for the last message passed from
// this cont
var
latestWithdrawHeader
*
types
.
Header
contractEventFilter
:=
database
.
ContractEvent
{
ContractAddress
:
predeploys
.
L2ToL1MessagePasserAddr
,
EventSignature
:
messagePassedID
}
withdrawEvent
,
err
:=
db
.
ContractEvents
.
L2LatestContractEventWithFilter
(
contractEventFilter
)
if
err
!=
nil
{
return
nil
,
err
}
if
withdrawEvent
!=
nil
{
l2BlockHeader
,
err
:=
db
.
Blocks
.
L2BlockHeader
(
withdrawEvent
.
BlockHash
)
if
err
!=
nil
{
return
nil
,
err
}
if
l2BlockHeader
!=
nil
{
latestWithdrawHeader
=
l2BlockHeader
.
RLPHeader
.
Header
()
}
}
// (2) Finalization markers for L1
// Since deposited transactions from L1 are apart of the block derivation process,
// there are no native finalization markers for OptimismPortal#TransactionDeposited.
// The lowest layer to check for here is the CrossDomainMessenger#RelayedMessage event.
// This also converts the StandardBridge which simply is an extension of the messenger.
var
latestRelayedMessageHeader
*
types
.
Header
contractEventFilter
=
database
.
ContractEvent
{
ContractAddress
:
predeploys
.
L2CrossDomainMessengerAddr
,
EventSignature
:
relayedEventID
}
relayedEvent
,
err
:=
db
.
ContractEvents
.
L2LatestContractEventWithFilter
(
contractEventFilter
)
if
err
!=
nil
{
return
nil
,
err
}
if
relayedEvent
!=
nil
{
l2BlockHeader
,
err
:=
db
.
Blocks
.
L2BlockHeader
(
relayedEvent
.
BlockHash
)
if
err
!=
nil
{
return
nil
,
err
}
if
l2BlockHeader
!=
nil
{
latestRelayedMessageHeader
=
l2BlockHeader
.
RLPHeader
.
Header
()
}
}
// No causaal relationship between withdraw and relayed messages
if
latestWithdrawHeader
==
nil
||
latestRelayedMessageHeader
==
nil
{
return
nil
,
nil
}
else
{
if
latestWithdrawHeader
.
Time
>
latestRelayedMessageHeader
.
Time
{
return
latestWithdrawHeader
,
nil
}
else
{
return
latestRelayedMessageHeader
,
nil
}
}
}
indexer/ui/schema.prisma
View file @
e552edee
This diff is collapsed.
Click to expand it.
pnpm-lock.yaml
View file @
e552edee
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment