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
278c7e59
Unverified
Commit
278c7e59
authored
Oct 06, 2022
by
protolambda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
op-e2e: action testing batcher actor
parent
e9628932
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
262 additions
and
0 deletions
+262
-0
l2_batcher.go
op-e2e/actions/l2_batcher.go
+175
-0
l2_batcher_test.go
op-e2e/actions/l2_batcher_test.go
+87
-0
No files found.
op-e2e/actions/l2_batcher.go
0 → 100644
View file @
278c7e59
package
actions
import
(
"bytes"
"context"
"crypto/ecdsa"
"io"
"math/big"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
type
SyncStatusAPI
interface
{
SyncStatus
(
ctx
context
.
Context
)
(
*
eth
.
SyncStatus
,
error
)
}
type
BlocksAPI
interface
{
BlockByNumber
(
ctx
context
.
Context
,
number
*
big
.
Int
)
(
*
types
.
Block
,
error
)
}
type
L1TxAPI
interface
{
PendingNonceAt
(
ctx
context
.
Context
,
account
common
.
Address
)
(
uint64
,
error
)
HeaderByNumber
(
ctx
context
.
Context
,
number
*
big
.
Int
)
(
*
types
.
Header
,
error
)
SendTransaction
(
ctx
context
.
Context
,
tx
*
types
.
Transaction
)
error
}
type
BatcherCfg
struct
{
// Limit the size of txs
MinL1TxSize
uint64
MaxL1TxSize
uint64
BatcherKey
*
ecdsa
.
PrivateKey
}
// L2Batcher buffers and submits L2 batches to L1.
//
// TODO: note the batcher shares little logic/state with actual op-batcher,
// tests should only use this actor to build batch contents for rollup node actors to consume,
// until the op-batcher is refactored and can be covered better.
type
L2Batcher
struct
{
log
log
.
Logger
rollupCfg
*
rollup
.
Config
syncStatusAPI
SyncStatusAPI
l2
BlocksAPI
l1
L1TxAPI
l1Signer
types
.
Signer
l2ChannelOut
*
derive
.
ChannelOut
l2Submitting
bool
// when the channel out is being submitted, and not safe to write to without resetting
l2BufferedBlock
eth
.
BlockID
l2SubmittedBlock
eth
.
BlockID
l2BatcherCfg
*
BatcherCfg
}
func
NewL2Batcher
(
log
log
.
Logger
,
rollupCfg
*
rollup
.
Config
,
batcherCfg
*
BatcherCfg
,
api
SyncStatusAPI
,
l1
L1TxAPI
,
l2
BlocksAPI
)
*
L2Batcher
{
return
&
L2Batcher
{
log
:
log
,
rollupCfg
:
rollupCfg
,
syncStatusAPI
:
api
,
l1
:
l1
,
l2
:
l2
,
l2BatcherCfg
:
batcherCfg
,
l1Signer
:
types
.
LatestSignerForChainID
(
rollupCfg
.
L1ChainID
),
}
}
// SubmittingData indicates if the actor is submitting buffer data.
// All data must be submitted before it can safely continue buffering more L2 blocks.
func
(
s
*
L2Batcher
)
SubmittingData
()
bool
{
return
s
.
l2Submitting
}
// ActL2BatchBuffer adds the next L2 block to the batch buffer.
// If the buffer is being submitted, the buffer is wiped.
func
(
s
*
L2Batcher
)
ActL2BatchBuffer
(
t
Testing
)
{
if
s
.
l2Submitting
{
// break ongoing submitting work if necessary
s
.
l2ChannelOut
=
nil
s
.
l2Submitting
=
false
}
syncStatus
,
err
:=
s
.
syncStatusAPI
.
SyncStatus
(
t
.
Ctx
())
require
.
NoError
(
t
,
err
,
"no sync status error"
)
// If we just started, start at safe-head
if
s
.
l2SubmittedBlock
==
(
eth
.
BlockID
{})
{
s
.
log
.
Info
(
"Starting batch-submitter work at safe-head"
,
"safe"
,
syncStatus
.
SafeL2
)
s
.
l2SubmittedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2BufferedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2ChannelOut
=
nil
}
// If it's lagging behind, catch it up.
if
s
.
l2SubmittedBlock
.
Number
<
syncStatus
.
SafeL2
.
Number
{
s
.
log
.
Warn
(
"last submitted block lagged behind L2 safe head: batch submission will continue from the safe head now"
,
"last"
,
s
.
l2SubmittedBlock
,
"safe"
,
syncStatus
.
SafeL2
)
s
.
l2SubmittedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2BufferedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2ChannelOut
=
nil
}
// Create channel if we don't have one yet
if
s
.
l2ChannelOut
==
nil
{
ch
,
err
:=
derive
.
NewChannelOut
()
require
.
NoError
(
t
,
err
,
"failed to create channel"
)
s
.
l2ChannelOut
=
ch
}
// Add the next unsafe block to the channel
if
s
.
l2BufferedBlock
.
Number
>=
syncStatus
.
UnsafeL2
.
Number
{
return
}
block
,
err
:=
s
.
l2
.
BlockByNumber
(
t
.
Ctx
(),
big
.
NewInt
(
int64
(
s
.
l2BufferedBlock
.
Number
+
1
)))
require
.
NoError
(
t
,
err
,
"need l2 block %d from sync status"
,
s
.
l2SubmittedBlock
.
Number
+
1
)
if
block
.
ParentHash
()
!=
s
.
l2BufferedBlock
.
Hash
{
s
.
log
.
Error
(
"detected a reorg in L2 chain vs previous submitted information, resetting to safe head now"
,
"safe_head"
,
syncStatus
.
SafeL2
)
s
.
l2SubmittedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2BufferedBlock
=
syncStatus
.
SafeL2
.
ID
()
s
.
l2ChannelOut
=
nil
}
if
err
:=
s
.
l2ChannelOut
.
AddBlock
(
block
);
err
!=
nil
{
// should always succeed
t
.
Fatalf
(
"failed to add block to channel: %v"
,
err
)
}
}
// ActL2BatchSubmit constructs a batch tx from previous buffered L2 blocks, and submits it to L1
func
(
s
*
L2Batcher
)
ActL2BatchSubmit
(
t
Testing
)
{
// Don't run this action if there's no data to submit
if
s
.
l2ChannelOut
==
nil
||
s
.
l2ChannelOut
.
ReadyBytes
()
==
0
{
t
.
InvalidAction
(
"need to buffer data first, cannot batch submit with empty buffer"
)
return
}
require
.
NoError
(
t
,
s
.
l2ChannelOut
.
Close
(),
"must close channel before submitting it"
)
// Collect the output frame
data
:=
new
(
bytes
.
Buffer
)
data
.
WriteByte
(
derive
.
DerivationVersion0
)
// subtract one, to account for the version byte
if
err
:=
s
.
l2ChannelOut
.
OutputFrame
(
data
,
s
.
l2BatcherCfg
.
MaxL1TxSize
-
1
);
err
==
io
.
EOF
{
s
.
l2Submitting
=
false
// there may still be some data to submit
}
else
if
err
!=
nil
{
s
.
l2Submitting
=
false
t
.
Fatalf
(
"failed to output channel data to frame: %v"
,
err
)
}
nonce
,
err
:=
s
.
l1
.
PendingNonceAt
(
t
.
Ctx
(),
s
.
rollupCfg
.
BatchSenderAddress
)
require
.
NoError
(
t
,
err
,
"need batcher nonce"
)
gasTipCap
:=
big
.
NewInt
(
2
*
params
.
GWei
)
pendingHeader
,
err
:=
s
.
l1
.
HeaderByNumber
(
t
.
Ctx
(),
big
.
NewInt
(
-
1
))
require
.
NoError
(
t
,
err
,
"need l1 pending header for gas price estimation"
)
gasFeeCap
:=
new
(
big
.
Int
)
.
Add
(
gasTipCap
,
new
(
big
.
Int
)
.
Mul
(
pendingHeader
.
BaseFee
,
big
.
NewInt
(
2
)))
rawTx
:=
&
types
.
DynamicFeeTx
{
ChainID
:
s
.
rollupCfg
.
L1ChainID
,
Nonce
:
nonce
,
To
:
&
s
.
rollupCfg
.
BatchInboxAddress
,
GasTipCap
:
gasTipCap
,
GasFeeCap
:
gasFeeCap
,
Data
:
data
.
Bytes
(),
}
gas
,
err
:=
core
.
IntrinsicGas
(
rawTx
.
Data
,
nil
,
false
,
true
,
true
)
require
.
NoError
(
t
,
err
,
"need to compute intrinsic gas"
)
rawTx
.
Gas
=
gas
tx
,
err
:=
types
.
SignNewTx
(
s
.
l2BatcherCfg
.
BatcherKey
,
s
.
l1Signer
,
rawTx
)
require
.
NoError
(
t
,
err
,
"need to sign tx"
)
err
=
s
.
l1
.
SendTransaction
(
t
.
Ctx
(),
tx
)
require
.
NoError
(
t
,
err
,
"need to send tx"
)
}
op-e2e/actions/l2_batcher_test.go
0 → 100644
View file @
278c7e59
package
actions
import
(
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
)
func
TestBatcher
(
gt
*
testing
.
T
)
{
t
:=
NewDefaultTesting
(
gt
)
p
:=
&
e2eutils
.
TestParams
{
MaxSequencerDrift
:
20
,
// larger than L1 block time we simulate in this test (12)
SequencerWindowSize
:
24
,
ChannelTimeout
:
20
,
}
dp
:=
e2eutils
.
MakeDeployParams
(
t
,
p
)
sd
:=
e2eutils
.
Setup
(
t
,
dp
,
defaultAlloc
)
log
:=
testlog
.
Logger
(
t
,
log
.
LvlDebug
)
miner
,
seqEngine
,
sequencer
:=
setupSequencerTest
(
t
,
sd
,
log
)
verifEngine
,
verifier
:=
setupVerifier
(
t
,
sd
,
log
,
miner
.
L1Client
(
t
,
sd
.
RollupCfg
))
rollupSeqCl
:=
sequencer
.
RollupClient
()
batcher
:=
NewL2Batcher
(
log
,
sd
.
RollupCfg
,
&
BatcherCfg
{
MinL1TxSize
:
0
,
MaxL1TxSize
:
128
_000
,
BatcherKey
:
dp
.
Secrets
.
Batcher
,
},
rollupSeqCl
,
miner
.
EthClient
(),
seqEngine
.
EthClient
())
// Alice makes a L2 tx
cl
:=
seqEngine
.
EthClient
()
n
,
err
:=
cl
.
PendingNonceAt
(
t
.
Ctx
(),
dp
.
Addresses
.
Alice
)
require
.
NoError
(
t
,
err
)
signer
:=
types
.
LatestSigner
(
sd
.
L2Cfg
.
Config
)
tx
:=
types
.
MustSignNewTx
(
dp
.
Secrets
.
Alice
,
signer
,
&
types
.
DynamicFeeTx
{
ChainID
:
sd
.
L2Cfg
.
Config
.
ChainID
,
Nonce
:
n
,
GasTipCap
:
big
.
NewInt
(
2
*
params
.
GWei
),
GasFeeCap
:
new
(
big
.
Int
)
.
Add
(
miner
.
l1Chain
.
CurrentBlock
()
.
BaseFee
(),
big
.
NewInt
(
2
*
params
.
GWei
)),
Gas
:
params
.
TxGas
,
To
:
&
dp
.
Addresses
.
Bob
,
Value
:
e2eutils
.
Ether
(
2
),
})
require
.
NoError
(
gt
,
cl
.
SendTransaction
(
t
.
Ctx
(),
tx
))
sequencer
.
ActL2PipelineFull
(
t
)
verifier
.
ActL2PipelineFull
(
t
)
// Make L2 block
sequencer
.
ActL2StartBlock
(
t
)
seqEngine
.
ActL2IncludeTx
(
dp
.
Addresses
.
Alice
)(
t
)
sequencer
.
ActL2EndBlock
(
t
)
// batch submit to L1
batcher
.
ActL2BatchBuffer
(
t
)
batcher
.
ActL2BatchSubmit
(
t
)
// confirm batch on L1
miner
.
ActL1StartBlock
(
12
)(
t
)
miner
.
ActL1IncludeTx
(
dp
.
Addresses
.
Batcher
)(
t
)
miner
.
ActL1EndBlock
(
t
)
bl
:=
miner
.
l1Chain
.
CurrentBlock
()
log
.
Info
(
"bl"
,
"txs"
,
len
(
bl
.
Transactions
()))
// Now make enough L1 blocks that the verifier will have to derive a L2 block
for
i
:=
uint64
(
1
);
i
<
sd
.
RollupCfg
.
SeqWindowSize
;
i
++
{
miner
.
ActL1StartBlock
(
12
)(
t
)
miner
.
ActL1EndBlock
(
t
)
}
// sync verifier from L1 batch in otherwise empty sequence window
verifier
.
ActL1HeadSignal
(
t
)
verifier
.
ActL2PipelineFull
(
t
)
require
.
Equal
(
t
,
uint64
(
1
),
verifier
.
SyncStatus
()
.
SafeL2
.
L1Origin
.
Number
)
// check that the tx from alice made it into the L2 chain
verifCl
:=
verifEngine
.
EthClient
()
vTx
,
isPending
,
err
:=
verifCl
.
TransactionByHash
(
t
.
Ctx
(),
tx
.
Hash
())
require
.
NoError
(
t
,
err
)
require
.
False
(
t
,
isPending
)
require
.
NotNil
(
t
,
vTx
)
}
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