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
1078f86f
Unverified
Commit
1078f86f
authored
Feb 15, 2024
by
Adrian Sutton
Committed by
GitHub
Feb 15, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-challenger: Binary search for guaranteed safe block (#9538)
parent
830cdc64
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
194 additions
and
0 deletions
+194
-0
start.go
op-challenger/game/fault/trace/outputs/source/start.go
+48
-0
start_test.go
op-challenger/game/fault/trace/outputs/source/start_test.go
+146
-0
No files found.
op-challenger/game/fault/trace/outputs/source/start.go
0 → 100644
View file @
1078f86f
package
source
import
(
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
type
L2Source
interface
{
L2BlockRefByLabel
(
ctx
context
.
Context
,
label
eth
.
BlockLabel
)
(
eth
.
L2BlockRef
,
error
)
L2BlockRefByNumber
(
ctx
context
.
Context
,
num
uint64
)
(
eth
.
L2BlockRef
,
error
)
}
// FindGuaranteedSafeHead finds a L2 block where the L1 origin is the most recent L1 block closes to l1BlockNum
// where the block is guaranteed to now be safe because the sequencer window has expired.
// That is: block.origin.Number + sequencerWindowSize < l1BlockNum
// Note that the derivation rules guarantee that there is at least 1 L2 block for each L1 block.
// Otherwise deposits from the skipped L1 block would be missed.
func
FindGuaranteedSafeHead
(
ctx
context
.
Context
,
rollupCfg
*
rollup
.
Config
,
l1BlockNum
uint64
,
l2Client
L2Source
)
(
eth
.
BlockID
,
error
)
{
if
l1BlockNum
<=
rollupCfg
.
SeqWindowSize
{
// The sequencer window hasn't completed yet, so the only guaranteed safe block is L2 genesis
return
rollupCfg
.
Genesis
.
L2
,
nil
}
safeHead
,
err
:=
l2Client
.
L2BlockRefByLabel
(
ctx
,
eth
.
Safe
)
if
err
!=
nil
{
return
eth
.
BlockID
{},
fmt
.
Errorf
(
"failed to load local safe head: %w"
,
err
)
}
safeL1BlockNum
:=
l1BlockNum
-
rollupCfg
.
SeqWindowSize
-
1
start
:=
rollupCfg
.
Genesis
.
L2
.
Number
end
:=
safeHead
.
Number
for
start
<=
end
{
mid
:=
(
start
+
end
)
/
2
l2Block
,
err
:=
l2Client
.
L2BlockRefByNumber
(
ctx
,
mid
)
if
err
!=
nil
{
return
eth
.
BlockID
{},
fmt
.
Errorf
(
"failed to retrieve l2 block %v: %w"
,
mid
,
err
)
}
if
l2Block
.
L1Origin
.
Number
==
safeL1BlockNum
{
return
l2Block
.
ID
(),
nil
}
else
if
l2Block
.
L1Origin
.
Number
<
safeL1BlockNum
{
start
=
mid
+
1
}
else
{
end
=
mid
-
1
}
}
return
rollupCfg
.
Genesis
.
L2
,
nil
}
op-challenger/game/fault/trace/outputs/source/start_test.go
0 → 100644
View file @
1078f86f
package
source
import
(
"context"
"errors"
"math"
"testing"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func
TestFindGuaranteedSafeHead_ErrorWhenSafeHeadNotAvailable
(
t
*
testing
.
T
)
{
cfg
:=
&
rollup
.
Config
{
SeqWindowSize
:
100
,
Genesis
:
rollup
.
Genesis
{
L2
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1
},
Number
:
1343
,
},
},
}
expectedErr
:=
errors
.
New
(
"boom"
)
l2Source
:=
&
stubL2Source
{
byLabelError
:
expectedErr
}
_
,
err
:=
FindGuaranteedSafeHead
(
context
.
Background
(),
cfg
,
248249
,
l2Source
)
require
.
Error
(
t
,
err
)
}
func
TestFindGuaranteedSafeHead_L2GenesisWhenL1HeadNotPastSequenceWindow
(
t
*
testing
.
T
)
{
cfg
:=
&
rollup
.
Config
{
SeqWindowSize
:
100
,
Genesis
:
rollup
.
Genesis
{
L2
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1
},
Number
:
1343
,
},
},
}
l2Source
:=
&
stubL2Source
{}
actual
,
err
:=
FindGuaranteedSafeHead
(
context
.
Background
(),
cfg
,
99
,
l2Source
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
cfg
.
Genesis
.
L2
,
actual
)
}
func
TestFindGuaranteedSafeHead_L2GenesisWhenL1HeadEqualToSequenceWindow
(
t
*
testing
.
T
)
{
cfg
:=
&
rollup
.
Config
{
SeqWindowSize
:
100
,
Genesis
:
rollup
.
Genesis
{
L2
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1
},
Number
:
1343
,
},
},
}
l2Source
:=
&
stubL2Source
{}
actual
,
err
:=
FindGuaranteedSafeHead
(
context
.
Background
(),
cfg
,
100
,
l2Source
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
cfg
.
Genesis
.
L2
,
actual
)
}
func
TestFindGuaranteedSafeHead_SafeHeadIsGuaranteedSafe
(
t
*
testing
.
T
)
{
cfg
:=
&
rollup
.
Config
{
SeqWindowSize
:
100
,
Genesis
:
rollup
.
Genesis
{
L2
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1
},
Number
:
1343
,
},
},
}
safeHead
:=
eth
.
L2BlockRef
{
Hash
:
common
.
Hash
{
0xaa
},
Number
:
1000
,
L1Origin
:
eth
.
BlockID
{
Number
:
499
,
},
}
l2Source
:=
&
stubL2Source
{
safe
:
safeHead
,
}
actual
,
err
:=
FindGuaranteedSafeHead
(
context
.
Background
(),
cfg
,
500
,
l2Source
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
cfg
.
Genesis
.
L2
,
actual
)
}
func
TestFindGuaranteedSafeHead_SearchBackwardFromSafeHead
(
t
*
testing
.
T
)
{
cfg
:=
&
rollup
.
Config
{
SeqWindowSize
:
100
,
Genesis
:
rollup
.
Genesis
{
L2
:
eth
.
BlockID
{
Hash
:
common
.
Hash
{
0x1
},
Number
:
500
,
},
},
}
safeHead
:=
eth
.
L2BlockRef
{
Hash
:
common
.
Hash
{
0xaa
},
Number
:
1500
,
L1Origin
:
eth
.
BlockID
{
Number
:
5000
,
},
}
l2Source
:=
&
stubL2Source
{
safe
:
safeHead
,
blocks
:
make
(
map
[
uint64
]
eth
.
L2BlockRef
),
}
for
i
:=
cfg
.
Genesis
.
L2
.
Number
+
1
;
i
<
safeHead
.
Number
;
i
++
{
block
:=
eth
.
L2BlockRef
{
Hash
:
common
.
Hash
{
byte
(
i
)},
Number
:
i
,
L1Origin
:
eth
.
BlockID
{
Number
:
2000
+
i
,
// Make it different from L2 block number
},
}
l2Source
.
blocks
[
block
.
Number
]
=
block
}
expected
:=
l2Source
.
blocks
[
1260
]
actual
,
err
:=
FindGuaranteedSafeHead
(
context
.
Background
(),
cfg
,
expected
.
L1Origin
.
Number
+
cfg
.
SeqWindowSize
+
1
,
l2Source
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
.
ID
(),
actual
)
maxQueries
:=
int
(
math
.
Log2
(
float64
(
len
(
l2Source
.
blocks
)))
+
1
)
require
.
LessOrEqual
(
t
,
l2Source
.
byNumCount
,
maxQueries
,
"Should use an efficient search"
)
}
type
stubL2Source
struct
{
safe
eth
.
L2BlockRef
byLabelError
error
blocks
map
[
uint64
]
eth
.
L2BlockRef
byNumCount
int
}
func
(
s
*
stubL2Source
)
L2BlockRefByLabel
(
_
context
.
Context
,
_
eth
.
BlockLabel
)
(
eth
.
L2BlockRef
,
error
)
{
return
s
.
safe
,
s
.
byLabelError
}
func
(
s
*
stubL2Source
)
L2BlockRefByNumber
(
_
context
.
Context
,
blockNum
uint64
)
(
eth
.
L2BlockRef
,
error
)
{
s
.
byNumCount
++
ref
,
ok
:=
s
.
blocks
[
blockNum
]
if
!
ok
{
return
eth
.
L2BlockRef
{},
errors
.
New
(
"not found"
)
}
return
ref
,
nil
}
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