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
601de6b6
Unverified
Commit
601de6b6
authored
Oct 19, 2023
by
Joshua Gutow
Committed by
GitHub
Oct 19, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7707 from danyalprout/shanghai-p2p
Canyon: Blocks V2 P2P & Testing
parents
86fedeaa
76a6573e
Changes
30
Show whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
1019 additions
and
137 deletions
+1019
-137
config.go
op-chain-ops/genesis/config.go
+16
-1
config_test.go
op-chain-ops/genesis/config_test.go
+12
-0
genesis.go
op-chain-ops/genesis/genesis.go
+2
-0
l2_engine_test.go
op-e2e/actions/l2_engine_test.go
+11
-3
setup.go
op-e2e/e2eutils/setup.go
+10
-0
op_geth.go
op-e2e/op_geth.go
+8
-1
op_geth_test.go
op-e2e/op_geth_test.go
+147
-5
setup.go
op-e2e/setup.go
+2
-0
system_test.go
op-e2e/system_test.go
+1
-1
node.go
op-node/node/node.go
+1
-0
gossip.go
op-node/p2p/gossip.go
+116
-35
gossip_test.go
op-node/p2p/gossip_test.go
+5
-0
rpc_server.go
op-node/p2p/rpc_server.go
+13
-11
sync.go
op-node/p2p/sync.go
+13
-5
attributes.go
op-node/rollup/derive/attributes.go
+6
-0
engine_consolidate.go
op-node/rollup/derive/engine_consolidate.go
+31
-0
engine_consolidate_test.go
op-node/rollup/derive/engine_consolidate_test.go
+69
-0
engine_queue.go
op-node/rollup/derive/engine_queue.go
+1
-0
types.go
op-node/rollup/types.go
+4
-0
types_test.go
op-node/rollup/types_test.go
+51
-0
engine.go
op-program/client/l2/engine.go
+8
-4
engine_test.go
op-program/client/l2/engine_test.go
+3
-3
l2_engine_api.go
op-program/client/l2/engineapi/l2_engine_api.go
+21
-1
l2_engine_api_tests.go
op-program/client/l2/engineapi/test/l2_engine_api_tests.go
+42
-18
Makefile
op-service/Makefile
+2
-1
ssz.go
op-service/eth/ssz.go
+165
-19
ssz_test.go
op-service/eth/ssz_test.go
+206
-18
types.go
op-service/eth/types.go
+39
-8
types.go
op-service/sources/types.go
+2
-0
rollup-node-p2p.md
specs/rollup-node-p2p.md
+12
-3
No files found.
op-chain-ops/genesis/config.go
View file @
601de6b6
...
@@ -112,8 +112,11 @@ type DeployConfig struct {
...
@@ -112,8 +112,11 @@ type DeployConfig struct {
L2GenesisBlockBaseFeePerGas
*
hexutil
.
Big
`json:"l2GenesisBlockBaseFeePerGas"`
L2GenesisBlockBaseFeePerGas
*
hexutil
.
Big
`json:"l2GenesisBlockBaseFeePerGas"`
// L2GenesisRegolithTimeOffset is the number of seconds after genesis block that Regolith hard fork activates.
// L2GenesisRegolithTimeOffset is the number of seconds after genesis block that Regolith hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable
r
egolith.
// Set it to 0 to activate at genesis. Nil to disable
R
egolith.
L2GenesisRegolithTimeOffset
*
hexutil
.
Uint64
`json:"l2GenesisRegolithTimeOffset,omitempty"`
L2GenesisRegolithTimeOffset
*
hexutil
.
Uint64
`json:"l2GenesisRegolithTimeOffset,omitempty"`
// L2GenesisCanyonTimeOffset is the number of seconds after genesis block that Canyon hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable Canyon.
L2GenesisCanyonTimeOffset
*
hexutil
.
Uint64
`json:"L2GenesisCanyonTimeOffset,omitempty"`
// L2GenesisSpanBatchTimeOffset is the number of seconds after genesis block that Span Batch hard fork activates.
// L2GenesisSpanBatchTimeOffset is the number of seconds after genesis block that Span Batch hard fork activates.
// Set it to 0 to activate at genesis. Nil to disable SpanBatch.
// Set it to 0 to activate at genesis. Nil to disable SpanBatch.
L2GenesisSpanBatchTimeOffset
*
hexutil
.
Uint64
`json:"l2GenesisSpanBatchTimeOffset,omitempty"`
L2GenesisSpanBatchTimeOffset
*
hexutil
.
Uint64
`json:"l2GenesisSpanBatchTimeOffset,omitempty"`
...
@@ -449,6 +452,17 @@ func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 {
...
@@ -449,6 +452,17 @@ func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 {
return
&
v
return
&
v
}
}
func
(
d
*
DeployConfig
)
CanyonTime
(
genesisTime
uint64
)
*
uint64
{
if
d
.
L2GenesisCanyonTimeOffset
==
nil
{
return
nil
}
v
:=
uint64
(
0
)
if
offset
:=
*
d
.
L2GenesisCanyonTimeOffset
;
offset
>
0
{
v
=
genesisTime
+
uint64
(
offset
)
}
return
&
v
}
func
(
d
*
DeployConfig
)
SpanBatchTime
(
genesisTime
uint64
)
*
uint64
{
func
(
d
*
DeployConfig
)
SpanBatchTime
(
genesisTime
uint64
)
*
uint64
{
if
d
.
L2GenesisSpanBatchTimeOffset
==
nil
{
if
d
.
L2GenesisSpanBatchTimeOffset
==
nil
{
return
nil
return
nil
...
@@ -497,6 +511,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas
...
@@ -497,6 +511,7 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *types.Block, l2GenesisBlockHas
DepositContractAddress
:
d
.
OptimismPortalProxy
,
DepositContractAddress
:
d
.
OptimismPortalProxy
,
L1SystemConfigAddress
:
d
.
SystemConfigProxy
,
L1SystemConfigAddress
:
d
.
SystemConfigProxy
,
RegolithTime
:
d
.
RegolithTime
(
l1StartBlock
.
Time
()),
RegolithTime
:
d
.
RegolithTime
(
l1StartBlock
.
Time
()),
CanyonTime
:
d
.
CanyonTime
(
l1StartBlock
.
Time
()),
SpanBatchTime
:
d
.
SpanBatchTime
(
l1StartBlock
.
Time
()),
SpanBatchTime
:
d
.
SpanBatchTime
(
l1StartBlock
.
Time
()),
},
nil
},
nil
}
}
...
...
op-chain-ops/genesis/config_test.go
View file @
601de6b6
...
@@ -49,6 +49,18 @@ func TestRegolithTimeAsOffset(t *testing.T) {
...
@@ -49,6 +49,18 @@ func TestRegolithTimeAsOffset(t *testing.T) {
require
.
Equal
(
t
,
uint64
(
1500
+
5000
),
*
config
.
RegolithTime
(
5000
))
require
.
Equal
(
t
,
uint64
(
1500
+
5000
),
*
config
.
RegolithTime
(
5000
))
}
}
func
TestCanyonTimeZero
(
t
*
testing
.
T
)
{
canyonOffset
:=
hexutil
.
Uint64
(
0
)
config
:=
&
DeployConfig
{
L2GenesisCanyonTimeOffset
:
&
canyonOffset
}
require
.
Equal
(
t
,
uint64
(
0
),
*
config
.
CanyonTime
(
1234
))
}
func
TestCanyonTimeOffset
(
t
*
testing
.
T
)
{
canyonOffset
:=
hexutil
.
Uint64
(
1500
)
config
:=
&
DeployConfig
{
L2GenesisCanyonTimeOffset
:
&
canyonOffset
}
require
.
Equal
(
t
,
uint64
(
1234
+
1500
),
*
config
.
CanyonTime
(
1234
))
}
// TestCopy will copy a DeployConfig and ensure that the copy is equal to the original.
// TestCopy will copy a DeployConfig and ensure that the copy is equal to the original.
func
TestCopy
(
t
*
testing
.
T
)
{
func
TestCopy
(
t
*
testing
.
T
)
{
b
,
err
:=
os
.
ReadFile
(
"testdata/test-deploy-config-full.json"
)
b
,
err
:=
os
.
ReadFile
(
"testdata/test-deploy-config-full.json"
)
...
...
op-chain-ops/genesis/genesis.go
View file @
601de6b6
...
@@ -58,6 +58,8 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro
...
@@ -58,6 +58,8 @@ func NewL2Genesis(config *DeployConfig, block *types.Block) (*core.Genesis, erro
TerminalTotalDifficultyPassed
:
true
,
TerminalTotalDifficultyPassed
:
true
,
BedrockBlock
:
new
(
big
.
Int
)
.
SetUint64
(
uint64
(
config
.
L2GenesisBlockNumber
)),
BedrockBlock
:
new
(
big
.
Int
)
.
SetUint64
(
uint64
(
config
.
L2GenesisBlockNumber
)),
RegolithTime
:
config
.
RegolithTime
(
block
.
Time
()),
RegolithTime
:
config
.
RegolithTime
(
block
.
Time
()),
CanyonTime
:
config
.
CanyonTime
(
block
.
Time
()),
ShanghaiTime
:
config
.
CanyonTime
(
block
.
Time
()),
Optimism
:
&
params
.
OptimismConfig
{
Optimism
:
&
params
.
OptimismConfig
{
EIP1559Denominator
:
eip1559Denom
,
EIP1559Denominator
:
eip1559Denom
,
EIP1559Elasticity
:
eip1559Elasticity
,
EIP1559Elasticity
:
eip1559Elasticity
,
...
...
op-e2e/actions/l2_engine_test.go
View file @
601de6b6
...
@@ -46,7 +46,7 @@ func TestL2EngineAPI(gt *testing.T) {
...
@@ -46,7 +46,7 @@ func TestL2EngineAPI(gt *testing.T) {
chainA
,
_
:=
core
.
GenerateChain
(
sd
.
L2Cfg
.
Config
,
genesisBlock
,
consensus
,
db
,
1
,
func
(
i
int
,
gen
*
core
.
BlockGen
)
{
chainA
,
_
:=
core
.
GenerateChain
(
sd
.
L2Cfg
.
Config
,
genesisBlock
,
consensus
,
db
,
1
,
func
(
i
int
,
gen
*
core
.
BlockGen
)
{
gen
.
SetCoinbase
(
common
.
Address
{
'A'
})
gen
.
SetCoinbase
(
common
.
Address
{
'A'
})
})
})
payloadA
,
err
:=
eth
.
BlockAsPayload
(
chainA
[
0
])
payloadA
,
err
:=
eth
.
BlockAsPayload
(
chainA
[
0
]
,
sd
.
RollupCfg
.
CanyonTime
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// apply the payload
// apply the payload
...
@@ -69,7 +69,7 @@ func TestL2EngineAPI(gt *testing.T) {
...
@@ -69,7 +69,7 @@ func TestL2EngineAPI(gt *testing.T) {
chainB
,
_
:=
core
.
GenerateChain
(
sd
.
L2Cfg
.
Config
,
genesisBlock
,
consensus
,
db
,
1
,
func
(
i
int
,
gen
*
core
.
BlockGen
)
{
chainB
,
_
:=
core
.
GenerateChain
(
sd
.
L2Cfg
.
Config
,
genesisBlock
,
consensus
,
db
,
1
,
func
(
i
int
,
gen
*
core
.
BlockGen
)
{
gen
.
SetCoinbase
(
common
.
Address
{
'B'
})
gen
.
SetCoinbase
(
common
.
Address
{
'B'
})
})
})
payloadB
,
err
:=
eth
.
BlockAsPayload
(
chainB
[
0
])
payloadB
,
err
:=
eth
.
BlockAsPayload
(
chainB
[
0
]
,
sd
.
RollupCfg
.
CanyonTime
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
// apply the payload
// apply the payload
...
@@ -125,18 +125,26 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) {
...
@@ -125,18 +125,26 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) {
l2Cl
,
err
:=
sources
.
NewEngineClient
(
engine
.
RPCClient
(),
log
,
nil
,
sources
.
EngineClientDefaultConfig
(
sd
.
RollupCfg
))
l2Cl
,
err
:=
sources
.
NewEngineClient
(
engine
.
RPCClient
(),
log
,
nil
,
sources
.
EngineClientDefaultConfig
(
sd
.
RollupCfg
))
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
nextBlockTime
:=
eth
.
Uint64Quantity
(
parent
.
Time
)
+
2
var
w
*
eth
.
Withdrawals
if
sd
.
RollupCfg
.
IsCanyon
(
uint64
(
nextBlockTime
))
{
w
=
&
eth
.
Withdrawals
{}
}
// Now let's ask the engine to build a block
// Now let's ask the engine to build a block
fcRes
,
err
:=
l2Cl
.
ForkchoiceUpdate
(
t
.
Ctx
(),
&
eth
.
ForkchoiceState
{
fcRes
,
err
:=
l2Cl
.
ForkchoiceUpdate
(
t
.
Ctx
(),
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
parent
.
Hash
(),
HeadBlockHash
:
parent
.
Hash
(),
SafeBlockHash
:
genesisBlock
.
Hash
(),
SafeBlockHash
:
genesisBlock
.
Hash
(),
FinalizedBlockHash
:
genesisBlock
.
Hash
(),
FinalizedBlockHash
:
genesisBlock
.
Hash
(),
},
&
eth
.
PayloadAttributes
{
},
&
eth
.
PayloadAttributes
{
Timestamp
:
eth
.
Uint64Quantity
(
parent
.
Time
)
+
2
,
Timestamp
:
nextBlockTime
,
PrevRandao
:
eth
.
Bytes32
{},
PrevRandao
:
eth
.
Bytes32
{},
SuggestedFeeRecipient
:
common
.
Address
{
'C'
},
SuggestedFeeRecipient
:
common
.
Address
{
'C'
},
Transactions
:
nil
,
Transactions
:
nil
,
NoTxPool
:
false
,
NoTxPool
:
false
,
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
sd
.
RollupCfg
.
Genesis
.
SystemConfig
.
GasLimit
),
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
sd
.
RollupCfg
.
Genesis
.
SystemConfig
.
GasLimit
),
Withdrawals
:
w
,
})
})
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
fcRes
.
PayloadStatus
.
Status
,
eth
.
ExecutionValid
)
require
.
Equal
(
t
,
fcRes
.
PayloadStatus
.
Status
,
eth
.
ExecutionValid
)
...
...
op-e2e/e2eutils/setup.go
View file @
601de6b6
...
@@ -58,6 +58,7 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
...
@@ -58,6 +58,7 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
deployConfig
.
ChannelTimeout
=
tp
.
ChannelTimeout
deployConfig
.
ChannelTimeout
=
tp
.
ChannelTimeout
deployConfig
.
L1BlockTime
=
tp
.
L1BlockTime
deployConfig
.
L1BlockTime
=
tp
.
L1BlockTime
deployConfig
.
L2GenesisRegolithTimeOffset
=
nil
deployConfig
.
L2GenesisRegolithTimeOffset
=
nil
deployConfig
.
L2GenesisCanyonTimeOffset
=
CanyonTimeOffset
()
deployConfig
.
L2GenesisSpanBatchTimeOffset
=
SpanBatchTimeOffset
()
deployConfig
.
L2GenesisSpanBatchTimeOffset
=
SpanBatchTimeOffset
()
require
.
NoError
(
t
,
deployConfig
.
Check
())
require
.
NoError
(
t
,
deployConfig
.
Check
())
...
@@ -157,6 +158,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
...
@@ -157,6 +158,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
DepositContractAddress
:
deployConf
.
OptimismPortalProxy
,
DepositContractAddress
:
deployConf
.
OptimismPortalProxy
,
L1SystemConfigAddress
:
deployConf
.
SystemConfigProxy
,
L1SystemConfigAddress
:
deployConf
.
SystemConfigProxy
,
RegolithTime
:
deployConf
.
RegolithTime
(
uint64
(
deployConf
.
L1GenesisBlockTimestamp
)),
RegolithTime
:
deployConf
.
RegolithTime
(
uint64
(
deployConf
.
L1GenesisBlockTimestamp
)),
CanyonTime
:
deployConf
.
CanyonTime
(
uint64
(
deployConf
.
L1GenesisBlockTimestamp
)),
SpanBatchTime
:
deployConf
.
SpanBatchTime
(
uint64
(
deployConf
.
L1GenesisBlockTimestamp
)),
SpanBatchTime
:
deployConf
.
SpanBatchTime
(
uint64
(
deployConf
.
L1GenesisBlockTimestamp
)),
}
}
...
@@ -191,3 +193,11 @@ func SpanBatchTimeOffset() *hexutil.Uint64 {
...
@@ -191,3 +193,11 @@ func SpanBatchTimeOffset() *hexutil.Uint64 {
}
}
return
nil
return
nil
}
}
func
CanyonTimeOffset
()
*
hexutil
.
Uint64
{
if
os
.
Getenv
(
"OP_E2E_USE_CANYON"
)
==
"true"
{
offset
:=
hexutil
.
Uint64
(
0
)
return
&
offset
}
return
nil
}
op-e2e/op_geth.go
View file @
601de6b6
...
@@ -105,7 +105,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
...
@@ -105,7 +105,7 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
l2Client
,
err
:=
ethclient
.
Dial
(
node
.
HTTPEndpoint
())
l2Client
,
err
:=
ethclient
.
Dial
(
node
.
HTTPEndpoint
())
require
.
Nil
(
t
,
err
)
require
.
Nil
(
t
,
err
)
genesisPayload
,
err
:=
eth
.
BlockAsPayload
(
l2GenesisBlock
)
genesisPayload
,
err
:=
eth
.
BlockAsPayload
(
l2GenesisBlock
,
cfg
.
DeployConfig
.
CanyonTime
(
l2GenesisBlock
.
Time
())
)
require
.
Nil
(
t
,
err
)
require
.
Nil
(
t
,
err
)
return
&
OpGeth
{
return
&
OpGeth
{
...
@@ -209,11 +209,18 @@ func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.Payloa
...
@@ -209,11 +209,18 @@ func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.Payloa
}
}
txBytes
=
append
(
txBytes
,
bin
)
txBytes
=
append
(
txBytes
,
bin
)
}
}
var
withdrawals
*
eth
.
Withdrawals
if
d
.
L2ChainConfig
.
IsCanyon
(
uint64
(
timestamp
))
{
withdrawals
=
&
eth
.
Withdrawals
{}
}
attrs
:=
eth
.
PayloadAttributes
{
attrs
:=
eth
.
PayloadAttributes
{
Timestamp
:
timestamp
,
Timestamp
:
timestamp
,
Transactions
:
txBytes
,
Transactions
:
txBytes
,
NoTxPool
:
true
,
NoTxPool
:
true
,
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
d
.
SystemConfig
.
GasLimit
),
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
d
.
SystemConfig
.
GasLimit
),
Withdrawals
:
withdrawals
,
}
}
return
&
attrs
,
nil
return
&
attrs
,
nil
}
}
op-e2e/op_geth_test.go
View file @
601de6b6
...
@@ -2,12 +2,13 @@ package op_e2e
...
@@ -2,12 +2,13 @@ package op_e2e
import
(
import
(
"context"
"context"
"fmt"
"math/big"
"math/big"
"testing"
"testing"
"time"
"time"
"github.com/
stretchr/testify/requir
e"
"github.com/
ethereum-optimism/optimism/op-node/rollup/deriv
e"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/hexutil"
...
@@ -16,9 +17,8 @@ import (
...
@@ -16,9 +17,8 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/assert"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
)
// TestMissingGasLimit tests that op-geth cannot build a block without gas limit while optimism is active in the chain config.
// TestMissingGasLimit tests that op-geth cannot build a block without gas limit while optimism is active in the chain config.
...
@@ -719,3 +719,145 @@ func TestRegolith(t *testing.T) {
...
@@ -719,3 +719,145 @@ func TestRegolith(t *testing.T) {
})
})
}
}
}
}
func
TestPreCanyon
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
futureTimestamp
:=
hexutil
.
Uint64
(
4
)
tests
:=
[]
struct
{
name
string
canyonTime
*
hexutil
.
Uint64
}{
{
name
:
"CanyonNotScheduled"
},
{
name
:
"CanyonNotYetActive"
,
canyonTime
:
&
futureTimestamp
},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
fmt
.
Sprintf
(
"ReturnsNilWithdrawals_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
cfg
:=
DefaultSystemConfig
(
t
)
cfg
.
DeployConfig
.
L2GenesisCanyonTimeOffset
=
test
.
canyonTime
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
60
*
time
.
Second
)
defer
cancel
()
opGeth
,
err
:=
NewOpGeth
(
t
,
ctx
,
&
cfg
)
require
.
NoError
(
t
,
err
)
defer
opGeth
.
Close
()
b
,
err
:=
opGeth
.
AddL2Block
(
ctx
)
require
.
NoError
(
t
,
err
)
assert
.
Nil
(
t
,
b
.
Withdrawals
,
"should not have withdrawals"
)
l1Block
,
err
:=
opGeth
.
L2Client
.
BlockByNumber
(
ctx
,
nil
)
require
.
Nil
(
t
,
err
)
assert
.
Equal
(
t
,
types
.
Withdrawals
(
nil
),
l1Block
.
Withdrawals
())
})
t
.
Run
(
fmt
.
Sprintf
(
"RejectPushZeroTx_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
cfg
:=
DefaultSystemConfig
(
t
)
cfg
.
DeployConfig
.
L2GenesisCanyonTimeOffset
=
test
.
canyonTime
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
60
*
time
.
Second
)
defer
cancel
()
opGeth
,
err
:=
NewOpGeth
(
t
,
ctx
,
&
cfg
)
require
.
NoError
(
t
,
err
)
defer
opGeth
.
Close
()
pushZeroContractCreateTxn
:=
types
.
NewTx
(
&
types
.
DepositTx
{
From
:
cfg
.
Secrets
.
Addresses
()
.
Alice
,
Value
:
big
.
NewInt
(
params
.
Ether
),
Gas
:
1000001
,
Data
:
[]
byte
{
byte
(
vm
.
PUSH0
),
},
IsSystemTransaction
:
false
,
})
_
,
err
=
opGeth
.
AddL2Block
(
ctx
,
pushZeroContractCreateTxn
)
require
.
NoError
(
t
,
err
)
receipt
,
err
:=
opGeth
.
L2Client
.
TransactionReceipt
(
ctx
,
pushZeroContractCreateTxn
.
Hash
())
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
types
.
ReceiptStatusFailed
,
receipt
.
Status
)
})
}
}
func
TestCanyon
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
tests
:=
[]
struct
{
name
string
canyonTime
hexutil
.
Uint64
activeCanyon
func
(
ctx
context
.
Context
,
opGeth
*
OpGeth
)
}{
{
name
:
"ActivateAtGenesis"
,
canyonTime
:
0
,
activeCanyon
:
func
(
ctx
context
.
Context
,
opGeth
*
OpGeth
)
{}},
{
name
:
"ActivateAfterGenesis"
,
canyonTime
:
2
,
activeCanyon
:
func
(
ctx
context
.
Context
,
opGeth
*
OpGeth
)
{
// Adding this block advances us to the fork time.
_
,
err
:=
opGeth
.
AddL2Block
(
ctx
)
require
.
NoError
(
t
,
err
)
}},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
fmt
.
Sprintf
(
"ReturnsEmptyWithdrawals_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
cfg
:=
DefaultSystemConfig
(
t
)
s
:=
hexutil
.
Uint64
(
0
)
cfg
.
DeployConfig
.
L2GenesisRegolithTimeOffset
=
&
s
cfg
.
DeployConfig
.
L2GenesisCanyonTimeOffset
=
&
test
.
canyonTime
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
60
*
time
.
Second
)
defer
cancel
()
opGeth
,
err
:=
NewOpGeth
(
t
,
ctx
,
&
cfg
)
require
.
NoError
(
t
,
err
)
defer
opGeth
.
Close
()
test
.
activeCanyon
(
ctx
,
opGeth
)
b
,
err
:=
opGeth
.
AddL2Block
(
ctx
)
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
*
b
.
Withdrawals
,
eth
.
Withdrawals
{})
l1Block
,
err
:=
opGeth
.
L2Client
.
BlockByNumber
(
ctx
,
nil
)
require
.
Nil
(
t
,
err
)
assert
.
Equal
(
t
,
l1Block
.
Withdrawals
(),
types
.
Withdrawals
{})
})
t
.
Run
(
fmt
.
Sprintf
(
"AcceptsPushZeroTxn_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
InitParallel
(
t
)
cfg
:=
DefaultSystemConfig
(
t
)
cfg
.
DeployConfig
.
L2GenesisCanyonTimeOffset
=
&
test
.
canyonTime
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
60
*
time
.
Second
)
defer
cancel
()
opGeth
,
err
:=
NewOpGeth
(
t
,
ctx
,
&
cfg
)
require
.
NoError
(
t
,
err
)
defer
opGeth
.
Close
()
pushZeroContractCreateTxn
:=
types
.
NewTx
(
&
types
.
DepositTx
{
From
:
cfg
.
Secrets
.
Addresses
()
.
Alice
,
Value
:
big
.
NewInt
(
params
.
Ether
),
Gas
:
1000001
,
Data
:
[]
byte
{
byte
(
vm
.
PUSH0
),
},
IsSystemTransaction
:
false
,
})
_
,
err
=
opGeth
.
AddL2Block
(
ctx
,
pushZeroContractCreateTxn
)
require
.
NoError
(
t
,
err
)
receipt
,
err
:=
opGeth
.
L2Client
.
TransactionReceipt
(
ctx
,
pushZeroContractCreateTxn
.
Hash
())
require
.
NoError
(
t
,
err
)
assert
.
Equal
(
t
,
types
.
ReceiptStatusSuccessful
,
receipt
.
Status
)
})
}
}
op-e2e/setup.go
View file @
601de6b6
...
@@ -86,6 +86,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
...
@@ -86,6 +86,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
deployConfig
:=
config
.
DeployConfig
.
Copy
()
deployConfig
:=
config
.
DeployConfig
.
Copy
()
deployConfig
.
L1GenesisBlockTimestamp
=
hexutil
.
Uint64
(
time
.
Now
()
.
Unix
())
deployConfig
.
L1GenesisBlockTimestamp
=
hexutil
.
Uint64
(
time
.
Now
()
.
Unix
())
deployConfig
.
L2GenesisCanyonTimeOffset
=
e2eutils
.
CanyonTimeOffset
()
deployConfig
.
L2GenesisSpanBatchTimeOffset
=
e2eutils
.
SpanBatchTimeOffset
()
deployConfig
.
L2GenesisSpanBatchTimeOffset
=
e2eutils
.
SpanBatchTimeOffset
()
require
.
NoError
(
t
,
deployConfig
.
Check
(),
"Deploy config is invalid, do you need to run make devnet-allocs?"
)
require
.
NoError
(
t
,
deployConfig
.
Check
(),
"Deploy config is invalid, do you need to run make devnet-allocs?"
)
l1Deployments
:=
config
.
L1Deployments
.
Copy
()
l1Deployments
:=
config
.
L1Deployments
.
Copy
()
...
@@ -425,6 +426,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
...
@@ -425,6 +426,7 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
DepositContractAddress
:
cfg
.
DeployConfig
.
OptimismPortalProxy
,
DepositContractAddress
:
cfg
.
DeployConfig
.
OptimismPortalProxy
,
L1SystemConfigAddress
:
cfg
.
DeployConfig
.
SystemConfigProxy
,
L1SystemConfigAddress
:
cfg
.
DeployConfig
.
SystemConfigProxy
,
RegolithTime
:
cfg
.
DeployConfig
.
RegolithTime
(
uint64
(
cfg
.
DeployConfig
.
L1GenesisBlockTimestamp
)),
RegolithTime
:
cfg
.
DeployConfig
.
RegolithTime
(
uint64
(
cfg
.
DeployConfig
.
L1GenesisBlockTimestamp
)),
CanyonTime
:
cfg
.
DeployConfig
.
CanyonTime
(
uint64
(
cfg
.
DeployConfig
.
L1GenesisBlockTimestamp
)),
SpanBatchTime
:
cfg
.
DeployConfig
.
SpanBatchTime
(
uint64
(
cfg
.
DeployConfig
.
L1GenesisBlockTimestamp
)),
SpanBatchTime
:
cfg
.
DeployConfig
.
SpanBatchTime
(
uint64
(
cfg
.
DeployConfig
.
L1GenesisBlockTimestamp
)),
ProtocolVersionsAddress
:
cfg
.
L1Deployments
.
ProtocolVersionsProxy
,
ProtocolVersionsAddress
:
cfg
.
L1Deployments
.
ProtocolVersionsProxy
,
}
}
...
...
op-e2e/system_test.go
View file @
601de6b6
...
@@ -491,7 +491,7 @@ func TestSystemMockP2P(t *testing.T) {
...
@@ -491,7 +491,7 @@ func TestSystemMockP2P(t *testing.T) {
verifierPeerID
:=
sys
.
RollupNodes
[
"verifier"
]
.
P2P
()
.
Host
()
.
ID
()
verifierPeerID
:=
sys
.
RollupNodes
[
"verifier"
]
.
P2P
()
.
Host
()
.
ID
()
check
:=
func
()
bool
{
check
:=
func
()
bool
{
sequencerBlocksTopicPeers
:=
sys
.
RollupNodes
[
"sequencer"
]
.
P2P
()
.
GossipOut
()
.
BlocksTopic
Peers
()
sequencerBlocksTopicPeers
:=
sys
.
RollupNodes
[
"sequencer"
]
.
P2P
()
.
GossipOut
()
.
AllBlockTopics
Peers
()
return
slices
.
Contains
[[]
peer
.
ID
](
sequencerBlocksTopicPeers
,
verifierPeerID
)
return
slices
.
Contains
[[]
peer
.
ID
](
sequencerBlocksTopicPeers
,
verifierPeerID
)
}
}
...
...
op-node/node/node.go
View file @
601de6b6
...
@@ -510,6 +510,7 @@ func (n *OpNode) OnUnsafeL2Payload(ctx context.Context, from peer.ID, payload *e
...
@@ -510,6 +510,7 @@ func (n *OpNode) OnUnsafeL2Payload(ctx context.Context, from peer.ID, payload *e
// Pass on the event to the L2 Engine
// Pass on the event to the L2 Engine
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Second
*
30
)
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Second
*
30
)
defer
cancel
()
defer
cancel
()
if
err
:=
n
.
l2Driver
.
OnUnsafeL2Payload
(
ctx
,
payload
);
err
!=
nil
{
if
err
:=
n
.
l2Driver
.
OnUnsafeL2Payload
(
ctx
,
payload
);
err
!=
nil
{
n
.
log
.
Warn
(
"failed to notify engine driver of new L2 payload"
,
"err"
,
err
,
"id"
,
payload
.
ID
())
n
.
log
.
Warn
(
"failed to notify engine driver of new L2 payload"
,
"err"
,
err
,
"id"
,
payload
.
ID
())
}
}
...
...
op-node/p2p/gossip.go
View file @
601de6b6
...
@@ -70,10 +70,14 @@ func blocksTopicV1(cfg *rollup.Config) string {
...
@@ -70,10 +70,14 @@ func blocksTopicV1(cfg *rollup.Config) string {
return
fmt
.
Sprintf
(
"/optimism/%s/0/blocks"
,
cfg
.
L2ChainID
.
String
())
return
fmt
.
Sprintf
(
"/optimism/%s/0/blocks"
,
cfg
.
L2ChainID
.
String
())
}
}
func
blocksTopicV2
(
cfg
*
rollup
.
Config
)
string
{
return
fmt
.
Sprintf
(
"/optimism/%s/1/blocks"
,
cfg
.
L2ChainID
.
String
())
}
// BuildSubscriptionFilter builds a simple subscription filter,
// BuildSubscriptionFilter builds a simple subscription filter,
// to help protect against peers spamming useless subscriptions.
// to help protect against peers spamming useless subscriptions.
func
BuildSubscriptionFilter
(
cfg
*
rollup
.
Config
)
pubsub
.
SubscriptionFilter
{
func
BuildSubscriptionFilter
(
cfg
*
rollup
.
Config
)
pubsub
.
SubscriptionFilter
{
return
pubsub
.
NewAllowlistSubscriptionFilter
(
blocksTopicV1
(
cfg
))
// add more topics here in the future, if any.
return
pubsub
.
NewAllowlistSubscriptionFilter
(
blocksTopicV1
(
cfg
)
,
blocksTopicV2
(
cfg
)
)
// add more topics here in the future, if any.
}
}
var
msgBufPool
=
sync
.
Pool
{
New
:
func
()
any
{
var
msgBufPool
=
sync
.
Pool
{
New
:
func
()
any
{
...
@@ -239,7 +243,7 @@ func (sb *seenBlocks) markSeen(h common.Hash) {
...
@@ -239,7 +243,7 @@ func (sb *seenBlocks) markSeen(h common.Hash) {
sb
.
blockHashes
=
append
(
sb
.
blockHashes
,
h
)
sb
.
blockHashes
=
append
(
sb
.
blockHashes
,
h
)
}
}
func
BuildBlocksValidator
(
log
log
.
Logger
,
cfg
*
rollup
.
Config
,
runCfg
GossipRuntimeConfig
)
pubsub
.
ValidatorEx
{
func
BuildBlocksValidator
(
log
log
.
Logger
,
cfg
*
rollup
.
Config
,
runCfg
GossipRuntimeConfig
,
blockVersion
eth
.
BlockVersion
)
pubsub
.
ValidatorEx
{
// Seen block hashes per block height
// Seen block hashes per block height
// uint64 -> *seenBlocks
// uint64 -> *seenBlocks
...
@@ -284,7 +288,7 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
...
@@ -284,7 +288,7 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
// [REJECT] if the block encoding is not valid
// [REJECT] if the block encoding is not valid
var
payload
eth
.
ExecutionPayload
var
payload
eth
.
ExecutionPayload
if
err
:=
payload
.
UnmarshalSSZ
(
uint32
(
len
(
payloadBytes
)),
bytes
.
NewReader
(
payloadBytes
));
err
!=
nil
{
if
err
:=
payload
.
UnmarshalSSZ
(
blockVersion
,
uint32
(
len
(
payloadBytes
)),
bytes
.
NewReader
(
payloadBytes
));
err
!=
nil
{
log
.
Warn
(
"invalid payload"
,
"err"
,
err
,
"peer"
,
id
)
log
.
Warn
(
"invalid payload"
,
"err"
,
err
,
"peer"
,
id
)
return
pubsub
.
ValidationReject
return
pubsub
.
ValidationReject
}
}
...
@@ -310,6 +314,18 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
...
@@ -310,6 +314,18 @@ func BuildBlocksValidator(log log.Logger, cfg *rollup.Config, runCfg GossipRunti
return
pubsub
.
ValidationReject
return
pubsub
.
ValidationReject
}
}
// [REJECT] if a V1 Block has withdrawals
if
blockVersion
==
eth
.
BlockV1
&&
payload
.
Withdrawals
!=
nil
{
log
.
Warn
(
"payload is on v1 topic, but has withdrawals"
,
"bad_hash"
,
payload
.
BlockHash
.
String
())
return
pubsub
.
ValidationReject
}
// [REJECT] if a V2 Block does not have withdrawals
if
blockVersion
==
eth
.
BlockV2
&&
payload
.
Withdrawals
==
nil
{
log
.
Warn
(
"payload is on v2 topic, but does not have withdrawals"
,
"bad_hash"
,
payload
.
BlockHash
.
String
())
return
pubsub
.
ValidationReject
}
seen
,
ok
:=
blockHeightLRU
.
Get
(
uint64
(
payload
.
BlockNumber
))
seen
,
ok
:=
blockHeightLRU
.
Get
(
uint64
(
payload
.
BlockNumber
))
if
!
ok
{
if
!
ok
{
seen
=
new
(
seenBlocks
)
seen
=
new
(
seenBlocks
)
...
@@ -370,7 +386,9 @@ type GossipIn interface {
...
@@ -370,7 +386,9 @@ type GossipIn interface {
}
}
type
GossipTopicInfo
interface
{
type
GossipTopicInfo
interface
{
BlocksTopicPeers
()
[]
peer
.
ID
AllBlockTopicsPeers
()
[]
peer
.
ID
BlocksTopicV1Peers
()
[]
peer
.
ID
BlocksTopicV2Peers
()
[]
peer
.
ID
}
}
type
GossipOut
interface
{
type
GossipOut
interface
{
...
@@ -379,6 +397,21 @@ type GossipOut interface {
...
@@ -379,6 +397,21 @@ type GossipOut interface {
Close
()
error
Close
()
error
}
}
type
blockTopic
struct
{
// blocks topic, main handle on block gossip
topic
*
pubsub
.
Topic
// block events handler, to be cancelled before closing the blocks topic.
events
*
pubsub
.
TopicEventHandler
// block subscriptions, to be cancelled before closing blocks topic.
sub
*
pubsub
.
Subscription
}
func
(
bt
*
blockTopic
)
Close
()
error
{
bt
.
events
.
Cancel
()
bt
.
sub
.
Cancel
()
return
bt
.
topic
.
Close
()
}
type
publisher
struct
{
type
publisher
struct
{
log
log
.
Logger
log
log
.
Logger
cfg
*
rollup
.
Config
cfg
*
rollup
.
Config
...
@@ -388,20 +421,39 @@ type publisher struct {
...
@@ -388,20 +421,39 @@ type publisher struct {
// thus we have to stop it ourselves this way.
// thus we have to stop it ourselves this way.
p2pCancel
context
.
CancelFunc
p2pCancel
context
.
CancelFunc
// blocks topic, main handle on block gossip
blocksV1
*
blockTopic
blocksTopic
*
pubsub
.
Topic
blocksV2
*
blockTopic
// block events handler, to be cancelled before closing the blocks topic.
blocksEvents
*
pubsub
.
TopicEventHandler
// block subscriptions, to be cancelled before closing blocks topic.
blocksSub
*
pubsub
.
Subscription
runCfg
GossipRuntimeConfig
runCfg
GossipRuntimeConfig
}
}
var
_
GossipOut
=
(
*
publisher
)(
nil
)
var
_
GossipOut
=
(
*
publisher
)(
nil
)
func
(
p
*
publisher
)
BlocksTopicPeers
()
[]
peer
.
ID
{
func
combinePeers
(
allPeers
...
[]
peer
.
ID
)
[]
peer
.
ID
{
return
p
.
blocksTopic
.
ListPeers
()
var
seen
=
make
(
map
[
peer
.
ID
]
bool
)
var
res
[]
peer
.
ID
for
_
,
peers
:=
range
allPeers
{
for
_
,
p
:=
range
peers
{
if
_
,
ok
:=
seen
[
p
];
ok
{
continue
}
res
=
append
(
res
,
p
)
seen
[
p
]
=
true
}
}
return
res
}
func
(
p
*
publisher
)
AllBlockTopicsPeers
()
[]
peer
.
ID
{
return
combinePeers
(
p
.
BlocksTopicV1Peers
(),
p
.
BlocksTopicV2Peers
())
}
func
(
p
*
publisher
)
BlocksTopicV1Peers
()
[]
peer
.
ID
{
return
p
.
blocksV1
.
topic
.
ListPeers
()
}
func
(
p
*
publisher
)
BlocksTopicV2Peers
()
[]
peer
.
ID
{
return
p
.
blocksV2
.
topic
.
ListPeers
()
}
}
func
(
p
*
publisher
)
PublishL2Payload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
,
signer
Signer
)
error
{
func
(
p
*
publisher
)
PublishL2Payload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
,
signer
Signer
)
error
{
...
@@ -428,55 +480,84 @@ func (p *publisher) PublishL2Payload(ctx context.Context, payload *eth.Execution
...
@@ -428,55 +480,84 @@ func (p *publisher) PublishL2Payload(ctx context.Context, payload *eth.Execution
// This also copies the data, freeing up the original buffer to go back into the pool
// This also copies the data, freeing up the original buffer to go back into the pool
out
:=
snappy
.
Encode
(
nil
,
data
)
out
:=
snappy
.
Encode
(
nil
,
data
)
return
p
.
blocksTopic
.
Publish
(
ctx
,
out
)
if
p
.
cfg
.
IsCanyon
(
uint64
(
payload
.
Timestamp
))
{
return
p
.
blocksV2
.
topic
.
Publish
(
ctx
,
out
)
}
else
{
return
p
.
blocksV1
.
topic
.
Publish
(
ctx
,
out
)
}
}
}
func
(
p
*
publisher
)
Close
()
error
{
func
(
p
*
publisher
)
Close
()
error
{
p
.
p2pCancel
()
p
.
p2pCancel
()
p
.
blocksEvents
.
Cancel
()
e1
:=
p
.
blocksV1
.
Close
()
p
.
blocksSub
.
Cancel
()
e2
:=
p
.
blocksV2
.
Close
()
return
p
.
blocksTopic
.
Close
(
)
return
errors
.
Join
(
e1
,
e2
)
}
}
func
JoinGossip
(
self
peer
.
ID
,
ps
*
pubsub
.
PubSub
,
log
log
.
Logger
,
cfg
*
rollup
.
Config
,
runCfg
GossipRuntimeConfig
,
gossipIn
GossipIn
)
(
GossipOut
,
error
)
{
func
JoinGossip
(
self
peer
.
ID
,
ps
*
pubsub
.
PubSub
,
log
log
.
Logger
,
cfg
*
rollup
.
Config
,
runCfg
GossipRuntimeConfig
,
gossipIn
GossipIn
)
(
GossipOut
,
error
)
{
val
:=
guardGossipValidator
(
log
,
logValidationResult
(
self
,
"validated block"
,
log
,
BuildBlocksValidator
(
log
,
cfg
,
runCfg
)))
p2pCtx
,
p2pCancel
:=
context
.
WithCancel
(
context
.
Background
())
blocksTopicName
:=
blocksTopicV1
(
cfg
)
err
:=
ps
.
RegisterTopicValidator
(
blocksTopicName
,
v1Logger
:=
log
.
New
(
"topic"
,
"blocksV1"
)
val
,
blocksV1Validator
:=
guardGossipValidator
(
log
,
logValidationResult
(
self
,
"validated blockv1"
,
v1Logger
,
BuildBlocksValidator
(
v1Logger
,
cfg
,
runCfg
,
eth
.
BlockV1
)))
blocksV1
,
err
:=
newBlockTopic
(
p2pCtx
,
blocksTopicV1
(
cfg
),
ps
,
v1Logger
,
gossipIn
,
blocksV1Validator
)
if
err
!=
nil
{
p2pCancel
()
return
nil
,
fmt
.
Errorf
(
"failed to setup blocks v1 p2p: %w"
,
err
)
}
v2Logger
:=
log
.
New
(
"topic"
,
"blocksV2"
)
blocksV2Validator
:=
guardGossipValidator
(
log
,
logValidationResult
(
self
,
"validated blockv2"
,
v2Logger
,
BuildBlocksValidator
(
v2Logger
,
cfg
,
runCfg
,
eth
.
BlockV2
)))
blocksV2
,
err
:=
newBlockTopic
(
p2pCtx
,
blocksTopicV2
(
cfg
),
ps
,
v2Logger
,
gossipIn
,
blocksV2Validator
)
if
err
!=
nil
{
p2pCancel
()
return
nil
,
fmt
.
Errorf
(
"failed to setup blocks v2 p2p: %w"
,
err
)
}
return
&
publisher
{
log
:
log
,
cfg
:
cfg
,
p2pCancel
:
p2pCancel
,
blocksV1
:
blocksV1
,
blocksV2
:
blocksV2
,
runCfg
:
runCfg
,
},
nil
}
func
newBlockTopic
(
ctx
context
.
Context
,
topicId
string
,
ps
*
pubsub
.
PubSub
,
log
log
.
Logger
,
gossipIn
GossipIn
,
validator
pubsub
.
ValidatorEx
)
(
*
blockTopic
,
error
)
{
err
:=
ps
.
RegisterTopicValidator
(
topicId
,
validator
,
pubsub
.
WithValidatorTimeout
(
3
*
time
.
Second
),
pubsub
.
WithValidatorTimeout
(
3
*
time
.
Second
),
pubsub
.
WithValidatorConcurrency
(
4
))
pubsub
.
WithValidatorConcurrency
(
4
))
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to register
blocks
gossip topic: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to register gossip topic: %w"
,
err
)
}
}
blocksTopic
,
err
:=
ps
.
Join
(
blocksTopicName
)
blocksTopic
,
err
:=
ps
.
Join
(
topicId
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to join
blocks
gossip topic: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to join gossip topic: %w"
,
err
)
}
}
blocksTopicEvents
,
err
:=
blocksTopic
.
EventHandler
()
blocksTopicEvents
,
err
:=
blocksTopic
.
EventHandler
()
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create blocks gossip topic handler: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to create blocks gossip topic handler: %w"
,
err
)
}
}
p2pCtx
,
p2pCancel
:=
context
.
WithCancel
(
context
.
Background
())
go
LogTopicEvents
(
p2pCtx
,
log
.
New
(
"topic"
,
"blocks"
)
,
blocksTopicEvents
)
go
LogTopicEvents
(
ctx
,
log
,
blocksTopicEvents
)
subscription
,
err
:=
blocksTopic
.
Subscribe
()
subscription
,
err
:=
blocksTopic
.
Subscribe
()
if
err
!=
nil
{
if
err
!=
nil
{
p2pCancel
()
err
=
errors
.
Join
(
err
,
blocksTopic
.
Close
())
err
=
errors
.
Join
(
err
,
blocksTopic
.
Close
())
return
nil
,
fmt
.
Errorf
(
"failed to subscribe to blocks gossip topic: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to subscribe to blocks gossip topic: %w"
,
err
)
}
}
subscriber
:=
MakeSubscriber
(
log
,
BlocksHandler
(
gossipIn
.
OnUnsafeL2Payload
))
subscriber
:=
MakeSubscriber
(
log
,
BlocksHandler
(
gossipIn
.
OnUnsafeL2Payload
))
go
subscriber
(
p2pC
tx
,
subscription
)
go
subscriber
(
c
tx
,
subscription
)
return
&
publisher
{
return
&
blockTopic
{
log
:
log
,
topic
:
blocksTopic
,
cfg
:
cfg
,
events
:
blocksTopicEvents
,
blocksTopic
:
blocksTopic
,
sub
:
subscription
,
blocksEvents
:
blocksTopicEvents
,
blocksSub
:
subscription
,
p2pCancel
:
p2pCancel
,
runCfg
:
runCfg
,
},
nil
},
nil
}
}
...
...
op-node/p2p/gossip_test.go
View file @
601de6b6
...
@@ -40,6 +40,11 @@ func TestGuardGossipValidator(t *testing.T) {
...
@@ -40,6 +40,11 @@ func TestGuardGossipValidator(t *testing.T) {
require
.
Equal
(
t
,
pubsub
.
ValidationIgnore
,
val
(
context
.
Background
(),
"bob"
,
nil
))
require
.
Equal
(
t
,
pubsub
.
ValidationIgnore
,
val
(
context
.
Background
(),
"bob"
,
nil
))
}
}
func
TestCombinePeers
(
t
*
testing
.
T
)
{
res
:=
combinePeers
([]
peer
.
ID
{
"foo"
,
"bar"
},
[]
peer
.
ID
{
"bar"
,
"baz"
})
require
.
Equal
(
t
,
[]
peer
.
ID
{
"foo"
,
"bar"
,
"baz"
},
res
)
}
func
TestVerifyBlockSignature
(
t
*
testing
.
T
)
{
func
TestVerifyBlockSignature
(
t
*
testing
.
T
)
{
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlCrit
)
logger
:=
testlog
.
Logger
(
t
,
log
.
LvlCrit
)
cfg
:=
&
rollup
.
Config
{
cfg
:=
&
rollup
.
Config
{
...
...
op-node/p2p/rpc_server.go
View file @
601de6b6
...
@@ -194,7 +194,7 @@ func (s *APIBackend) Peers(ctx context.Context, connected bool) (*PeerDump, erro
...
@@ -194,7 +194,7 @@ func (s *APIBackend) Peers(ctx context.Context, connected bool) (*PeerDump, erro
dump
.
TotalConnected
+=
1
dump
.
TotalConnected
+=
1
}
}
}
}
for
_
,
id
:=
range
s
.
node
.
GossipOut
()
.
BlocksTopic
Peers
()
{
for
_
,
id
:=
range
s
.
node
.
GossipOut
()
.
AllBlockTopics
Peers
()
{
if
p
,
ok
:=
dump
.
Peers
[
id
.
String
()];
ok
{
if
p
,
ok
:=
dump
.
Peers
[
id
.
String
()];
ok
{
p
.
GossipBlocks
=
true
p
.
GossipBlocks
=
true
}
}
...
@@ -211,6 +211,7 @@ type PeerStats struct {
...
@@ -211,6 +211,7 @@ type PeerStats struct {
Connected
uint
`json:"connected"`
Connected
uint
`json:"connected"`
Table
uint
`json:"table"`
Table
uint
`json:"table"`
BlocksTopic
uint
`json:"blocksTopic"`
BlocksTopic
uint
`json:"blocksTopic"`
BlocksTopicV2
uint
`json:"blocksTopicV2"`
Banned
uint
`json:"banned"`
Banned
uint
`json:"banned"`
Known
uint
`json:"known"`
Known
uint
`json:"known"`
}
}
...
@@ -225,7 +226,8 @@ func (s *APIBackend) PeerStats(_ context.Context) (*PeerStats, error) {
...
@@ -225,7 +226,8 @@ func (s *APIBackend) PeerStats(_ context.Context) (*PeerStats, error) {
stats
:=
&
PeerStats
{
stats
:=
&
PeerStats
{
Connected
:
uint
(
len
(
nw
.
Peers
())),
Connected
:
uint
(
len
(
nw
.
Peers
())),
Table
:
0
,
Table
:
0
,
BlocksTopic
:
uint
(
len
(
s
.
node
.
GossipOut
()
.
BlocksTopicPeers
())),
BlocksTopic
:
uint
(
len
(
s
.
node
.
GossipOut
()
.
BlocksTopicV1Peers
())),
BlocksTopicV2
:
uint
(
len
(
s
.
node
.
GossipOut
()
.
BlocksTopicV2Peers
())),
Banned
:
0
,
Banned
:
0
,
Known
:
uint
(
len
(
pstore
.
Peers
())),
Known
:
uint
(
len
(
pstore
.
Peers
())),
}
}
...
...
op-node/p2p/sync.go
View file @
601de6b6
...
@@ -571,7 +571,7 @@ func (r requestResultErr) ResultCode() byte {
...
@@ -571,7 +571,7 @@ func (r requestResultErr) ResultCode() byte {
return
byte
(
r
)
return
byte
(
r
)
}
}
func
(
s
*
SyncClient
)
doRequest
(
ctx
context
.
Context
,
id
peer
.
ID
,
n
uint64
)
error
{
func
(
s
*
SyncClient
)
doRequest
(
ctx
context
.
Context
,
id
peer
.
ID
,
expectedBlockNum
uint64
)
error
{
// open stream to peer
// open stream to peer
reqCtx
,
reqCancel
:=
context
.
WithTimeout
(
ctx
,
streamTimeout
)
reqCtx
,
reqCancel
:=
context
.
WithTimeout
(
ctx
,
streamTimeout
)
str
,
err
:=
s
.
newStreamFn
(
reqCtx
,
id
,
s
.
payloadByNumber
)
str
,
err
:=
s
.
newStreamFn
(
reqCtx
,
id
,
s
.
payloadByNumber
)
...
@@ -582,8 +582,8 @@ func (s *SyncClient) doRequest(ctx context.Context, id peer.ID, n uint64) error
...
@@ -582,8 +582,8 @@ func (s *SyncClient) doRequest(ctx context.Context, id peer.ID, n uint64) error
defer
str
.
Close
()
defer
str
.
Close
()
// set write timeout (if available)
// set write timeout (if available)
_
=
str
.
SetWriteDeadline
(
time
.
Now
()
.
Add
(
clientWriteRequestTimeout
))
_
=
str
.
SetWriteDeadline
(
time
.
Now
()
.
Add
(
clientWriteRequestTimeout
))
if
err
:=
binary
.
Write
(
str
,
binary
.
LittleEndian
,
n
);
err
!=
nil
{
if
err
:=
binary
.
Write
(
str
,
binary
.
LittleEndian
,
expectedBlockNum
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to write request (%d): %w"
,
n
,
err
)
return
fmt
.
Errorf
(
"failed to write request (%d): %w"
,
expectedBlockNum
,
err
)
}
}
if
err
:=
str
.
CloseWrite
();
err
!=
nil
{
if
err
:=
str
.
CloseWrite
();
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to close writer side while making request: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to close writer side while making request: %w"
,
err
)
...
@@ -620,14 +620,22 @@ func (s *SyncClient) doRequest(ctx context.Context, id peer.ID, n uint64) error
...
@@ -620,14 +620,22 @@ func (s *SyncClient) doRequest(ctx context.Context, id peer.ID, n uint64) error
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to read response: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to read response: %w"
,
err
)
}
}
expectedBlockTime
:=
s
.
cfg
.
TimestampForBlock
(
expectedBlockNum
)
blockVersion
:=
eth
.
BlockV1
if
s
.
cfg
.
IsCanyon
(
expectedBlockTime
)
{
blockVersion
=
eth
.
BlockV2
}
var
res
eth
.
ExecutionPayload
var
res
eth
.
ExecutionPayload
if
err
:=
res
.
UnmarshalSSZ
(
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
));
err
!=
nil
{
if
err
:=
res
.
UnmarshalSSZ
(
blockVersion
,
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
));
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to decode response: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to decode response: %w"
,
err
)
}
}
if
err
:=
str
.
CloseRead
();
err
!=
nil
{
if
err
:=
str
.
CloseRead
();
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to close reading side"
)
return
fmt
.
Errorf
(
"failed to close reading side"
)
}
}
if
err
:=
verifyBlock
(
&
res
,
n
);
err
!=
nil
{
if
err
:=
verifyBlock
(
&
res
,
expectedBlockNum
);
err
!=
nil
{
return
fmt
.
Errorf
(
"received execution payload is invalid: %w"
,
err
)
return
fmt
.
Errorf
(
"received execution payload is invalid: %w"
,
err
)
}
}
select
{
select
{
...
...
op-node/rollup/derive/attributes.go
View file @
601de6b6
...
@@ -109,6 +109,11 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
...
@@ -109,6 +109,11 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
txs
=
append
(
txs
,
l1InfoTx
)
txs
=
append
(
txs
,
l1InfoTx
)
txs
=
append
(
txs
,
depositTxs
...
)
txs
=
append
(
txs
,
depositTxs
...
)
var
withdrawals
*
eth
.
Withdrawals
if
ba
.
cfg
.
IsCanyon
(
nextL2Time
)
{
withdrawals
=
&
eth
.
Withdrawals
{}
}
return
&
eth
.
PayloadAttributes
{
return
&
eth
.
PayloadAttributes
{
Timestamp
:
hexutil
.
Uint64
(
nextL2Time
),
Timestamp
:
hexutil
.
Uint64
(
nextL2Time
),
PrevRandao
:
eth
.
Bytes32
(
l1Info
.
MixDigest
()),
PrevRandao
:
eth
.
Bytes32
(
l1Info
.
MixDigest
()),
...
@@ -116,5 +121,6 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
...
@@ -116,5 +121,6 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
Transactions
:
txs
,
Transactions
:
txs
,
NoTxPool
:
true
,
NoTxPool
:
true
,
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
sysConfig
.
GasLimit
),
GasLimit
:
(
*
eth
.
Uint64Quantity
)(
&
sysConfig
.
GasLimit
),
Withdrawals
:
withdrawals
,
},
nil
},
nil
}
}
op-node/rollup/derive/engine_consolidate.go
View file @
601de6b6
...
@@ -41,6 +41,37 @@ func AttributesMatchBlock(attrs *eth.PayloadAttributes, parentHash common.Hash,
...
@@ -41,6 +41,37 @@ func AttributesMatchBlock(attrs *eth.PayloadAttributes, parentHash common.Hash,
if
*
attrs
.
GasLimit
!=
block
.
GasLimit
{
if
*
attrs
.
GasLimit
!=
block
.
GasLimit
{
return
fmt
.
Errorf
(
"gas limit does not match. expected %d. got: %d"
,
*
attrs
.
GasLimit
,
block
.
GasLimit
)
return
fmt
.
Errorf
(
"gas limit does not match. expected %d. got: %d"
,
*
attrs
.
GasLimit
,
block
.
GasLimit
)
}
}
if
withdrawalErr
:=
checkWithdrawalsMatch
(
attrs
.
Withdrawals
,
block
.
Withdrawals
);
withdrawalErr
!=
nil
{
return
withdrawalErr
}
return
nil
}
func
checkWithdrawalsMatch
(
attrWithdrawals
*
eth
.
Withdrawals
,
blockWithdrawals
*
eth
.
Withdrawals
)
error
{
if
attrWithdrawals
==
nil
&&
blockWithdrawals
==
nil
{
return
nil
}
if
attrWithdrawals
==
nil
&&
blockWithdrawals
!=
nil
{
return
fmt
.
Errorf
(
"expected withdrawals in block to be nil, actual %v"
,
*
blockWithdrawals
)
}
if
attrWithdrawals
!=
nil
&&
blockWithdrawals
==
nil
{
return
fmt
.
Errorf
(
"expected withdrawals in block to be non-nil %v, actual nil"
,
*
attrWithdrawals
)
}
if
len
(
*
attrWithdrawals
)
!=
len
(
*
blockWithdrawals
)
{
return
fmt
.
Errorf
(
"expected withdrawals in block to be %d, actual %d"
,
len
(
*
attrWithdrawals
),
len
(
*
blockWithdrawals
))
}
for
idx
,
expected
:=
range
*
attrWithdrawals
{
actual
:=
(
*
blockWithdrawals
)[
idx
]
if
expected
!=
actual
{
return
fmt
.
Errorf
(
"expected withdrawal %d to be %v, actual %v"
,
idx
,
expected
,
actual
)
}
}
return
nil
return
nil
}
}
...
...
op-node/rollup/derive/engine_consolidate_test.go
0 → 100644
View file @
601de6b6
package
derive
import
(
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/stretchr/testify/require"
)
func
TestWithdrawalsMatch
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
attrs
*
eth
.
Withdrawals
block
*
eth
.
Withdrawals
shouldMatch
bool
}{
{
attrs
:
nil
,
block
:
nil
,
shouldMatch
:
true
,
},
{
attrs
:
&
eth
.
Withdrawals
{},
block
:
nil
,
shouldMatch
:
false
,
},
{
attrs
:
nil
,
block
:
&
eth
.
Withdrawals
{},
shouldMatch
:
false
,
},
{
attrs
:
&
eth
.
Withdrawals
{},
block
:
&
eth
.
Withdrawals
{},
shouldMatch
:
true
,
},
{
attrs
:
&
eth
.
Withdrawals
{
{
Index
:
1
,
},
},
block
:
&
eth
.
Withdrawals
{},
shouldMatch
:
false
,
},
{
attrs
:
&
eth
.
Withdrawals
{
{
Index
:
1
,
},
},
block
:
&
eth
.
Withdrawals
{
{
Index
:
2
,
},
},
shouldMatch
:
false
,
},
}
for
_
,
test
:=
range
tests
{
err
:=
checkWithdrawalsMatch
(
test
.
attrs
,
test
.
block
)
if
test
.
shouldMatch
{
require
.
NoError
(
t
,
err
)
}
else
{
require
.
Error
(
t
,
err
)
}
}
}
op-node/rollup/derive/engine_queue.go
View file @
601de6b6
...
@@ -182,6 +182,7 @@ func (eq *EngineQueue) AddUnsafePayload(payload *eth.ExecutionPayload) {
...
@@ -182,6 +182,7 @@ func (eq *EngineQueue) AddUnsafePayload(payload *eth.ExecutionPayload) {
eq
.
log
.
Warn
(
"cannot add nil unsafe payload"
)
eq
.
log
.
Warn
(
"cannot add nil unsafe payload"
)
return
return
}
}
if
err
:=
eq
.
unsafePayloads
.
Push
(
payload
);
err
!=
nil
{
if
err
:=
eq
.
unsafePayloads
.
Push
(
payload
);
err
!=
nil
{
eq
.
log
.
Warn
(
"Could not add unsafe payload"
,
"id"
,
payload
.
ID
(),
"timestamp"
,
uint64
(
payload
.
Timestamp
),
"err"
,
err
)
eq
.
log
.
Warn
(
"Could not add unsafe payload"
,
"id"
,
payload
.
ID
(),
"timestamp"
,
uint64
(
payload
.
Timestamp
),
"err"
,
err
)
return
return
...
...
op-node/rollup/types.go
View file @
601de6b6
...
@@ -125,6 +125,10 @@ func (cfg *Config) ValidateL2Config(ctx context.Context, client L2Client) error
...
@@ -125,6 +125,10 @@ func (cfg *Config) ValidateL2Config(ctx context.Context, client L2Client) error
return
nil
return
nil
}
}
func
(
cfg
*
Config
)
TimestampForBlock
(
blockNumber
uint64
)
uint64
{
return
cfg
.
Genesis
.
L2Time
+
((
blockNumber
-
cfg
.
Genesis
.
L2
.
Number
)
*
cfg
.
BlockTime
)
}
func
(
cfg
*
Config
)
TargetBlockNumber
(
timestamp
uint64
)
(
num
uint64
,
err
error
)
{
func
(
cfg
*
Config
)
TargetBlockNumber
(
timestamp
uint64
)
(
num
uint64
,
err
error
)
{
// subtract genesis time from timestamp to get the time elapsed since genesis, and then divide that
// subtract genesis time from timestamp to get the time elapsed since genesis, and then divide that
// difference by the block time to get the expected L2 block number at the current time. If the
// difference by the block time to get the expected L2 block number at the current time. If the
...
...
op-node/rollup/types_test.go
View file @
601de6b6
...
@@ -396,3 +396,54 @@ func TestConfig_Check(t *testing.T) {
...
@@ -396,3 +396,54 @@ func TestConfig_Check(t *testing.T) {
})
})
}
}
}
}
func
TestTimestampForBlock
(
t
*
testing
.
T
)
{
config
:=
randConfig
()
tests
:=
[]
struct
{
name
string
genesisTime
uint64
genesisBlock
uint64
blockTime
uint64
blockNum
uint64
expectedBlockTime
uint64
}{
{
name
:
"FirstBlock"
,
genesisTime
:
100
,
genesisBlock
:
0
,
blockTime
:
2
,
blockNum
:
0
,
expectedBlockTime
:
100
,
},
{
name
:
"SecondBlock"
,
genesisTime
:
100
,
genesisBlock
:
0
,
blockTime
:
2
,
blockNum
:
1
,
expectedBlockTime
:
102
,
},
{
name
:
"NBlock"
,
genesisTime
:
100
,
genesisBlock
:
0
,
blockTime
:
2
,
blockNum
:
25
,
expectedBlockTime
:
150
,
},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
fmt
.
Sprintf
(
"TestTimestampForBlock_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
config
.
Genesis
.
L2Time
=
test
.
genesisTime
config
.
Genesis
.
L2
.
Number
=
test
.
genesisBlock
config
.
BlockTime
=
test
.
blockTime
timestamp
:=
config
.
TimestampForBlock
(
test
.
blockNum
)
assert
.
Equal
(
t
,
timestamp
,
test
.
expectedBlockTime
)
})
}
}
op-program/client/l2/engine.go
View file @
601de6b6
...
@@ -48,15 +48,19 @@ func (o *OracleEngine) L2OutputRoot() (eth.Bytes32, error) {
...
@@ -48,15 +48,19 @@ func (o *OracleEngine) L2OutputRoot() (eth.Bytes32, error) {
}
}
func
(
o
*
OracleEngine
)
GetPayload
(
ctx
context
.
Context
,
payloadId
eth
.
PayloadID
)
(
*
eth
.
ExecutionPayload
,
error
)
{
func
(
o
*
OracleEngine
)
GetPayload
(
ctx
context
.
Context
,
payloadId
eth
.
PayloadID
)
(
*
eth
.
ExecutionPayload
,
error
)
{
return
o
.
api
.
GetPayloadV1
(
ctx
,
payloadId
)
res
,
err
:=
o
.
api
.
GetPayloadV2
(
ctx
,
payloadId
)
if
err
!=
nil
{
return
nil
,
err
}
return
res
.
ExecutionPayload
,
nil
}
}
func
(
o
*
OracleEngine
)
ForkchoiceUpdate
(
ctx
context
.
Context
,
state
*
eth
.
ForkchoiceState
,
attr
*
eth
.
PayloadAttributes
)
(
*
eth
.
ForkchoiceUpdatedResult
,
error
)
{
func
(
o
*
OracleEngine
)
ForkchoiceUpdate
(
ctx
context
.
Context
,
state
*
eth
.
ForkchoiceState
,
attr
*
eth
.
PayloadAttributes
)
(
*
eth
.
ForkchoiceUpdatedResult
,
error
)
{
return
o
.
api
.
ForkchoiceUpdatedV
1
(
ctx
,
state
,
attr
)
return
o
.
api
.
ForkchoiceUpdatedV
2
(
ctx
,
state
,
attr
)
}
}
func
(
o
*
OracleEngine
)
NewPayload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
)
(
*
eth
.
PayloadStatusV1
,
error
)
{
func
(
o
*
OracleEngine
)
NewPayload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
)
(
*
eth
.
PayloadStatusV1
,
error
)
{
return
o
.
api
.
NewPayloadV
1
(
ctx
,
payload
)
return
o
.
api
.
NewPayloadV
2
(
ctx
,
payload
)
}
}
func
(
o
*
OracleEngine
)
PayloadByHash
(
ctx
context
.
Context
,
hash
common
.
Hash
)
(
*
eth
.
ExecutionPayload
,
error
)
{
func
(
o
*
OracleEngine
)
PayloadByHash
(
ctx
context
.
Context
,
hash
common
.
Hash
)
(
*
eth
.
ExecutionPayload
,
error
)
{
...
@@ -64,7 +68,7 @@ func (o *OracleEngine) PayloadByHash(ctx context.Context, hash common.Hash) (*et
...
@@ -64,7 +68,7 @@ func (o *OracleEngine) PayloadByHash(ctx context.Context, hash common.Hash) (*et
if
block
==
nil
{
if
block
==
nil
{
return
nil
,
ErrNotFound
return
nil
,
ErrNotFound
}
}
return
eth
.
BlockAsPayload
(
block
)
return
eth
.
BlockAsPayload
(
block
,
o
.
rollupCfg
.
CanyonTime
)
}
}
func
(
o
*
OracleEngine
)
PayloadByNumber
(
ctx
context
.
Context
,
n
uint64
)
(
*
eth
.
ExecutionPayload
,
error
)
{
func
(
o
*
OracleEngine
)
PayloadByNumber
(
ctx
context
.
Context
,
n
uint64
)
(
*
eth
.
ExecutionPayload
,
error
)
{
...
...
op-program/client/l2/engine_test.go
View file @
601de6b6
...
@@ -29,7 +29,7 @@ func TestPayloadByHash(t *testing.T) {
...
@@ -29,7 +29,7 @@ func TestPayloadByHash(t *testing.T) {
block
:=
stub
.
head
block
:=
stub
.
head
payload
,
err
:=
engine
.
PayloadByHash
(
ctx
,
block
.
Hash
())
payload
,
err
:=
engine
.
PayloadByHash
(
ctx
,
block
.
Hash
())
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
expected
,
err
:=
eth
.
BlockAsPayload
(
block
)
expected
,
err
:=
eth
.
BlockAsPayload
(
block
,
engine
.
rollupCfg
.
CanyonTime
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
payload
)
require
.
Equal
(
t
,
expected
,
payload
)
})
})
...
@@ -51,7 +51,7 @@ func TestPayloadByNumber(t *testing.T) {
...
@@ -51,7 +51,7 @@ func TestPayloadByNumber(t *testing.T) {
block
:=
stub
.
head
block
:=
stub
.
head
payload
,
err
:=
engine
.
PayloadByNumber
(
ctx
,
block
.
NumberU64
())
payload
,
err
:=
engine
.
PayloadByNumber
(
ctx
,
block
.
NumberU64
())
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
expected
,
err
:=
eth
.
BlockAsPayload
(
block
)
expected
,
err
:=
eth
.
BlockAsPayload
(
block
,
engine
.
rollupCfg
.
CanyonTime
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
payload
)
require
.
Equal
(
t
,
expected
,
payload
)
})
})
...
@@ -124,7 +124,7 @@ func TestSystemConfigByL2Hash(t *testing.T) {
...
@@ -124,7 +124,7 @@ func TestSystemConfigByL2Hash(t *testing.T) {
engine
,
stub
:=
createOracleEngine
(
t
)
engine
,
stub
:=
createOracleEngine
(
t
)
t
.
Run
(
"KnownBlock"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"KnownBlock"
,
func
(
t
*
testing
.
T
)
{
payload
,
err
:=
eth
.
BlockAsPayload
(
stub
.
safe
)
payload
,
err
:=
eth
.
BlockAsPayload
(
stub
.
safe
,
engine
.
rollupCfg
.
CanyonTime
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
expected
,
err
:=
derive
.
PayloadToSystemConfig
(
payload
,
engine
.
rollupCfg
)
expected
,
err
:=
derive
.
PayloadToSystemConfig
(
payload
,
engine
.
rollupCfg
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
...
...
op-program/client/l2/engineapi/l2_engine_api.go
View file @
601de6b6
...
@@ -264,7 +264,7 @@ func (ea *L2EngineAPI) getPayload(ctx context.Context, payloadId eth.PayloadID)
...
@@ -264,7 +264,7 @@ func (ea *L2EngineAPI) getPayload(ctx context.Context, payloadId eth.PayloadID)
ea
.
log
.
Error
(
"failed to finish block building"
,
"err"
,
err
)
ea
.
log
.
Error
(
"failed to finish block building"
,
"err"
,
err
)
return
nil
,
engine
.
UnknownPayload
return
nil
,
engine
.
UnknownPayload
}
}
return
eth
.
BlockAsPayload
(
bl
)
return
eth
.
BlockAsPayload
(
bl
,
ea
.
config
()
.
CanyonTime
)
}
}
func
(
ea
*
L2EngineAPI
)
forkchoiceUpdated
(
ctx
context
.
Context
,
state
*
eth
.
ForkchoiceState
,
attr
*
eth
.
PayloadAttributes
)
(
*
eth
.
ForkchoiceUpdatedResult
,
error
)
{
func
(
ea
*
L2EngineAPI
)
forkchoiceUpdated
(
ctx
context
.
Context
,
state
*
eth
.
ForkchoiceState
,
attr
*
eth
.
PayloadAttributes
)
(
*
eth
.
ForkchoiceUpdatedResult
,
error
)
{
...
@@ -350,6 +350,25 @@ func (ea *L2EngineAPI) forkchoiceUpdated(ctx context.Context, state *eth.Forkcho
...
@@ -350,6 +350,25 @@ func (ea *L2EngineAPI) forkchoiceUpdated(ctx context.Context, state *eth.Forkcho
return
valid
(
nil
),
nil
return
valid
(
nil
),
nil
}
}
func
toGethWithdrawals
(
payload
*
eth
.
ExecutionPayload
)
[]
*
types
.
Withdrawal
{
if
payload
.
Withdrawals
==
nil
{
return
nil
}
result
:=
make
([]
*
types
.
Withdrawal
,
0
,
len
(
*
payload
.
Withdrawals
))
for
_
,
w
:=
range
*
payload
.
Withdrawals
{
result
=
append
(
result
,
&
types
.
Withdrawal
{
Index
:
w
.
Index
,
Validator
:
w
.
Validator
,
Address
:
w
.
Address
,
Amount
:
w
.
Amount
,
})
}
return
result
}
func
(
ea
*
L2EngineAPI
)
newPayload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
)
(
*
eth
.
PayloadStatusV1
,
error
)
{
func
(
ea
*
L2EngineAPI
)
newPayload
(
ctx
context
.
Context
,
payload
*
eth
.
ExecutionPayload
)
(
*
eth
.
PayloadStatusV1
,
error
)
{
ea
.
log
.
Trace
(
"L2Engine API request received"
,
"method"
,
"ExecutePayload"
,
"number"
,
payload
.
BlockNumber
,
"hash"
,
payload
.
BlockHash
)
ea
.
log
.
Trace
(
"L2Engine API request received"
,
"method"
,
"ExecutePayload"
,
"number"
,
payload
.
BlockNumber
,
"hash"
,
payload
.
BlockHash
)
txs
:=
make
([][]
byte
,
len
(
payload
.
Transactions
))
txs
:=
make
([][]
byte
,
len
(
payload
.
Transactions
))
...
@@ -371,6 +390,7 @@ func (ea *L2EngineAPI) newPayload(ctx context.Context, payload *eth.ExecutionPay
...
@@ -371,6 +390,7 @@ func (ea *L2EngineAPI) newPayload(ctx context.Context, payload *eth.ExecutionPay
BaseFeePerGas
:
payload
.
BaseFeePerGas
.
ToBig
(),
BaseFeePerGas
:
payload
.
BaseFeePerGas
.
ToBig
(),
BlockHash
:
payload
.
BlockHash
,
BlockHash
:
payload
.
BlockHash
,
Transactions
:
txs
,
Transactions
:
txs
,
Withdrawals
:
toGethWithdrawals
(
payload
),
},
nil
,
nil
)
},
nil
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Debug
(
"Invalid NewPayload params"
,
"params"
,
payload
,
"error"
,
err
)
log
.
Debug
(
"Invalid NewPayload params"
,
"params"
,
payload
,
"error"
,
err
)
...
...
op-program/client/l2/engineapi/test/l2_engine_api_tests.go
View file @
601de6b6
...
@@ -55,17 +55,25 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -55,17 +55,25 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
txRlp
,
err
:=
tx
.
MarshalBinary
()
txRlp
,
err
:=
tx
.
MarshalBinary
()
api
.
assert
.
NoError
(
err
)
api
.
assert
.
NoError
(
err
)
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
nextBlockTime
:=
eth
.
Uint64Quantity
(
genesis
.
Time
+
1
)
var
w
*
eth
.
Withdrawals
if
api
.
backend
.
Config
()
.
IsCanyon
(
uint64
(
nextBlockTime
))
{
w
=
&
eth
.
Withdrawals
{}
}
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
genesis
.
Hash
(),
HeadBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
},
&
eth
.
PayloadAttributes
{
},
&
eth
.
PayloadAttributes
{
Timestamp
:
eth
.
Uint64Quantity
(
genesis
.
Time
+
1
)
,
Timestamp
:
nextBlockTime
,
PrevRandao
:
eth
.
Bytes32
(
genesis
.
MixDigest
),
PrevRandao
:
eth
.
Bytes32
(
genesis
.
MixDigest
),
SuggestedFeeRecipient
:
feeRecipient
,
SuggestedFeeRecipient
:
feeRecipient
,
Transactions
:
[]
eth
.
Data
{
txRlp
},
Transactions
:
[]
eth
.
Data
{
txRlp
},
NoTxPool
:
true
,
NoTxPool
:
true
,
GasLimit
:
&
gasLimit
,
GasLimit
:
&
gasLimit
,
Withdrawals
:
w
,
})
})
api
.
assert
.
Error
(
err
)
api
.
assert
.
Error
(
err
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
result
.
PayloadStatus
.
Status
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
result
.
PayloadStatus
.
Status
)
...
@@ -103,9 +111,16 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -103,9 +111,16 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
t
.
Run
(
"RejectInvalidBlockHash"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"RejectInvalidBlockHash"
,
func
(
t
*
testing
.
T
)
{
api
:=
newTestHelper
(
t
,
createBackend
)
api
:=
newTestHelper
(
t
,
createBackend
)
var
w
*
eth
.
Withdrawals
if
api
.
backend
.
Config
()
.
IsCanyon
(
uint64
(
0
))
{
w
=
&
eth
.
Withdrawals
{}
}
// Invalid because BlockHash won't be correct (among many other reasons)
// Invalid because BlockHash won't be correct (among many other reasons)
block
:=
&
eth
.
ExecutionPayload
{}
block
:=
&
eth
.
ExecutionPayload
{
r
,
err
:=
api
.
engine
.
NewPayloadV1
(
api
.
ctx
,
block
)
Withdrawals
:
w
,
}
r
,
err
:=
api
.
engine
.
NewPayloadV2
(
api
.
ctx
,
block
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalidBlockHash
,
r
.
Status
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalidBlockHash
,
r
.
Status
)
})
})
...
@@ -122,7 +137,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -122,7 +137,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
newBlock
.
StateRoot
=
eth
.
Bytes32
(
genesis
.
TxHash
)
newBlock
.
StateRoot
=
eth
.
Bytes32
(
genesis
.
TxHash
)
updateBlockHash
(
newBlock
)
updateBlockHash
(
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
1
(
api
.
ctx
,
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
2
(
api
.
ctx
,
newBlock
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
})
})
...
@@ -139,7 +154,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -139,7 +154,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
newBlock
.
Timestamp
=
eth
.
Uint64Quantity
(
genesis
.
Time
)
newBlock
.
Timestamp
=
eth
.
Uint64Quantity
(
genesis
.
Time
)
updateBlockHash
(
newBlock
)
updateBlockHash
(
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
1
(
api
.
ctx
,
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
2
(
api
.
ctx
,
newBlock
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
})
})
...
@@ -156,7 +171,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -156,7 +171,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
newBlock
.
Timestamp
=
eth
.
Uint64Quantity
(
genesis
.
Time
-
1
)
newBlock
.
Timestamp
=
eth
.
Uint64Quantity
(
genesis
.
Time
-
1
)
updateBlockHash
(
newBlock
)
updateBlockHash
(
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
1
(
api
.
ctx
,
newBlock
)
r
,
err
:=
api
.
engine
.
NewPayloadV
2
(
api
.
ctx
,
newBlock
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
NoError
(
err
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
api
.
assert
.
Equal
(
eth
.
ExecutionInvalid
,
r
.
Status
)
})
})
...
@@ -165,7 +180,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -165,7 +180,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
api
:=
newTestHelper
(
t
,
createBackend
)
api
:=
newTestHelper
(
t
,
createBackend
)
genesis
:=
api
.
backend
.
CurrentHeader
()
genesis
:=
api
.
backend
.
CurrentHeader
()
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
genesis
.
Hash
(),
HeadBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
...
@@ -185,7 +200,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -185,7 +200,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
api
:=
newTestHelper
(
t
,
createBackend
)
api
:=
newTestHelper
(
t
,
createBackend
)
genesis
:=
api
.
backend
.
CurrentHeader
()
genesis
:=
api
.
backend
.
CurrentHeader
()
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
genesis
.
Hash
(),
HeadBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
...
@@ -207,7 +222,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -207,7 +222,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
gasLimit
:=
eth
.
Uint64Quantity
(
params
.
MaxGasLimit
+
1
)
gasLimit
:=
eth
.
Uint64Quantity
(
params
.
MaxGasLimit
+
1
)
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
genesis
.
Hash
(),
HeadBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
SafeBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
FinalizedBlockHash
:
genesis
.
Hash
(),
...
@@ -246,7 +261,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -246,7 +261,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
chainB1
:=
api
.
addBlockWithParent
(
genesis
,
eth
.
Uint64Quantity
(
genesis
.
Time
+
3
))
chainB1
:=
api
.
addBlockWithParent
(
genesis
,
eth
.
Uint64Quantity
(
genesis
.
Time
+
3
))
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
chainA3
.
BlockHash
,
HeadBlockHash
:
chainA3
.
BlockHash
,
SafeBlockHash
:
chainB1
.
BlockHash
,
SafeBlockHash
:
chainB1
.
BlockHash
,
FinalizedBlockHash
:
chainA2
.
BlockHash
,
FinalizedBlockHash
:
chainA2
.
BlockHash
,
...
@@ -266,7 +281,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
...
@@ -266,7 +281,7 @@ func RunEngineAPITests(t *testing.T, createBackend func(t *testing.T) engineapi.
chainB1
:=
api
.
addBlockWithParent
(
genesis
,
eth
.
Uint64Quantity
(
genesis
.
Time
+
3
))
chainB1
:=
api
.
addBlockWithParent
(
genesis
,
eth
.
Uint64Quantity
(
genesis
.
Time
+
3
))
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
1
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
api
.
engine
.
ForkchoiceUpdatedV
2
(
api
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
chainA3
.
BlockHash
,
HeadBlockHash
:
chainA3
.
BlockHash
,
SafeBlockHash
:
chainA2
.
BlockHash
,
SafeBlockHash
:
chainA2
.
BlockHash
,
FinalizedBlockHash
:
chainB1
.
BlockHash
,
FinalizedBlockHash
:
chainB1
.
BlockHash
,
...
@@ -349,7 +364,7 @@ func (h *testHelper) addBlockWithParent(head *types.Header, timestamp eth.Uint64
...
@@ -349,7 +364,7 @@ func (h *testHelper) addBlockWithParent(head *types.Header, timestamp eth.Uint64
func
(
h
*
testHelper
)
forkChoiceUpdated
(
head
common
.
Hash
,
safe
common
.
Hash
,
finalized
common
.
Hash
)
{
func
(
h
*
testHelper
)
forkChoiceUpdated
(
head
common
.
Hash
,
safe
common
.
Hash
,
finalized
common
.
Hash
)
{
h
.
Log
(
"forkChoiceUpdated"
,
"head"
,
head
,
"safe"
,
safe
,
"finalized"
,
finalized
)
h
.
Log
(
"forkChoiceUpdated"
,
"head"
,
head
,
"safe"
,
safe
,
"finalized"
,
finalized
)
result
,
err
:=
h
.
engine
.
ForkchoiceUpdatedV
1
(
h
.
ctx
,
&
eth
.
ForkchoiceState
{
result
,
err
:=
h
.
engine
.
ForkchoiceUpdatedV
2
(
h
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
head
,
HeadBlockHash
:
head
,
SafeBlockHash
:
safe
,
SafeBlockHash
:
safe
,
FinalizedBlockHash
:
finalized
,
FinalizedBlockHash
:
finalized
,
...
@@ -368,7 +383,14 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
...
@@ -368,7 +383,14 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
h
.
assert
.
NoError
(
err
,
"Failed to marshall tx %v"
,
tx
)
h
.
assert
.
NoError
(
err
,
"Failed to marshall tx %v"
,
tx
)
txData
=
append
(
txData
,
rlp
)
txData
=
append
(
txData
,
rlp
)
}
}
result
,
err
:=
h
.
engine
.
ForkchoiceUpdatedV1
(
h
.
ctx
,
&
eth
.
ForkchoiceState
{
canyonTime
:=
h
.
backend
.
Config
()
.
CanyonTime
var
w
*
eth
.
Withdrawals
if
canyonTime
!=
nil
&&
*
canyonTime
<=
uint64
(
newBlockTimestamp
)
{
w
=
&
eth
.
Withdrawals
{}
}
result
,
err
:=
h
.
engine
.
ForkchoiceUpdatedV2
(
h
.
ctx
,
&
eth
.
ForkchoiceState
{
HeadBlockHash
:
head
.
Hash
(),
HeadBlockHash
:
head
.
Hash
(),
SafeBlockHash
:
head
.
Hash
(),
SafeBlockHash
:
head
.
Hash
(),
FinalizedBlockHash
:
head
.
Hash
(),
FinalizedBlockHash
:
head
.
Hash
(),
...
@@ -379,6 +401,7 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
...
@@ -379,6 +401,7 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
Transactions
:
txData
,
Transactions
:
txData
,
NoTxPool
:
true
,
NoTxPool
:
true
,
GasLimit
:
&
gasLimit
,
GasLimit
:
&
gasLimit
,
Withdrawals
:
w
,
})
})
h
.
assert
.
NoError
(
err
)
h
.
assert
.
NoError
(
err
)
h
.
assert
.
Equal
(
eth
.
ExecutionValid
,
result
.
PayloadStatus
.
Status
)
h
.
assert
.
Equal
(
eth
.
ExecutionValid
,
result
.
PayloadStatus
.
Status
)
...
@@ -389,15 +412,16 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
...
@@ -389,15 +412,16 @@ func (h *testHelper) startBlockBuilding(head *types.Header, newBlockTimestamp et
func
(
h
*
testHelper
)
getPayload
(
id
*
eth
.
PayloadID
)
*
eth
.
ExecutionPayload
{
func
(
h
*
testHelper
)
getPayload
(
id
*
eth
.
PayloadID
)
*
eth
.
ExecutionPayload
{
h
.
Log
(
"getPayload"
,
"id"
,
id
)
h
.
Log
(
"getPayload"
,
"id"
,
id
)
block
,
err
:=
h
.
engine
.
GetPayloadV1
(
h
.
ctx
,
*
id
)
envelope
,
err
:=
h
.
engine
.
GetPayloadV2
(
h
.
ctx
,
*
id
)
h
.
assert
.
NoError
(
err
)
h
.
assert
.
NoError
(
err
)
h
.
assert
.
NotNil
(
block
)
h
.
assert
.
NotNil
(
envelope
)
return
block
h
.
assert
.
NotNil
(
envelope
.
ExecutionPayload
)
return
envelope
.
ExecutionPayload
}
}
func
(
h
*
testHelper
)
newPayload
(
block
*
eth
.
ExecutionPayload
)
{
func
(
h
*
testHelper
)
newPayload
(
block
*
eth
.
ExecutionPayload
)
{
h
.
Log
(
"newPayload"
,
"hash"
,
block
.
BlockHash
)
h
.
Log
(
"newPayload"
,
"hash"
,
block
.
BlockHash
)
r
,
err
:=
h
.
engine
.
NewPayloadV
1
(
h
.
ctx
,
block
)
r
,
err
:=
h
.
engine
.
NewPayloadV
2
(
h
.
ctx
,
block
)
h
.
assert
.
NoError
(
err
)
h
.
assert
.
NoError
(
err
)
h
.
assert
.
Equal
(
eth
.
ExecutionValid
,
r
.
Status
)
h
.
assert
.
Equal
(
eth
.
ExecutionValid
,
r
.
Status
)
h
.
assert
.
Nil
(
r
.
ValidationError
)
h
.
assert
.
Nil
(
r
.
ValidationError
)
...
...
op-service/Makefile
View file @
601de6b6
...
@@ -15,5 +15,6 @@ generate-mocks:
...
@@ -15,5 +15,6 @@ generate-mocks:
fuzz
:
fuzz
:
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzExecutionPayloadUnmarshal ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzExecutionPayloadUnmarshal ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzExecutionPayloadMarshalUnmarshal ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzExecutionPayloadMarshalUnmarshalV1 ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzExecutionPayloadMarshalUnmarshalV2 ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzOBP01 ./eth
go
test
-run
NOTAREALTEST
-v
-fuzztime
10s
-fuzz
FuzzOBP01 ./eth
op-service/eth/ssz.go
View file @
601de6b6
...
@@ -9,16 +9,33 @@ import (
...
@@ -9,16 +9,33 @@ import (
"sync"
"sync"
)
)
type
BlockVersion
int
const
(
// iota is reset to 0
BlockV1
BlockVersion
=
iota
BlockV2
=
iota
)
// ExecutionPayload is the only SSZ type we have to marshal/unmarshal,
// ExecutionPayload is the only SSZ type we have to marshal/unmarshal,
// so instead of importing a SSZ lib we implement the bare minimum.
// so instead of importing a SSZ lib we implement the bare minimum.
// This is more efficient than RLP, and matches the L1 consensus-layer encoding of ExecutionPayload.
// This is more efficient than RLP, and matches the L1 consensus-layer encoding of ExecutionPayload.
// All fields (4s are offsets to dynamic data)
// All fields (4s are offsets to dynamic data)
const
executionPayloadFixedPart
=
32
+
20
+
32
+
32
+
256
+
32
+
8
+
8
+
8
+
8
+
4
+
32
+
32
+
4
const
blockV1FixedPart
=
32
+
20
+
32
+
32
+
256
+
32
+
8
+
8
+
8
+
8
+
4
+
32
+
32
+
4
// V1 + Withdrawals offset
const
blockV2FixedPart
=
blockV1FixedPart
+
4
const
withdrawalSize
=
8
+
8
+
20
+
8
// MAX_TRANSACTIONS_PER_PAYLOAD in consensus spec
// MAX_TRANSACTIONS_PER_PAYLOAD in consensus spec
// https://github.com/ethereum/consensus-specs/blob/ef434e87165e9a4c82a99f54ffd4974ae113f732/specs/bellatrix/beacon-chain.md#execution
const
maxTransactionsPerPayload
=
1
<<
20
const
maxTransactionsPerPayload
=
1
<<
20
// MAX_WITHDRAWALS_PER_PAYLOAD in consensus spec
// https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#execution
const
maxWithdrawalsPerPayload
=
1
<<
4
// ErrExtraDataTooLarge occurs when the ExecutionPayload's ExtraData field
// ErrExtraDataTooLarge occurs when the ExecutionPayload's ExtraData field
// is too large to be properly represented in SSZ.
// is too large to be properly represented in SSZ.
var
ErrExtraDataTooLarge
=
errors
.
New
(
"extra data too large"
)
var
ErrExtraDataTooLarge
=
errors
.
New
(
"extra data too large"
)
...
@@ -31,16 +48,44 @@ var payloadBufPool = sync.Pool{New: func() any {
...
@@ -31,16 +48,44 @@ var payloadBufPool = sync.Pool{New: func() any {
}}
}}
var
ErrBadTransactionOffset
=
errors
.
New
(
"transactions offset is smaller than extra data offset, aborting"
)
var
ErrBadTransactionOffset
=
errors
.
New
(
"transactions offset is smaller than extra data offset, aborting"
)
var
ErrBadWithdrawalsOffset
=
errors
.
New
(
"withdrawals offset is smaller than transaction offset, aborting"
)
func
executionPayloadFixedPart
(
version
BlockVersion
)
uint32
{
if
version
==
BlockV2
{
return
blockV2FixedPart
}
else
{
return
blockV1FixedPart
}
}
func
(
payload
*
ExecutionPayload
)
inferVersion
()
BlockVersion
{
if
payload
.
Withdrawals
!=
nil
{
return
BlockV2
}
else
{
return
BlockV1
}
}
func
(
payload
*
ExecutionPayload
)
SizeSSZ
()
(
full
uint32
)
{
func
(
payload
*
ExecutionPayload
)
SizeSSZ
()
(
full
uint32
)
{
full
=
executionPayloadFixedPart
+
uint32
(
len
(
payload
.
ExtraData
))
return
executionPayloadFixedPart
(
payload
.
inferVersion
())
+
uint32
(
len
(
payload
.
ExtraData
))
+
payload
.
transactionSize
()
+
payload
.
withdrawalSize
()
}
func
(
payload
*
ExecutionPayload
)
withdrawalSize
()
uint32
{
if
payload
.
Withdrawals
==
nil
{
return
0
}
return
uint32
(
len
(
*
payload
.
Withdrawals
)
*
withdrawalSize
)
}
func
(
payload
*
ExecutionPayload
)
transactionSize
()
uint32
{
// One offset to each transaction
// One offset to each transaction
full
+
=
uint32
(
len
(
payload
.
Transactions
))
*
4
result
:
=
uint32
(
len
(
payload
.
Transactions
))
*
4
// Each transaction
// Each transaction
for
_
,
tx
:=
range
payload
.
Transactions
{
for
_
,
tx
:=
range
payload
.
Transactions
{
full
+=
uint32
(
len
(
tx
))
result
+=
uint32
(
len
(
tx
))
}
}
return
full
return
result
}
}
// marshalBytes32LE returns the value of z as a 32-byte little-endian array.
// marshalBytes32LE returns the value of z as a 32-byte little-endian array.
...
@@ -62,9 +107,13 @@ func unmarshalBytes32LE(in []byte, z *Uint256Quantity) {
...
@@ -62,9 +107,13 @@ func unmarshalBytes32LE(in []byte, z *Uint256Quantity) {
// MarshalSSZ encodes the ExecutionPayload as SSZ type
// MarshalSSZ encodes the ExecutionPayload as SSZ type
func
(
payload
*
ExecutionPayload
)
MarshalSSZ
(
w
io
.
Writer
)
(
n
int
,
err
error
)
{
func
(
payload
*
ExecutionPayload
)
MarshalSSZ
(
w
io
.
Writer
)
(
n
int
,
err
error
)
{
fixedSize
:=
executionPayloadFixedPart
(
payload
.
inferVersion
())
transactionSize
:=
payload
.
transactionSize
()
// Cast to uint32 to enable 32-bit MIPS support where math.MaxUint32-executionPayloadFixedPart is too big for int
// Cast to uint32 to enable 32-bit MIPS support where math.MaxUint32-executionPayloadFixedPart is too big for int
// In that case, len(payload.ExtraData) can't be longer than an int so this is always false anyway.
// In that case, len(payload.ExtraData) can't be longer than an int so this is always false anyway.
if
uint32
(
len
(
payload
.
ExtraData
))
>
math
.
MaxUint32
-
uint32
(
executionPayloadFixedPart
)
{
extraDataSize
:=
uint32
(
len
(
payload
.
ExtraData
))
if
extraDataSize
>
math
.
MaxUint32
-
fixedSize
{
return
0
,
ErrExtraDataTooLarge
return
0
,
ErrExtraDataTooLarge
}
}
...
@@ -100,26 +149,58 @@ func (payload *ExecutionPayload) MarshalSSZ(w io.Writer) (n int, err error) {
...
@@ -100,26 +149,58 @@ func (payload *ExecutionPayload) MarshalSSZ(w io.Writer) (n int, err error) {
binary
.
LittleEndian
.
PutUint64
(
buf
[
offset
:
offset
+
8
],
uint64
(
payload
.
Timestamp
))
binary
.
LittleEndian
.
PutUint64
(
buf
[
offset
:
offset
+
8
],
uint64
(
payload
.
Timestamp
))
offset
+=
8
offset
+=
8
// offset to ExtraData
// offset to ExtraData
binary
.
LittleEndian
.
PutUint32
(
buf
[
offset
:
offset
+
4
],
executionPayloadFixedPart
)
binary
.
LittleEndian
.
PutUint32
(
buf
[
offset
:
offset
+
4
],
fixedSize
)
offset
+=
4
offset
+=
4
marshalBytes32LE
(
buf
[
offset
:
offset
+
32
],
&
payload
.
BaseFeePerGas
)
marshalBytes32LE
(
buf
[
offset
:
offset
+
32
],
&
payload
.
BaseFeePerGas
)
offset
+=
32
offset
+=
32
copy
(
buf
[
offset
:
offset
+
32
],
payload
.
BlockHash
[
:
])
copy
(
buf
[
offset
:
offset
+
32
],
payload
.
BlockHash
[
:
])
offset
+=
32
offset
+=
32
// offset to Transactions
// offset to Transactions
binary
.
LittleEndian
.
PutUint32
(
buf
[
offset
:
offset
+
4
],
executionPayloadFixedPart
+
uint32
(
len
(
payload
.
ExtraData
))
)
binary
.
LittleEndian
.
PutUint32
(
buf
[
offset
:
offset
+
4
],
fixedSize
+
extraDataSize
)
offset
+=
4
offset
+=
4
if
offset
!=
executionPayloadFixedPart
{
panic
(
"fixed part size is inconsistent"
)
if
payload
.
Withdrawals
==
nil
&&
offset
!=
fixedSize
{
panic
(
"transactions - fixed part size is inconsistent"
)
}
if
payload
.
Withdrawals
!=
nil
{
binary
.
LittleEndian
.
PutUint32
(
buf
[
offset
:
offset
+
4
],
fixedSize
+
extraDataSize
+
transactionSize
)
offset
+=
4
if
offset
!=
fixedSize
{
panic
(
"withdrawals - fixed part size is inconsistent"
)
}
}
}
// dynamic value 1: ExtraData
// dynamic value 1: ExtraData
copy
(
buf
[
offset
:
offset
+
uint32
(
len
(
payload
.
ExtraData
))
],
payload
.
ExtraData
[
:
])
copy
(
buf
[
offset
:
offset
+
extraDataSize
],
payload
.
ExtraData
[
:
])
offset
+=
uint32
(
len
(
payload
.
ExtraData
))
offset
+=
extraDataSize
// dynamic value 2: Transactions
// dynamic value 2: Transactions
marshalTransactions
(
buf
[
offset
:
],
payload
.
Transactions
)
marshalTransactions
(
buf
[
offset
:
offset
+
transactionSize
],
payload
.
Transactions
)
offset
+=
transactionSize
// dyanmic value 3: Withdrawals
if
payload
.
Withdrawals
!=
nil
{
marshalWithdrawals
(
buf
[
offset
:
],
payload
.
Withdrawals
)
}
return
w
.
Write
(
buf
)
return
w
.
Write
(
buf
)
}
}
func
marshalWithdrawals
(
out
[]
byte
,
withdrawals
*
Withdrawals
)
{
offset
:=
uint32
(
0
)
for
_
,
withdrawal
:=
range
*
withdrawals
{
binary
.
LittleEndian
.
PutUint64
(
out
[
offset
:
offset
+
8
],
withdrawal
.
Index
)
offset
+=
8
binary
.
LittleEndian
.
PutUint64
(
out
[
offset
:
offset
+
8
],
withdrawal
.
Validator
)
offset
+=
8
copy
(
out
[
offset
:
offset
+
20
],
withdrawal
.
Address
[
:
])
offset
+=
20
binary
.
LittleEndian
.
PutUint64
(
out
[
offset
:
offset
+
8
],
withdrawal
.
Amount
)
offset
+=
8
}
}
func
marshalTransactions
(
out
[]
byte
,
txs
[]
Data
)
{
func
marshalTransactions
(
out
[]
byte
,
txs
[]
Data
)
{
offset
:=
uint32
(
0
)
offset
:=
uint32
(
0
)
txOffset
:=
uint32
(
len
(
txs
))
*
4
txOffset
:=
uint32
(
len
(
txs
))
*
4
...
@@ -133,8 +214,10 @@ func marshalTransactions(out []byte, txs []Data) {
...
@@ -133,8 +214,10 @@ func marshalTransactions(out []byte, txs []Data) {
}
}
// UnmarshalSSZ decodes the ExecutionPayload as SSZ type
// UnmarshalSSZ decodes the ExecutionPayload as SSZ type
func
(
payload
*
ExecutionPayload
)
UnmarshalSSZ
(
scope
uint32
,
r
io
.
Reader
)
error
{
func
(
payload
*
ExecutionPayload
)
UnmarshalSSZ
(
version
BlockVersion
,
scope
uint32
,
r
io
.
Reader
)
error
{
if
scope
<
executionPayloadFixedPart
{
fixedSize
:=
executionPayloadFixedPart
(
version
)
if
scope
<
fixedSize
{
return
fmt
.
Errorf
(
"scope too small to decode execution payload: %d"
,
scope
)
return
fmt
.
Errorf
(
"scope too small to decode execution payload: %d"
,
scope
)
}
}
...
@@ -171,36 +254,99 @@ func (payload *ExecutionPayload) UnmarshalSSZ(scope uint32, r io.Reader) error {
...
@@ -171,36 +254,99 @@ func (payload *ExecutionPayload) UnmarshalSSZ(scope uint32, r io.Reader) error {
payload
.
Timestamp
=
Uint64Quantity
(
binary
.
LittleEndian
.
Uint64
(
buf
[
offset
:
offset
+
8
]))
payload
.
Timestamp
=
Uint64Quantity
(
binary
.
LittleEndian
.
Uint64
(
buf
[
offset
:
offset
+
8
]))
offset
+=
8
offset
+=
8
extraDataOffset
:=
binary
.
LittleEndian
.
Uint32
(
buf
[
offset
:
offset
+
4
])
extraDataOffset
:=
binary
.
LittleEndian
.
Uint32
(
buf
[
offset
:
offset
+
4
])
if
extraDataOffset
!=
executionPayloadFixedPart
{
if
extraDataOffset
!=
fixedSize
{
return
fmt
.
Errorf
(
"unexpected extra data offset: %d <> %d"
,
extraDataOffset
,
executionPayloadFixedPart
)
return
fmt
.
Errorf
(
"unexpected extra data offset: %d <> %d"
,
extraDataOffset
,
fixedSize
)
}
}
offset
+=
4
offset
+=
4
unmarshalBytes32LE
(
buf
[
offset
:
offset
+
32
],
&
payload
.
BaseFeePerGas
)
unmarshalBytes32LE
(
buf
[
offset
:
offset
+
32
],
&
payload
.
BaseFeePerGas
)
offset
+=
32
offset
+=
32
copy
(
payload
.
BlockHash
[
:
],
buf
[
offset
:
offset
+
32
])
copy
(
payload
.
BlockHash
[
:
],
buf
[
offset
:
offset
+
32
])
offset
+=
32
offset
+=
32
transactionsOffset
:=
binary
.
LittleEndian
.
Uint32
(
buf
[
offset
:
offset
+
4
])
transactionsOffset
:=
binary
.
LittleEndian
.
Uint32
(
buf
[
offset
:
offset
+
4
])
if
transactionsOffset
<
extraDataOffset
{
if
transactionsOffset
<
extraDataOffset
{
return
ErrBadTransactionOffset
return
ErrBadTransactionOffset
}
}
offset
+=
4
offset
+=
4
if
offset
!=
executionPayloadFixedPart
{
if
version
==
BlockV1
&&
offset
!=
fixedSize
{
panic
(
"fixed part size is inconsistent"
)
panic
(
"fixed part size is inconsistent"
)
}
}
withdrawalsOffset
:=
scope
if
version
==
BlockV2
{
withdrawalsOffset
=
binary
.
LittleEndian
.
Uint32
(
buf
[
offset
:
offset
+
4
])
// No offset increment, due to this being the last field
if
withdrawalsOffset
<
transactionsOffset
{
return
ErrBadWithdrawalsOffset
}
}
if
transactionsOffset
>
extraDataOffset
+
32
||
transactionsOffset
>
scope
{
if
transactionsOffset
>
extraDataOffset
+
32
||
transactionsOffset
>
scope
{
return
fmt
.
Errorf
(
"extra-data is too large: %d"
,
transactionsOffset
-
extraDataOffset
)
return
fmt
.
Errorf
(
"extra-data is too large: %d"
,
transactionsOffset
-
extraDataOffset
)
}
}
extraDataSize
:=
transactionsOffset
-
extraDataOffset
extraDataSize
:=
transactionsOffset
-
extraDataOffset
payload
.
ExtraData
=
make
(
BytesMax32
,
extraDataSize
)
payload
.
ExtraData
=
make
(
BytesMax32
,
extraDataSize
)
copy
(
payload
.
ExtraData
,
buf
[
extraDataOffset
:
transactionsOffset
])
copy
(
payload
.
ExtraData
,
buf
[
extraDataOffset
:
transactionsOffset
])
txs
,
err
:=
unmarshalTransactions
(
buf
[
transactionsOffset
:
])
txs
,
err
:=
unmarshalTransactions
(
buf
[
transactionsOffset
:
withdrawalsOffset
])
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to unmarshal transactions list: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to unmarshal transactions list: %w"
,
err
)
}
}
payload
.
Transactions
=
txs
payload
.
Transactions
=
txs
if
version
==
BlockV2
{
if
withdrawalsOffset
>
scope
{
return
fmt
.
Errorf
(
"withdrawals offset is too large: %d"
,
withdrawalsOffset
)
}
withdrawals
,
err
:=
unmarshalWithdrawals
(
buf
[
withdrawalsOffset
:
])
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to unmarshal withdrawals list: %w"
,
err
)
}
payload
.
Withdrawals
=
withdrawals
}
return
nil
return
nil
}
}
func
unmarshalWithdrawals
(
in
[]
byte
)
(
*
Withdrawals
,
error
)
{
result
:=
&
Withdrawals
{}
if
len
(
in
)
%
withdrawalSize
!=
0
{
return
nil
,
errors
.
New
(
"invalid withdrawals data"
)
}
withdrawalCount
:=
len
(
in
)
/
withdrawalSize
if
withdrawalCount
>
maxWithdrawalsPerPayload
{
return
nil
,
fmt
.
Errorf
(
"too many withdrawals: %d > %d"
,
withdrawalCount
,
maxWithdrawalsPerPayload
)
}
offset
:=
0
for
i
:=
0
;
i
<
withdrawalCount
;
i
++
{
withdrawal
:=
Withdrawal
{}
withdrawal
.
Index
=
binary
.
LittleEndian
.
Uint64
(
in
[
offset
:
offset
+
8
])
offset
+=
8
withdrawal
.
Validator
=
binary
.
LittleEndian
.
Uint64
(
in
[
offset
:
offset
+
8
])
offset
+=
8
copy
(
withdrawal
.
Address
[
:
],
in
[
offset
:
offset
+
20
])
offset
+=
20
withdrawal
.
Amount
=
binary
.
LittleEndian
.
Uint64
(
in
[
offset
:
offset
+
8
])
offset
+=
8
*
result
=
append
(
*
result
,
withdrawal
)
}
return
result
,
nil
}
func
unmarshalTransactions
(
in
[]
byte
)
(
txs
[]
Data
,
err
error
)
{
func
unmarshalTransactions
(
in
[]
byte
)
(
txs
[]
Data
,
err
error
)
{
scope
:=
uint32
(
len
(
in
))
scope
:=
uint32
(
len
(
in
))
if
scope
==
0
{
// empty txs list
if
scope
==
0
{
// empty txs list
...
...
op-service/eth/ssz_test.go
View file @
601de6b6
...
@@ -3,29 +3,41 @@ package eth
...
@@ -3,29 +3,41 @@ package eth
import
(
import
(
"bytes"
"bytes"
"encoding/binary"
"encoding/binary"
"fmt"
"math"
"math"
"testing"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp"
"github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
)
)
// FuzzExecutionPayloadUnmarshal checks that our SSZ decoding never panics
// FuzzExecutionPayloadUnmarshal checks that our SSZ decoding never panics
func
FuzzExecutionPayloadUnmarshal
(
f
*
testing
.
F
)
{
func
FuzzExecutionPayloadUnmarshal
(
f
*
testing
.
F
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
data
[]
byte
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
data
[]
byte
)
{
{
var
payload
ExecutionPayload
err
:=
payload
.
UnmarshalSSZ
(
BlockV1
,
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
if
err
!=
nil
{
// not every input is a valid ExecutionPayload, that's ok. Should just not panic.
return
}
}
{
var
payload
ExecutionPayload
var
payload
ExecutionPayload
err
:=
payload
.
UnmarshalSSZ
(
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
err
:=
payload
.
UnmarshalSSZ
(
BlockV2
,
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
if
err
!=
nil
{
if
err
!=
nil
{
// not every input is a valid ExecutionPayload, that's ok. Should just not panic.
// not every input is a valid ExecutionPayload, that's ok. Should just not panic.
return
return
}
}
}
})
})
}
}
// FuzzExecutionPayloadMarshalUnmarshal checks that our SSZ encoding>decoding round trips properly
// FuzzExecutionPayloadMarshalUnmarshal checks that our SSZ encoding>decoding round trips properly
func
FuzzExecutionPayloadMarshalUnmarshal
(
f
*
testing
.
F
)
{
func
FuzzExecutionPayloadMarshalUnmarshal
V1
(
f
*
testing
.
F
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
data
[]
byte
,
a
,
b
,
c
,
d
uint64
,
extraData
[]
byte
,
txs
uint16
,
txsData
[]
byte
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
data
[]
byte
,
a
,
b
,
c
,
d
uint64
,
extraData
[]
byte
,
txs
uint16
,
txsData
[]
byte
)
{
if
len
(
data
)
<
32
+
20
+
32
+
32
+
256
+
32
+
32
+
32
{
if
len
(
data
)
<
32
+
20
+
32
+
32
+
256
+
32
+
32
+
32
{
return
return
...
@@ -72,7 +84,77 @@ func FuzzExecutionPayloadMarshalUnmarshal(f *testing.F) {
...
@@ -72,7 +84,77 @@ func FuzzExecutionPayloadMarshalUnmarshal(f *testing.F) {
t
.
Fatalf
(
"failed to marshal ExecutionPayload: %v"
,
err
)
t
.
Fatalf
(
"failed to marshal ExecutionPayload: %v"
,
err
)
}
}
var
roundTripped
ExecutionPayload
var
roundTripped
ExecutionPayload
err
:=
roundTripped
.
UnmarshalSSZ
(
uint32
(
len
(
buf
.
Bytes
())),
bytes
.
NewReader
(
buf
.
Bytes
()))
err
:=
roundTripped
.
UnmarshalSSZ
(
BlockV1
,
uint32
(
len
(
buf
.
Bytes
())),
bytes
.
NewReader
(
buf
.
Bytes
()))
if
err
!=
nil
{
t
.
Fatalf
(
"failed to decode previously marshalled payload: %v"
,
err
)
}
if
diff
:=
cmp
.
Diff
(
payload
,
roundTripped
);
diff
!=
""
{
t
.
Fatalf
(
"The data did not round trip correctly:
\n
%s"
,
diff
)
}
})
}
func
FuzzExecutionPayloadMarshalUnmarshalV2
(
f
*
testing
.
F
)
{
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
data
[]
byte
,
a
,
b
,
c
,
d
uint64
,
extraData
[]
byte
,
txs
uint16
,
txsData
[]
byte
,
wCount
uint16
)
{
if
len
(
data
)
<
32
+
20
+
32
+
32
+
256
+
32
+
32
+
32
{
return
}
var
payload
ExecutionPayload
payload
.
ParentHash
=
*
(
*
common
.
Hash
)(
data
[
:
32
])
data
=
data
[
32
:
]
payload
.
FeeRecipient
=
*
(
*
common
.
Address
)(
data
[
:
20
])
data
=
data
[
20
:
]
payload
.
StateRoot
=
*
(
*
Bytes32
)(
data
[
:
32
])
data
=
data
[
32
:
]
payload
.
ReceiptsRoot
=
*
(
*
Bytes32
)(
data
[
:
32
])
data
=
data
[
32
:
]
payload
.
LogsBloom
=
*
(
*
Bytes256
)(
data
[
:
256
])
data
=
data
[
256
:
]
payload
.
PrevRandao
=
*
(
*
Bytes32
)(
data
[
:
32
])
data
=
data
[
32
:
]
payload
.
BlockNumber
=
Uint64Quantity
(
a
)
payload
.
GasLimit
=
Uint64Quantity
(
a
)
payload
.
GasUsed
=
Uint64Quantity
(
a
)
payload
.
Timestamp
=
Uint64Quantity
(
a
)
if
len
(
extraData
)
>
32
{
extraData
=
extraData
[
:
32
]
}
payload
.
ExtraData
=
extraData
payload
.
BaseFeePerGas
.
SetBytes
(
data
[
:
32
])
payload
.
BlockHash
=
*
(
*
common
.
Hash
)(
data
[
:
32
])
payload
.
Transactions
=
make
([]
Data
,
txs
)
for
i
:=
0
;
i
<
int
(
txs
);
i
++
{
if
len
(
txsData
)
<
2
{
payload
.
Transactions
[
i
]
=
make
(
Data
,
0
)
continue
}
txSize
:=
binary
.
LittleEndian
.
Uint16
(
txsData
[
:
2
])
txsData
=
txsData
[
2
:
]
if
int
(
txSize
)
>
len
(
txsData
)
{
txSize
=
uint16
(
len
(
txsData
))
}
payload
.
Transactions
[
i
]
=
txsData
[
:
txSize
]
txsData
=
txsData
[
txSize
:
]
}
wCount
=
wCount
%
maxWithdrawalsPerPayload
withdrawals
:=
make
(
Withdrawals
,
wCount
)
for
i
:=
0
;
i
<
int
(
wCount
);
i
++
{
withdrawals
[
i
]
=
Withdrawal
{
Index
:
a
,
Validator
:
b
,
Address
:
common
.
BytesToAddress
(
data
[
:
20
]),
Amount
:
c
,
}
}
payload
.
Withdrawals
=
&
withdrawals
var
buf
bytes
.
Buffer
if
_
,
err
:=
payload
.
MarshalSSZ
(
&
buf
);
err
!=
nil
{
t
.
Fatalf
(
"failed to marshal ExecutionPayload: %v"
,
err
)
}
var
roundTripped
ExecutionPayload
err
:=
roundTripped
.
UnmarshalSSZ
(
BlockV2
,
uint32
(
len
(
buf
.
Bytes
())),
bytes
.
NewReader
(
buf
.
Bytes
()))
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"failed to decode previously marshalled payload: %v"
,
err
)
t
.
Fatalf
(
"failed to decode previously marshalled payload: %v"
,
err
)
}
}
...
@@ -99,7 +181,7 @@ func FuzzOBP01(f *testing.F) {
...
@@ -99,7 +181,7 @@ func FuzzOBP01(f *testing.F) {
binary
.
LittleEndian
.
PutUint32
(
clone
[
504
:
508
],
txOffset
)
binary
.
LittleEndian
.
PutUint32
(
clone
[
504
:
508
],
txOffset
)
var
unmarshalled
ExecutionPayload
var
unmarshalled
ExecutionPayload
err
=
unmarshalled
.
UnmarshalSSZ
(
uint32
(
len
(
clone
)),
bytes
.
NewReader
(
clone
))
err
=
unmarshalled
.
UnmarshalSSZ
(
BlockV1
,
uint32
(
len
(
clone
)),
bytes
.
NewReader
(
clone
))
if
err
==
nil
{
if
err
==
nil
{
t
.
Fatalf
(
"expected a failure, but didn't get one"
)
t
.
Fatalf
(
"expected a failure, but didn't get one"
)
}
}
...
@@ -122,7 +204,7 @@ func TestOPB01(t *testing.T) {
...
@@ -122,7 +204,7 @@ func TestOPB01(t *testing.T) {
copy
(
data
[
504
:
508
],
make
([]
byte
,
4
))
copy
(
data
[
504
:
508
],
make
([]
byte
,
4
))
var
unmarshalled
ExecutionPayload
var
unmarshalled
ExecutionPayload
err
=
unmarshalled
.
UnmarshalSSZ
(
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
err
=
unmarshalled
.
UnmarshalSSZ
(
BlockV1
,
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
require
.
Equal
(
t
,
ErrBadTransactionOffset
,
err
)
require
.
Equal
(
t
,
ErrBadTransactionOffset
,
err
)
}
}
...
@@ -130,20 +212,126 @@ func TestOPB01(t *testing.T) {
...
@@ -130,20 +212,126 @@ func TestOPB01(t *testing.T) {
// properly returns an error when the ExtraData field
// properly returns an error when the ExtraData field
// cannot be represented in the outputted SSZ.
// cannot be represented in the outputted SSZ.
func
TestOPB04
(
t
*
testing
.
T
)
{
func
TestOPB04
(
t
*
testing
.
T
)
{
data
:=
make
([]
byte
,
math
.
MaxUint32
)
var
buf
bytes
.
Buffer
// First, test the maximum len - which in this case is the max uint32
// First, test the maximum len - which in this case is the max uint32
// minus the execution payload fixed part.
// minus the execution payload fixed part.
payload
:=
&
ExecutionPayload
{
payload
:=
&
ExecutionPayload
{
ExtraData
:
make
([]
byte
,
math
.
MaxUint32
-
executionPayloadFixedPart
),
ExtraData
:
data
[
:
math
.
MaxUint32
-
executionPayloadFixedPart
(
BlockV1
)],
Withdrawals
:
nil
,
}
}
var
buf
bytes
.
Buffer
_
,
err
:=
payload
.
MarshalSSZ
(
&
buf
)
_
,
err
:=
payload
.
MarshalSSZ
(
&
buf
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
buf
.
Reset
()
buf
.
Reset
()
payload
=
&
ExecutionPayload
{
tests
:=
[]
struct
{
ExtraData
:
make
([]
byte
,
math
.
MaxUint32
-
executionPayloadFixedPart
+
1
),
version
BlockVersion
withdrawals
*
Withdrawals
}{
{
BlockV1
,
nil
},
{
BlockV2
,
&
Withdrawals
{}},
}
}
_
,
err
=
payload
.
MarshalSSZ
(
&
buf
)
for
_
,
test
:=
range
tests
{
payload
:=
&
ExecutionPayload
{
ExtraData
:
data
[
:
math
.
MaxUint32
-
executionPayloadFixedPart
(
test
.
version
)
+
1
],
Withdrawals
:
test
.
withdrawals
,
}
_
,
err
:=
payload
.
MarshalSSZ
(
&
buf
)
require
.
Error
(
t
,
err
)
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
ErrExtraDataTooLarge
,
err
)
require
.
Equal
(
t
,
ErrExtraDataTooLarge
,
err
)
}
}
func
createPayloadWithWithdrawals
(
w
*
Withdrawals
)
*
ExecutionPayload
{
return
&
ExecutionPayload
{
ParentHash
:
common
.
HexToHash
(
"0x123"
),
FeeRecipient
:
common
.
HexToAddress
(
"0x456"
),
StateRoot
:
Bytes32
(
common
.
HexToHash
(
"0x789"
)),
ReceiptsRoot
:
Bytes32
(
common
.
HexToHash
(
"0xabc"
)),
LogsBloom
:
Bytes256
{
byte
(
13
),
byte
(
14
),
byte
(
15
)},
PrevRandao
:
Bytes32
(
common
.
HexToHash
(
"0x111"
)),
BlockNumber
:
Uint64Quantity
(
222
),
GasLimit
:
Uint64Quantity
(
333
),
GasUsed
:
Uint64Quantity
(
444
),
Timestamp
:
Uint64Quantity
(
555
),
ExtraData
:
common
.
Hex2Bytes
(
"0x666"
),
BaseFeePerGas
:
*
uint256
.
NewInt
(
777
),
BlockHash
:
common
.
HexToHash
(
"0x888"
),
Withdrawals
:
w
,
Transactions
:
[]
Data
{
common
.
Hex2Bytes
(
"0x999"
)},
}
}
func
TestMarshalUnmarshalWithdrawals
(
t
*
testing
.
T
)
{
emptyWithdrawal
:=
&
Withdrawals
{}
withdrawals
:=
&
Withdrawals
{
{
Index
:
987
,
Validator
:
654
,
Address
:
common
.
HexToAddress
(
"0x898"
),
Amount
:
321
,
},
}
maxWithdrawals
:=
make
(
Withdrawals
,
maxWithdrawalsPerPayload
)
for
i
:=
0
;
i
<
maxWithdrawalsPerPayload
;
i
++
{
maxWithdrawals
[
i
]
=
Withdrawal
{
Index
:
987
,
Validator
:
654
,
Address
:
common
.
HexToAddress
(
"0x898"
),
Amount
:
321
,
}
}
tooManyWithdrawals
:=
make
(
Withdrawals
,
maxWithdrawalsPerPayload
+
1
)
for
i
:=
0
;
i
<
maxWithdrawalsPerPayload
+
1
;
i
++
{
tooManyWithdrawals
[
i
]
=
Withdrawal
{
Index
:
987
,
Validator
:
654
,
Address
:
common
.
HexToAddress
(
"0x898"
),
Amount
:
321
,
}
}
tests
:=
[]
struct
{
name
string
version
BlockVersion
hasError
bool
withdrawals
*
Withdrawals
}{
{
"ZeroWithdrawalsSucceeds"
,
BlockV2
,
false
,
emptyWithdrawal
},
{
"ZeroWithdrawalsFailsToDeserialize"
,
BlockV1
,
true
,
emptyWithdrawal
},
{
"WithdrawalsSucceeds"
,
BlockV2
,
false
,
withdrawals
},
{
"WithdrawalsFailsToDeserialize"
,
BlockV1
,
true
,
withdrawals
},
{
"MaxWithdrawalsSucceeds"
,
BlockV2
,
false
,
&
maxWithdrawals
},
{
"TooManyWithdrawalsErrors"
,
BlockV2
,
true
,
&
tooManyWithdrawals
},
}
for
_
,
test
:=
range
tests
{
test
:=
test
t
.
Run
(
fmt
.
Sprintf
(
"TestWithdrawalUnmarshalMarshal_%s"
,
test
.
name
),
func
(
t
*
testing
.
T
)
{
input
:=
createPayloadWithWithdrawals
(
test
.
withdrawals
)
var
buf
bytes
.
Buffer
_
,
err
:=
input
.
MarshalSSZ
(
&
buf
)
require
.
NoError
(
t
,
err
)
data
:=
buf
.
Bytes
()
output
:=
&
ExecutionPayload
{}
err
=
output
.
UnmarshalSSZ
(
test
.
version
,
uint32
(
len
(
data
)),
bytes
.
NewReader
(
data
))
if
test
.
hasError
{
require
.
Error
(
t
,
err
)
}
else
{
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
input
,
output
)
if
test
.
withdrawals
!=
nil
{
require
.
Equal
(
t
,
len
(
*
test
.
withdrawals
),
len
(
*
output
.
Withdrawals
))
}
}
})
}
}
}
op-service/eth/types.go
View file @
601de6b6
...
@@ -6,13 +6,13 @@ import (
...
@@ -6,13 +6,13 @@ import (
"math/big"
"math/big"
"reflect"
"reflect"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
)
)
type
ErrorCode
int
type
ErrorCode
int
...
@@ -143,7 +143,7 @@ type ExecutionPayload struct {
...
@@ -143,7 +143,7 @@ type ExecutionPayload struct {
ExtraData
BytesMax32
`json:"extraData"`
ExtraData
BytesMax32
`json:"extraData"`
BaseFeePerGas
Uint256Quantity
`json:"baseFeePerGas"`
BaseFeePerGas
Uint256Quantity
`json:"baseFeePerGas"`
BlockHash
common
.
Hash
`json:"blockHash"`
BlockHash
common
.
Hash
`json:"blockHash"`
Withdrawals
*
[]
Withdrawal
`json:"withdrawals,omitempty"`
Withdrawals
*
Withdrawals
`json:"withdrawals,omitempty"`
// Array of transaction objects, each object is a byte list (DATA) representing
// Array of transaction objects, each object is a byte list (DATA) representing
// TransactionType || TransactionPayload or LegacyTransaction as defined in EIP-2718
// TransactionType || TransactionPayload or LegacyTransaction as defined in EIP-2718
Transactions
[]
Data
`json:"transactions"`
Transactions
[]
Data
`json:"transactions"`
...
@@ -168,6 +168,10 @@ func (s rawTransactions) EncodeIndex(i int, w *bytes.Buffer) {
...
@@ -168,6 +168,10 @@ func (s rawTransactions) EncodeIndex(i int, w *bytes.Buffer) {
w
.
Write
(
s
[
i
])
w
.
Write
(
s
[
i
])
}
}
func
(
payload
*
ExecutionPayload
)
CanyonBlock
()
bool
{
return
payload
.
Withdrawals
!=
nil
}
// CheckBlockHash recomputes the block hash and returns if the embedded block hash matches.
// CheckBlockHash recomputes the block hash and returns if the embedded block hash matches.
func
(
payload
*
ExecutionPayload
)
CheckBlockHash
()
(
actual
common
.
Hash
,
ok
bool
)
{
func
(
payload
*
ExecutionPayload
)
CheckBlockHash
()
(
actual
common
.
Hash
,
ok
bool
)
{
hasher
:=
trie
.
NewStackTrie
(
nil
)
hasher
:=
trie
.
NewStackTrie
(
nil
)
...
@@ -191,11 +195,17 @@ func (payload *ExecutionPayload) CheckBlockHash() (actual common.Hash, ok bool)
...
@@ -191,11 +195,17 @@ func (payload *ExecutionPayload) CheckBlockHash() (actual common.Hash, ok bool)
Nonce
:
types
.
BlockNonce
{},
// zeroed, proof-of-work legacy
Nonce
:
types
.
BlockNonce
{},
// zeroed, proof-of-work legacy
BaseFee
:
payload
.
BaseFeePerGas
.
ToBig
(),
BaseFee
:
payload
.
BaseFeePerGas
.
ToBig
(),
}
}
if
payload
.
CanyonBlock
()
{
withdrawalHash
:=
types
.
DeriveSha
(
*
payload
.
Withdrawals
,
hasher
)
header
.
WithdrawalsHash
=
&
withdrawalHash
}
blockHash
:=
header
.
Hash
()
blockHash
:=
header
.
Hash
()
return
blockHash
,
blockHash
==
payload
.
BlockHash
return
blockHash
,
blockHash
==
payload
.
BlockHash
}
}
func
BlockAsPayload
(
bl
*
types
.
Block
)
(
*
ExecutionPayload
,
error
)
{
func
BlockAsPayload
(
bl
*
types
.
Block
,
canyonForkTime
*
uint64
)
(
*
ExecutionPayload
,
error
)
{
baseFee
,
overflow
:=
uint256
.
FromBig
(
bl
.
BaseFee
())
baseFee
,
overflow
:=
uint256
.
FromBig
(
bl
.
BaseFee
())
if
overflow
{
if
overflow
{
return
nil
,
fmt
.
Errorf
(
"invalid base fee in block: %s"
,
bl
.
BaseFee
())
return
nil
,
fmt
.
Errorf
(
"invalid base fee in block: %s"
,
bl
.
BaseFee
())
...
@@ -208,7 +218,8 @@ func BlockAsPayload(bl *types.Block) (*ExecutionPayload, error) {
...
@@ -208,7 +218,8 @@ func BlockAsPayload(bl *types.Block) (*ExecutionPayload, error) {
}
}
opaqueTxs
[
i
]
=
otx
opaqueTxs
[
i
]
=
otx
}
}
return
&
ExecutionPayload
{
payload
:=
&
ExecutionPayload
{
ParentHash
:
bl
.
ParentHash
(),
ParentHash
:
bl
.
ParentHash
(),
FeeRecipient
:
bl
.
Coinbase
(),
FeeRecipient
:
bl
.
Coinbase
(),
StateRoot
:
Bytes32
(
bl
.
Root
()),
StateRoot
:
Bytes32
(
bl
.
Root
()),
...
@@ -220,10 +231,16 @@ func BlockAsPayload(bl *types.Block) (*ExecutionPayload, error) {
...
@@ -220,10 +231,16 @@ func BlockAsPayload(bl *types.Block) (*ExecutionPayload, error) {
GasUsed
:
Uint64Quantity
(
bl
.
GasUsed
()),
GasUsed
:
Uint64Quantity
(
bl
.
GasUsed
()),
Timestamp
:
Uint64Quantity
(
bl
.
Time
()),
Timestamp
:
Uint64Quantity
(
bl
.
Time
()),
ExtraData
:
bl
.
Extra
(),
ExtraData
:
bl
.
Extra
(),
BaseFeePerGas
:
Uint256Quantity
(
*
baseFee
)
,
BaseFeePerGas
:
*
baseFee
,
BlockHash
:
bl
.
Hash
(),
BlockHash
:
bl
.
Hash
(),
Transactions
:
opaqueTxs
,
Transactions
:
opaqueTxs
,
},
nil
}
if
canyonForkTime
!=
nil
&&
uint64
(
payload
.
Timestamp
)
>=
*
canyonForkTime
{
payload
.
Withdrawals
=
&
Withdrawals
{}
}
return
payload
,
nil
}
}
type
PayloadAttributes
struct
{
type
PayloadAttributes
struct
{
...
@@ -234,7 +251,7 @@ type PayloadAttributes struct {
...
@@ -234,7 +251,7 @@ type PayloadAttributes struct {
// suggested value for the coinbase field of the new payload
// suggested value for the coinbase field of the new payload
SuggestedFeeRecipient
common
.
Address
`json:"suggestedFeeRecipient"`
SuggestedFeeRecipient
common
.
Address
`json:"suggestedFeeRecipient"`
// Withdrawals to include into the block -- should be nil or empty depending on Shanghai enablement
// Withdrawals to include into the block -- should be nil or empty depending on Shanghai enablement
Withdrawals
*
[]
Withdrawal
`json:"withdrawals,omitempty"`
Withdrawals
*
Withdrawals
`json:"withdrawals,omitempty"`
// Transactions to force into the block (always at the start of the transactions list).
// Transactions to force into the block (always at the start of the transactions list).
Transactions
[]
Data
`json:"transactions,omitempty"`
Transactions
[]
Data
`json:"transactions,omitempty"`
// NoTxPool to disable adding any transactions from the transaction-pool.
// NoTxPool to disable adding any transactions from the transaction-pool.
...
@@ -302,9 +319,23 @@ type SystemConfig struct {
...
@@ -302,9 +319,23 @@ type SystemConfig struct {
}
}
// Withdrawal represents a validator withdrawal from the consensus layer.
// Withdrawal represents a validator withdrawal from the consensus layer.
// https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#withdrawal
type
Withdrawal
struct
{
type
Withdrawal
struct
{
Index
uint64
`json:"index"`
// monotonically increasing identifier issued by consensus layer
Index
uint64
`json:"index"`
// monotonically increasing identifier issued by consensus layer
Validator
uint64
`json:"validatorIndex"`
// index of validator associated with withdrawal
Validator
uint64
`json:"validatorIndex"`
// index of validator associated with withdrawal
Address
common
.
Address
`json:"address"`
// target address for withdrawn ether
Address
common
.
Address
`json:"address"`
// target address for withdrawn ether
Amount
uint64
`json:"amount"`
// value of withdrawal in Gwei
Amount
uint64
`json:"amount"`
// value of withdrawal in Gwei
}
}
// Withdrawals implements DerivableList for withdrawals.
type
Withdrawals
[]
Withdrawal
// Len returns the length of s.
func
(
s
Withdrawals
)
Len
()
int
{
return
len
(
s
)
}
// EncodeIndex encodes the i'th withdrawal to w. Note that this does not check for errors
// because we assume that *Withdrawal will only ever contain valid withdrawals that were either
// constructed by decoding or via public API in this package.
func
(
s
Withdrawals
)
EncodeIndex
(
i
int
,
w
*
bytes
.
Buffer
)
{
_
=
rlp
.
Encode
(
w
,
s
[
i
])
}
op-service/sources/types.go
View file @
601de6b6
...
@@ -181,6 +181,7 @@ func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (eth.BlockInfo
...
@@ -181,6 +181,7 @@ func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (eth.BlockInfo
type
rpcBlock
struct
{
type
rpcBlock
struct
{
rpcHeader
rpcHeader
Transactions
[]
*
types
.
Transaction
`json:"transactions"`
Transactions
[]
*
types
.
Transaction
`json:"transactions"`
Withdrawals
*
eth
.
Withdrawals
`json:"withdrawals,omitempty"`
}
}
func
(
block
*
rpcBlock
)
verify
()
error
{
func
(
block
*
rpcBlock
)
verify
()
error
{
...
@@ -252,6 +253,7 @@ func (block *rpcBlock) ExecutionPayload(trustCache bool) (*eth.ExecutionPayload,
...
@@ -252,6 +253,7 @@ func (block *rpcBlock) ExecutionPayload(trustCache bool) (*eth.ExecutionPayload,
BaseFeePerGas
:
baseFee
,
BaseFeePerGas
:
baseFee
,
BlockHash
:
block
.
Hash
,
BlockHash
:
block
.
Hash
,
Transactions
:
opaqueTxs
,
Transactions
:
opaqueTxs
,
Withdrawals
:
block
.
Withdrawals
,
},
nil
},
nil
}
}
...
...
specs/rollup-node-p2p.md
View file @
601de6b6
...
@@ -51,7 +51,8 @@ and are adopted by several other blockchains, most notably the [L1 consensus lay
...
@@ -51,7 +51,8 @@ and are adopted by several other blockchains, most notably the [L1 consensus lay
-
[
Topic configuration
](
#topic-configuration
)
-
[
Topic configuration
](
#topic-configuration
)
-
[
Topic validation
](
#topic-validation
)
-
[
Topic validation
](
#topic-validation
)
-
[
Gossip Topics
](
#gossip-topics
)
-
[
Gossip Topics
](
#gossip-topics
)
-
[
`blocks`
](
#blocks
)
-
[
`blocksv1`
](
#blocksv1
)
-
[
`blocksv2`
](
#blocksv2
)
-
[
Block encoding
](
#block-encoding
)
-
[
Block encoding
](
#block-encoding
)
-
[
Block signatures
](
#block-signatures
)
-
[
Block signatures
](
#block-signatures
)
-
[
Block validation
](
#block-validation
)
-
[
Block validation
](
#block-validation
)
...
@@ -247,9 +248,15 @@ The extended validator emits one of the following validation signals:
...
@@ -247,9 +248,15 @@ The extended validator emits one of the following validation signals:
## Gossip Topics
## Gossip Topics
### `blocks`
There are two topics for distributing blocks to other nodes faster than proxying through L1 would. These are:
The primary topic of the L2, to distribute blocks to other nodes faster than proxying through L1 would.
### `blocksv1`
Pre-Canyon/Shanghai blocks are broadcast on
`/optimism/<chainId>/0/blocks`
.
### `blocksv2`
Post-Canyon/Shanghai blocks are broadcast on
`/optimism/<chainId>/1/blocks`
.
#### Block encoding
#### Block encoding
...
@@ -282,6 +289,8 @@ An [extended-validator] checks the incoming messages as follows, in order of ope
...
@@ -282,6 +289,8 @@ An [extended-validator] checks the incoming messages as follows, in order of ope
(graceful boundary for worst-case propagation and clock skew)
(graceful boundary for worst-case propagation and clock skew)
-
`[REJECT]`
if the
`payload.timestamp`
is more than 5 seconds into the future
-
`[REJECT]`
if the
`payload.timestamp`
is more than 5 seconds into the future
-
`[REJECT]`
if the
`block_hash`
in the
`payload`
is not valid
-
`[REJECT]`
if the
`block_hash`
in the
`payload`
is not valid
-
`[REJECT]`
if the block is on the V1 topic and has withdrawals
-
`[REJECT]`
if the block is on the V2 topic and does not have withdrawals
-
`[REJECT]`
if more than 5 different blocks have been seen with the same block height
-
`[REJECT]`
if more than 5 different blocks have been seen with the same block height
-
`[IGNORE]`
if the block has already been seen
-
`[IGNORE]`
if the block has already been seen
-
`[REJECT]`
if the signature by the sequencer is not valid
-
`[REJECT]`
if the signature by the sequencer is not valid
...
...
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