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
f5fc73db
Unverified
Commit
f5fc73db
authored
Jan 14, 2025
by
Adrian Sutton
Committed by
GitHub
Jan 14, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-program: Transition to invalid state when L1Head reached before claimed block (#13743)
parent
9b8b5281
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
133 additions
and
45 deletions
+133
-45
interop_test.go
op-e2e/actions/interop/interop_test.go
+44
-0
env.go
op-e2e/actions/proofs/helpers/env.go
+6
-0
interop.go
op-program/client/interop/interop.go
+82
-45
interop_test.go
op-program/client/interop/interop_test.go
+1
-0
No files found.
op-e2e/actions/interop/interop_test.go
View file @
f5fc73db
...
@@ -7,6 +7,7 @@ import (
...
@@ -7,6 +7,7 @@ import (
fpHelpers
"github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers"
fpHelpers
"github.com/ethereum-optimism/optimism/op-e2e/actions/proofs/helpers"
"github.com/ethereum-optimism/optimism/op-program/client/claim"
"github.com/ethereum-optimism/optimism/op-program/client/claim"
"github.com/ethereum-optimism/optimism/op-program/client/interop"
"github.com/ethereum-optimism/optimism/op-program/client/interop/types"
"github.com/ethereum-optimism/optimism/op-program/client/interop/types"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -351,6 +352,43 @@ func TestInteropFaultProofs(gt *testing.T) {
...
@@ -351,6 +352,43 @@ func TestInteropFaultProofs(gt *testing.T) {
expectValid
:
true
,
expectValid
:
true
,
skip
:
true
,
skip
:
true
,
},
},
{
name
:
"FirstChainReachesL1Head"
,
startTimestamp
:
startTimestamp
,
agreedClaim
:
start
.
Marshal
(),
disputedClaim
:
interop
.
InvalidTransition
,
// The derivation reaches the L1 head before the next block can be created
l1Head
:
actors
.
L1Miner
.
L1Chain
()
.
Genesis
()
.
Hash
(),
expectValid
:
true
,
},
{
name
:
"SecondChainReachesL1Head"
,
startTimestamp
:
startTimestamp
,
agreedClaim
:
step1Expected
,
disputedClaim
:
interop
.
InvalidTransition
,
// The derivation reaches the L1 head before the next block can be created
l1Head
:
actors
.
L1Miner
.
L1Chain
()
.
Genesis
()
.
Hash
(),
expectValid
:
true
,
},
{
name
:
"SuperRootInvalidIfUnsupportedByL1Data"
,
startTimestamp
:
startTimestamp
,
agreedClaim
:
step1Expected
,
disputedClaim
:
step2Expected
,
// The derivation reaches the L1 head before the next block can be created
l1Head
:
actors
.
L1Miner
.
L1Chain
()
.
Genesis
()
.
Hash
(),
expectValid
:
false
,
},
{
name
:
"FromInvalidTransitionHash"
,
startTimestamp
:
startTimestamp
,
agreedClaim
:
interop
.
InvalidTransition
,
disputedClaim
:
interop
.
InvalidTransition
,
// The derivation reaches the L1 head before the next block can be created
l1Head
:
actors
.
L1Miner
.
L1Chain
()
.
Genesis
()
.
Hash
(),
expectValid
:
true
,
},
}
}
for
_
,
test
:=
range
tests
{
for
_
,
test
:=
range
tests
{
...
@@ -366,12 +404,17 @@ func TestInteropFaultProofs(gt *testing.T) {
...
@@ -366,12 +404,17 @@ func TestInteropFaultProofs(gt *testing.T) {
if
!
test
.
expectValid
{
if
!
test
.
expectValid
{
checkResult
=
fpHelpers
.
ExpectError
(
claim
.
ErrClaimNotValid
)
checkResult
=
fpHelpers
.
ExpectError
(
claim
.
ErrClaimNotValid
)
}
}
l1Head
:=
test
.
l1Head
if
l1Head
==
(
common
.
Hash
{})
{
l1Head
=
actors
.
L1Miner
.
L1Chain
()
.
CurrentBlock
()
.
Hash
()
}
fpHelpers
.
RunFaultProofProgram
(
fpHelpers
.
RunFaultProofProgram
(
t
,
t
,
logger
,
logger
,
actors
.
L1Miner
,
actors
.
L1Miner
,
checkResult
,
checkResult
,
WithInteropEnabled
(
actors
,
test
.
agreedClaim
,
crypto
.
Keccak256Hash
(
test
.
disputedClaim
),
endTimestamp
),
WithInteropEnabled
(
actors
,
test
.
agreedClaim
,
crypto
.
Keccak256Hash
(
test
.
disputedClaim
),
endTimestamp
),
fpHelpers
.
WithL1Head
(
l1Head
),
)
)
})
})
}
}
...
@@ -404,6 +447,7 @@ type transitionTest struct {
...
@@ -404,6 +447,7 @@ type transitionTest struct {
startTimestamp
uint64
startTimestamp
uint64
agreedClaim
[]
byte
agreedClaim
[]
byte
disputedClaim
[]
byte
disputedClaim
[]
byte
l1Head
common
.
Hash
// Defaults to current L1 head if not set
expectValid
bool
expectValid
bool
skip
bool
skip
bool
}
}
op-e2e/actions/proofs/helpers/env.go
View file @
f5fc73db
...
@@ -157,6 +157,12 @@ func WithL2BlockNumber(num uint64) FixtureInputParam {
...
@@ -157,6 +157,12 @@ func WithL2BlockNumber(num uint64) FixtureInputParam {
}
}
}
}
func
WithL1Head
(
head
common
.
Hash
)
FixtureInputParam
{
return
func
(
f
*
FixtureInputs
)
{
f
.
L1Head
=
head
}
}
func
(
env
*
L2FaultProofEnv
)
RunFaultProofProgram
(
t
helpers
.
Testing
,
l2ClaimBlockNum
uint64
,
checkResult
CheckResult
,
fixtureInputParams
...
FixtureInputParam
)
{
func
(
env
*
L2FaultProofEnv
)
RunFaultProofProgram
(
t
helpers
.
Testing
,
l2ClaimBlockNum
uint64
,
checkResult
CheckResult
,
fixtureInputParams
...
FixtureInputParam
)
{
defaultParam
:=
WithPreInteropDefaults
(
t
,
l2ClaimBlockNum
,
env
.
Sequencer
.
L2Verifier
,
env
.
Engine
)
defaultParam
:=
WithPreInteropDefaults
(
t
,
l2ClaimBlockNum
,
env
.
Sequencer
.
L2Verifier
,
env
.
Engine
)
combinedParams
:=
[]
FixtureInputParam
{
defaultParam
}
combinedParams
:=
[]
FixtureInputParam
{
defaultParam
}
...
...
op-program/client/interop/interop.go
View file @
f5fc73db
...
@@ -13,12 +13,17 @@ import (
...
@@ -13,12 +13,17 @@ import (
"github.com/ethereum-optimism/optimism/op-program/client/tasks"
"github.com/ethereum-optimism/optimism/op-program/client/tasks"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params"
)
)
var
(
var
(
ErrIncorrectOutputRootType
=
errors
.
New
(
"incorrect output root type"
)
ErrIncorrectOutputRootType
=
errors
.
New
(
"incorrect output root type"
)
ErrL1HeadReached
=
errors
.
New
(
"l1 head reached"
)
InvalidTransition
=
[]
byte
(
"invalid"
)
InvalidTransitionHash
=
crypto
.
Keccak256Hash
(
InvalidTransition
)
)
)
type
taskExecutor
interface
{
type
taskExecutor
interface
{
...
@@ -40,69 +45,101 @@ func RunInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
...
@@ -40,69 +45,101 @@ func RunInteropProgram(logger log.Logger, bootInfo *boot.BootInfoInterop, l1Prei
func
runInteropProgram
(
logger
log
.
Logger
,
bootInfo
*
boot
.
BootInfoInterop
,
l1PreimageOracle
l1
.
Oracle
,
l2PreimageOracle
l2
.
Oracle
,
validateClaim
bool
,
tasks
taskExecutor
)
error
{
func
runInteropProgram
(
logger
log
.
Logger
,
bootInfo
*
boot
.
BootInfoInterop
,
l1PreimageOracle
l1
.
Oracle
,
l2PreimageOracle
l2
.
Oracle
,
validateClaim
bool
,
tasks
taskExecutor
)
error
{
logger
.
Info
(
"Interop Program Bootstrapped"
,
"bootInfo"
,
bootInfo
)
logger
.
Info
(
"Interop Program Bootstrapped"
,
"bootInfo"
,
bootInfo
)
expected
,
err
:=
stateTransition
(
logger
,
bootInfo
,
l1PreimageOracle
,
l2PreimageOracle
,
tasks
)
if
err
!=
nil
{
return
err
}
if
!
validateClaim
{
return
nil
}
return
claim
.
ValidateClaim
(
logger
,
eth
.
Bytes32
(
bootInfo
.
Claim
),
eth
.
Bytes32
(
expected
))
}
func
stateTransition
(
logger
log
.
Logger
,
bootInfo
*
boot
.
BootInfoInterop
,
l1PreimageOracle
l1
.
Oracle
,
l2PreimageOracle
l2
.
Oracle
,
tasks
taskExecutor
)
(
common
.
Hash
,
error
)
{
if
bootInfo
.
AgreedPrestate
==
InvalidTransitionHash
{
return
InvalidTransitionHash
,
nil
}
transitionState
,
superRoot
,
err
:=
parseAgreedState
(
bootInfo
,
l2PreimageOracle
)
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
expectedPendingProgress
:=
transitionState
.
PendingProgress
if
transitionState
.
Step
<
uint64
(
len
(
superRoot
.
Chains
))
{
block
,
err
:=
deriveOptimisticBlock
(
logger
,
bootInfo
,
l1PreimageOracle
,
l2PreimageOracle
,
superRoot
,
transitionState
,
tasks
)
if
errors
.
Is
(
err
,
ErrL1HeadReached
)
{
return
InvalidTransitionHash
,
nil
}
else
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
expectedPendingProgress
=
append
(
expectedPendingProgress
,
block
)
}
finalState
:=
&
types
.
TransitionState
{
SuperRoot
:
transitionState
.
SuperRoot
,
PendingProgress
:
expectedPendingProgress
,
Step
:
transitionState
.
Step
+
1
,
}
expected
,
err
:=
finalState
.
Hash
()
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
return
expected
,
nil
}
func
parseAgreedState
(
bootInfo
*
boot
.
BootInfoInterop
,
l2PreimageOracle
l2
.
Oracle
)
(
*
types
.
TransitionState
,
*
eth
.
SuperV1
,
error
)
{
// For the first step in a timestamp, we would get a SuperRoot as the agreed claim - TransitionStateByRoot will
// For the first step in a timestamp, we would get a SuperRoot as the agreed claim - TransitionStateByRoot will
// automatically convert it to a TransitionState with Step: 0.
// automatically convert it to a TransitionState with Step: 0.
transitionState
:=
l2PreimageOracle
.
TransitionStateByRoot
(
bootInfo
.
AgreedPrestate
)
transitionState
:=
l2PreimageOracle
.
TransitionStateByRoot
(
bootInfo
.
AgreedPrestate
)
if
transitionState
.
Version
()
!=
types
.
IntermediateTransitionVersion
{
if
transitionState
.
Version
()
!=
types
.
IntermediateTransitionVersion
{
return
fmt
.
Errorf
(
"%w: %v"
,
ErrIncorrectOutputRootType
,
transitionState
.
Version
())
return
nil
,
nil
,
fmt
.
Errorf
(
"%w: %v"
,
ErrIncorrectOutputRootType
,
transitionState
.
Version
())
}
}
super
,
err
:=
eth
.
UnmarshalSuperRoot
(
transitionState
.
SuperRoot
)
super
,
err
:=
eth
.
UnmarshalSuperRoot
(
transitionState
.
SuperRoot
)
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid super root: %w"
,
err
)
return
nil
,
nil
,
fmt
.
Errorf
(
"invalid super root: %w"
,
err
)
}
}
if
super
.
Version
()
!=
eth
.
SuperRootVersionV1
{
if
super
.
Version
()
!=
eth
.
SuperRootVersionV1
{
return
fmt
.
Errorf
(
"%w: %v"
,
ErrIncorrectOutputRootType
,
super
.
Version
())
return
nil
,
nil
,
fmt
.
Errorf
(
"%w: %v"
,
ErrIncorrectOutputRootType
,
super
.
Version
())
}
}
superRoot
:=
super
.
(
*
eth
.
SuperV1
)
superRoot
:=
super
.
(
*
eth
.
SuperV1
)
return
transitionState
,
superRoot
,
nil
}
expectedPendingProgress
:=
transitionState
.
PendingProgress
func
deriveOptimisticBlock
(
logger
log
.
Logger
,
bootInfo
*
boot
.
BootInfoInterop
,
l1PreimageOracle
l1
.
Oracle
,
l2PreimageOracle
l2
.
Oracle
,
superRoot
*
eth
.
SuperV1
,
transitionState
*
types
.
TransitionState
,
tasks
taskExecutor
)
(
types
.
OptimisticBlock
,
error
)
{
if
transitionState
.
Step
<
uint64
(
len
(
superRoot
.
Chains
))
{
chainAgreedPrestate
:=
superRoot
.
Chains
[
transitionState
.
Step
]
chainAgreedPrestate
:=
superRoot
.
Chains
[
transitionState
.
Step
]
rollupCfg
,
err
:=
bootInfo
.
Configs
.
RollupConfig
(
chainAgreedPrestate
.
ChainID
)
rollupCfg
,
err
:=
bootInfo
.
Configs
.
RollupConfig
(
chainAgreedPrestate
.
ChainID
)
if
err
!=
nil
{
if
err
!=
nil
{
return
types
.
OptimisticBlock
{},
fmt
.
Errorf
(
"no rollup config available for chain ID %v: %w"
,
chainAgreedPrestate
.
ChainID
,
err
)
return
fmt
.
Errorf
(
"no rollup config available for chain ID %v: %w"
,
chainAgreedPrestate
.
ChainID
,
err
)
}
l2ChainConfig
,
err
:=
bootInfo
.
Configs
.
ChainConfig
(
chainAgreedPrestate
.
ChainID
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"no chain config available for chain ID %v: %w"
,
chainAgreedPrestate
.
ChainID
,
err
)
}
claimedBlockNumber
,
err
:=
rollupCfg
.
TargetBlockNumber
(
superRoot
.
Timestamp
+
1
)
if
err
!=
nil
{
return
err
}
derivationResult
,
err
:=
tasks
.
RunDerivation
(
logger
,
rollupCfg
,
l2ChainConfig
,
bootInfo
.
L1Head
,
chainAgreedPrestate
.
Output
,
claimedBlockNumber
,
l1PreimageOracle
,
l2PreimageOracle
,
)
if
err
!=
nil
{
return
err
}
expectedPendingProgress
=
append
(
expectedPendingProgress
,
types
.
OptimisticBlock
{
BlockHash
:
derivationResult
.
BlockHash
,
OutputRoot
:
derivationResult
.
OutputRoot
,
})
}
}
finalState
:=
&
types
.
TransitionState
{
l2ChainConfig
,
err
:=
bootInfo
.
Configs
.
ChainConfig
(
chainAgreedPrestate
.
ChainID
)
SuperRoot
:
transitionState
.
SuperRoot
,
if
err
!=
nil
{
PendingProgress
:
expectedPendingProgress
,
return
types
.
OptimisticBlock
{},
fmt
.
Errorf
(
"no chain config available for chain ID %v: %w"
,
chainAgreedPrestate
.
ChainID
,
err
)
Step
:
transitionState
.
Step
+
1
,
}
}
expected
,
err
:=
finalState
.
Hash
(
)
claimedBlockNumber
,
err
:=
rollupCfg
.
TargetBlockNumber
(
superRoot
.
Timestamp
+
1
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
types
.
OptimisticBlock
{},
err
}
}
if
!
validateClaim
{
derivationResult
,
err
:=
tasks
.
RunDerivation
(
return
nil
logger
,
rollupCfg
,
l2ChainConfig
,
bootInfo
.
L1Head
,
chainAgreedPrestate
.
Output
,
claimedBlockNumber
,
l1PreimageOracle
,
l2PreimageOracle
,
)
if
err
!=
nil
{
return
types
.
OptimisticBlock
{},
err
}
}
return
claim
.
ValidateClaim
(
logger
,
eth
.
Bytes32
(
bootInfo
.
Claim
),
eth
.
Bytes32
(
expected
))
if
derivationResult
.
Head
.
Number
<
claimedBlockNumber
{
return
types
.
OptimisticBlock
{},
ErrL1HeadReached
}
block
:=
types
.
OptimisticBlock
{
BlockHash
:
derivationResult
.
BlockHash
,
OutputRoot
:
derivationResult
.
OutputRoot
,
}
return
block
,
nil
}
}
type
interopTaskExecutor
struct
{
type
interopTaskExecutor
struct
{
...
...
op-program/client/interop/interop_test.go
View file @
f5fc73db
...
@@ -43,6 +43,7 @@ func setupTwoChains() (*staticConfigSource, *eth.SuperV1, stubTasks) {
...
@@ -43,6 +43,7 @@ func setupTwoChains() (*staticConfigSource, *eth.SuperV1, stubTasks) {
chainConfigs
:
[]
*
params
.
ChainConfig
{
chainCfg1
,
&
chainCfg2
},
chainConfigs
:
[]
*
params
.
ChainConfig
{
chainCfg1
,
&
chainCfg2
},
}
}
tasksStub
:=
stubTasks
{
tasksStub
:=
stubTasks
{
l2SafeHead
:
eth
.
L2BlockRef
{
Number
:
918429823450218
},
// Past the claimed block
blockHash
:
common
.
Hash
{
0x22
},
blockHash
:
common
.
Hash
{
0x22
},
outputRoot
:
eth
.
Bytes32
{
0x66
},
outputRoot
:
eth
.
Bytes32
{
0x66
},
}
}
...
...
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