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
008a5921
Unverified
Commit
008a5921
authored
Oct 14, 2022
by
mergify[bot]
Committed by
GitHub
Oct 14, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into jg/doctx_in_dial
parents
69a19fa1
c930dcc2
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
421 additions
and
1 deletion
+421
-1
l1_miner.go
op-e2e/actions/l1_miner.go
+1
-0
l2_engine.go
op-e2e/actions/l2_engine.go
+1
-0
user.go
op-e2e/actions/user.go
+336
-0
user_test.go
op-e2e/actions/user_test.go
+82
-0
sequencer.go
op-node/rollup/driver/sequencer.go
+1
-1
No files found.
op-e2e/actions/l1_miner.go
View file @
008a5921
...
...
@@ -106,6 +106,7 @@ func (s *L1Miner) ActL1IncludeTx(from common.Address) Action {
return
}
s
.
pendingIndices
[
from
]
=
i
+
1
// won't retry the tx
s
.
l1BuildingState
.
Prepare
(
tx
.
Hash
(),
len
(
s
.
l1Transactions
))
receipt
,
err
:=
core
.
ApplyTransaction
(
s
.
l1Cfg
.
Config
,
s
.
l1Chain
,
&
s
.
l1BuildingHeader
.
Coinbase
,
s
.
l1GasPool
,
s
.
l1BuildingState
,
s
.
l1BuildingHeader
,
tx
,
&
s
.
l1BuildingHeader
.
GasUsed
,
*
s
.
l1Chain
.
GetVMConfig
())
if
err
!=
nil
{
...
...
op-e2e/actions/l2_engine.go
View file @
008a5921
...
...
@@ -174,6 +174,7 @@ func (e *L2Engine) ActL2IncludeTx(from common.Address) Action {
return
}
e
.
pendingIndices
[
from
]
=
i
+
1
// won't retry the tx
e
.
l2BuildingState
.
Prepare
(
tx
.
Hash
(),
len
(
e
.
l2Transactions
))
receipt
,
err
:=
core
.
ApplyTransaction
(
e
.
l2Cfg
.
Config
,
e
.
l2Chain
,
&
e
.
l2BuildingHeader
.
Coinbase
,
e
.
l2GasPool
,
e
.
l2BuildingState
,
e
.
l2BuildingHeader
,
tx
,
&
e
.
l2BuildingHeader
.
GasUsed
,
*
e
.
l2Chain
.
GetVMConfig
())
if
err
!=
nil
{
...
...
op-e2e/actions/user.go
0 → 100644
View file @
008a5921
package
actions
import
(
"crypto/ecdsa"
"errors"
"math/big"
"math/rand"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
)
type
L1Bindings
struct
{
// contract bindings
OptimismPortal
*
bindings
.
OptimismPortal
L2OutputOracle
*
bindings
.
L2OutputOracle
}
func
NewL1Bindings
(
t
Testing
,
l1Cl
*
ethclient
.
Client
,
deployments
*
e2eutils
.
DeploymentsL1
)
*
L1Bindings
{
optimismPortal
,
err
:=
bindings
.
NewOptimismPortal
(
deployments
.
OptimismPortalProxy
,
l1Cl
)
require
.
NoError
(
t
,
err
)
l2OutputOracle
,
err
:=
bindings
.
NewL2OutputOracle
(
deployments
.
L2OutputOracleProxy
,
l1Cl
)
require
.
NoError
(
t
,
err
)
return
&
L1Bindings
{
OptimismPortal
:
optimismPortal
,
L2OutputOracle
:
l2OutputOracle
,
}
}
type
L2Bindings
struct
{
L2ToL1MessagePasser
*
bindings
.
L2ToL1MessagePasser
WithdrawalsClient
*
withdrawals
.
Client
}
func
NewL2Bindings
(
t
Testing
,
l2Cl
*
ethclient
.
Client
,
withdrawalsCl
*
withdrawals
.
Client
)
*
L2Bindings
{
l2ToL1MessagePasser
,
err
:=
bindings
.
NewL2ToL1MessagePasser
(
predeploys
.
L2ToL1MessagePasserAddr
,
l2Cl
)
require
.
NoError
(
t
,
err
)
return
&
L2Bindings
{
L2ToL1MessagePasser
:
l2ToL1MessagePasser
,
WithdrawalsClient
:
withdrawalsCl
,
}
}
// BasicUserEnv provides access to the eth RPC, signer, and contract bindings for a single ethereum layer.
// This environment can be shared between different BasicUser instances.
type
BasicUserEnv
[
B
any
]
struct
{
EthCl
*
ethclient
.
Client
Signer
types
.
Signer
AddressCorpora
[]
common
.
Address
Bindings
B
}
// BasicUser is an actor on a single ethereum layer, with one account key.
// The user maintains a set of standard txOpts to build its transactions with,
// along with configurable txToAddr and txCallData.
// The user has an RNG source with actions to randomize its transaction building.
type
BasicUser
[
B
any
]
struct
{
log
log
.
Logger
rng
*
rand
.
Rand
env
*
BasicUserEnv
[
B
]
account
*
ecdsa
.
PrivateKey
address
common
.
Address
txOpts
bind
.
TransactOpts
txToAddr
*
common
.
Address
txCallData
[]
byte
// lastTxHash persists the last transaction,
// so we can chain together tx sending and tx checking easily.
// Sending and checking are detached, since txs may not be instantly confirmed.
lastTxHash
common
.
Hash
}
func
NewBasicUser
[
B
any
](
log
log
.
Logger
,
priv
*
ecdsa
.
PrivateKey
,
rng
*
rand
.
Rand
)
*
BasicUser
[
B
]
{
return
&
BasicUser
[
B
]{
log
:
log
,
rng
:
rng
,
account
:
priv
,
address
:
crypto
.
PubkeyToAddress
(
priv
.
PublicKey
),
}
}
// SetUserEnv changes the user environment.
// This way a user can be initialized before being embedded in a genesis allocation,
// and change between different endpoints that may be initialized after the user.
func
(
s
*
BasicUser
[
B
])
SetUserEnv
(
env
*
BasicUserEnv
[
B
])
{
s
.
env
=
env
}
func
(
s
*
BasicUser
[
B
])
signerFn
(
address
common
.
Address
,
tx
*
types
.
Transaction
)
(
*
types
.
Transaction
,
error
)
{
if
address
!=
s
.
address
{
return
nil
,
bind
.
ErrNotAuthorized
}
signature
,
err
:=
crypto
.
Sign
(
s
.
env
.
Signer
.
Hash
(
tx
)
.
Bytes
(),
s
.
account
)
if
err
!=
nil
{
return
nil
,
err
}
return
tx
.
WithSignature
(
s
.
env
.
Signer
,
signature
)
}
// ActResetTxOpts prepares the tx options to default values, based on the current pending block header.
func
(
s
*
BasicUser
[
B
])
ActResetTxOpts
(
t
Testing
)
{
pendingHeader
,
err
:=
s
.
env
.
EthCl
.
HeaderByNumber
(
t
.
Ctx
(),
big
.
NewInt
(
-
1
))
require
.
NoError
(
t
,
err
,
"need l2 pending header for accurate basefee info"
)
gasTipCap
:=
big
.
NewInt
(
2
*
params
.
GWei
)
gasFeeCap
:=
new
(
big
.
Int
)
.
Add
(
gasTipCap
,
new
(
big
.
Int
)
.
Mul
(
pendingHeader
.
BaseFee
,
big
.
NewInt
(
2
)))
s
.
txOpts
=
bind
.
TransactOpts
{
From
:
s
.
address
,
Nonce
:
nil
,
// pick nonce based on pending state
Signer
:
s
.
signerFn
,
Value
:
big
.
NewInt
(
0
),
GasFeeCap
:
gasFeeCap
,
GasTipCap
:
gasTipCap
,
GasLimit
:
0
,
// a.k.a. estimate
NoSend
:
true
,
// actions should be explicit about sending
}
}
func
(
s
*
BasicUser
[
B
])
ActRandomTxToAddr
(
t
Testing
)
{
i
:=
s
.
rng
.
Intn
(
len
(
s
.
env
.
AddressCorpora
))
var
to
*
common
.
Address
if
i
>
0
{
// 0 == nil
to
=
&
s
.
env
.
AddressCorpora
[
i
]
}
s
.
txToAddr
=
to
}
func
(
s
*
BasicUser
[
B
])
ActSetTxToAddr
(
to
*
common
.
Address
)
Action
{
return
func
(
t
Testing
)
{
s
.
txToAddr
=
to
}
}
func
(
s
*
BasicUser
[
B
])
ActRandomTxValue
(
t
Testing
)
{
// compute a random portion of balance
precision
:=
int64
(
1000
)
bal
,
err
:=
s
.
env
.
EthCl
.
BalanceAt
(
t
.
Ctx
(),
s
.
address
,
nil
)
require
.
NoError
(
t
,
err
)
part
:=
big
.
NewInt
(
s
.
rng
.
Int63n
(
precision
))
new
(
big
.
Int
)
.
Div
(
new
(
big
.
Int
)
.
Mul
(
bal
,
part
),
big
.
NewInt
(
precision
))
s
.
txOpts
.
Value
=
big
.
NewInt
(
s
.
rng
.
Int63
())
}
func
(
s
*
BasicUser
[
B
])
ActRandomTxData
(
t
Testing
)
{
dataLen
:=
s
.
rng
.
Intn
(
128
_000
)
out
:=
make
([]
byte
,
dataLen
)
_
,
err
:=
s
.
rng
.
Read
(
out
[
:
])
require
.
NoError
(
t
,
err
)
s
.
txCallData
=
out
}
func
(
s
*
BasicUser
[
B
])
PendingNonce
(
t
Testing
)
uint64
{
if
s
.
txOpts
.
Nonce
!=
nil
{
return
s
.
txOpts
.
Nonce
.
Uint64
()
}
// fetch from pending state
nonce
,
err
:=
s
.
env
.
EthCl
.
PendingNonceAt
(
t
.
Ctx
(),
s
.
address
)
require
.
NoError
(
t
,
err
,
"failed to get L1 nonce for account %s"
,
s
.
address
)
return
nonce
}
func
(
s
*
BasicUser
[
B
])
TxValue
()
*
big
.
Int
{
if
s
.
txOpts
.
Value
!=
nil
{
return
s
.
txOpts
.
Value
}
return
big
.
NewInt
(
0
)
}
// ActMakeTx makes a tx with the predetermined contents (see randomization and other actions)
// and sends it to the tx pool
func
(
s
*
BasicUser
[
B
])
ActMakeTx
(
t
Testing
)
{
gas
,
err
:=
s
.
env
.
EthCl
.
EstimateGas
(
t
.
Ctx
(),
ethereum
.
CallMsg
{
From
:
s
.
address
,
To
:
s
.
txToAddr
,
GasFeeCap
:
s
.
txOpts
.
GasFeeCap
,
GasTipCap
:
s
.
txOpts
.
GasTipCap
,
Value
:
s
.
TxValue
(),
Data
:
s
.
txCallData
,
})
require
.
NoError
(
t
,
err
,
"gas estimation should pass"
)
tx
:=
types
.
MustSignNewTx
(
s
.
account
,
s
.
env
.
Signer
,
&
types
.
DynamicFeeTx
{
To
:
s
.
txToAddr
,
GasFeeCap
:
s
.
txOpts
.
GasFeeCap
,
GasTipCap
:
s
.
txOpts
.
GasTipCap
,
Value
:
s
.
TxValue
(),
ChainID
:
s
.
env
.
Signer
.
ChainID
(),
Nonce
:
s
.
PendingNonce
(
t
),
Gas
:
gas
,
})
err
=
s
.
env
.
EthCl
.
SendTransaction
(
t
.
Ctx
(),
tx
)
require
.
NoError
(
t
,
err
,
"must send tx"
)
s
.
lastTxHash
=
tx
.
Hash
()
}
func
(
s
*
BasicUser
[
B
])
ActCheckReceiptStatusOfLastTx
(
success
bool
)
func
(
t
Testing
)
{
return
func
(
t
Testing
)
{
s
.
CheckReceipt
(
t
,
success
,
s
.
lastTxHash
)
}
}
func
(
s
*
BasicUser
[
B
])
CheckReceipt
(
t
Testing
,
success
bool
,
txHash
common
.
Hash
)
*
types
.
Receipt
{
receipt
,
err
:=
s
.
env
.
EthCl
.
TransactionReceipt
(
t
.
Ctx
(),
txHash
)
if
receipt
!=
nil
&&
err
==
nil
{
expected
:=
types
.
ReceiptStatusFailed
if
success
{
expected
=
types
.
ReceiptStatusSuccessful
}
require
.
Equal
(
t
,
expected
,
receipt
.
Status
,
"expected receipt status to match"
)
return
receipt
}
else
if
err
!=
nil
&&
!
errors
.
Is
(
err
,
ethereum
.
NotFound
)
{
t
.
Fatalf
(
"receipt for tx %s was not found"
,
txHash
)
}
else
{
t
.
Fatalf
(
"receipt error: %v"
,
err
)
}
return
nil
}
type
L1User
struct
{
BasicUser
[
*
L1Bindings
]
}
type
L2User
struct
{
BasicUser
[
*
L2Bindings
]
}
// CrossLayerUser represents the same user account on L1 and L2,
// and provides actions to make cross-layer transactions.
type
CrossLayerUser
struct
{
L1
L1User
L2
L2User
// track the last deposit, to easily chain together deposit actions
lastL1DepositTxHash
common
.
Hash
}
func
NewCrossLayerUser
(
log
log
.
Logger
,
priv
*
ecdsa
.
PrivateKey
,
rng
*
rand
.
Rand
)
*
CrossLayerUser
{
addr
:=
crypto
.
PubkeyToAddress
(
priv
.
PublicKey
)
return
&
CrossLayerUser
{
L1
:
L1User
{
BasicUser
:
BasicUser
[
*
L1Bindings
]{
log
:
log
,
rng
:
rng
,
account
:
priv
,
address
:
addr
,
},
},
L2
:
L2User
{
BasicUser
:
BasicUser
[
*
L2Bindings
]{
log
:
log
,
rng
:
rng
,
account
:
priv
,
address
:
addr
,
},
},
}
}
func
(
s
*
CrossLayerUser
)
ActDeposit
(
t
Testing
)
{
isCreation
:=
false
toAddr
:=
common
.
Address
{}
if
s
.
L2
.
txToAddr
==
nil
{
isCreation
=
true
}
else
{
toAddr
=
*
s
.
L2
.
txToAddr
}
depositTransferValue
:=
s
.
L2
.
TxValue
()
depositGas
:=
s
.
L2
.
txOpts
.
GasLimit
if
s
.
L2
.
txOpts
.
GasLimit
==
0
{
// estimate gas used by deposit
gas
,
err
:=
s
.
L2
.
env
.
EthCl
.
EstimateGas
(
t
.
Ctx
(),
ethereum
.
CallMsg
{
From
:
s
.
L2
.
address
,
To
:
s
.
L2
.
txToAddr
,
Value
:
depositTransferValue
,
// TODO: estimate gas does not support minting yet
Data
:
s
.
L2
.
txCallData
,
AccessList
:
nil
,
})
require
.
NoError
(
t
,
err
)
depositGas
=
gas
}
tx
,
err
:=
s
.
L1
.
env
.
Bindings
.
OptimismPortal
.
DepositTransaction
(
&
s
.
L1
.
txOpts
,
toAddr
,
depositTransferValue
,
depositGas
,
isCreation
,
s
.
L2
.
txCallData
)
require
.
NoError
(
t
,
err
,
"failed to create deposit tx"
)
// Send the actual tx (since tx opts don't send by default)
err
=
s
.
L1
.
env
.
EthCl
.
SendTransaction
(
t
.
Ctx
(),
tx
)
require
.
NoError
(
t
,
err
,
"must send tx"
)
s
.
lastL1DepositTxHash
=
tx
.
Hash
()
}
func
(
s
*
CrossLayerUser
)
ActCheckDepositStatus
(
l1Success
,
l2Success
bool
)
Action
{
return
func
(
t
Testing
)
{
s
.
CheckDepositTx
(
t
,
s
.
lastL1DepositTxHash
,
0
,
l1Success
,
l2Success
)
}
}
func
(
s
*
CrossLayerUser
)
CheckDepositTx
(
t
Testing
,
l1TxHash
common
.
Hash
,
index
int
,
l1Success
,
l2Success
bool
)
{
depositReceipt
:=
s
.
L1
.
CheckReceipt
(
t
,
l1Success
,
l1TxHash
)
if
depositReceipt
==
nil
{
require
.
False
(
t
,
l1Success
)
require
.
False
(
t
,
l2Success
)
}
else
{
require
.
Less
(
t
,
index
,
len
(
depositReceipt
.
Logs
),
"must have enough logs in receipt"
)
reconstructedDep
,
err
:=
derive
.
UnmarshalDepositLogEvent
(
depositReceipt
.
Logs
[
index
])
require
.
NoError
(
t
,
err
,
"Could not reconstruct L2 Deposit"
)
l2Tx
:=
types
.
NewTx
(
reconstructedDep
)
s
.
L2
.
CheckReceipt
(
t
,
l2Success
,
l2Tx
.
Hash
())
}
}
func
(
s
*
CrossLayerUser
)
Address
()
common
.
Address
{
return
s
.
L1
.
address
}
op-e2e/actions/user_test.go
0 → 100644
View file @
008a5921
package
actions
import
(
"math/rand"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
)
func
TestCrossLayerUser
(
gt
*
testing
.
T
)
{
t
:=
NewDefaultTesting
(
gt
)
dp
:=
e2eutils
.
MakeDeployParams
(
t
,
defaultRollupTestParams
)
sd
:=
e2eutils
.
Setup
(
t
,
dp
,
defaultAlloc
)
log
:=
testlog
.
Logger
(
t
,
log
.
LvlDebug
)
miner
,
seqEngine
,
seq
:=
setupSequencerTest
(
t
,
sd
,
log
)
// need to start derivation before we can make L2 blocks
seq
.
ActL2PipelineFull
(
t
)
l1Cl
:=
miner
.
EthClient
()
l2Cl
:=
seqEngine
.
EthClient
()
withdrawalsCl
:=
&
withdrawals
.
Client
{}
// TODO: need a rollup node actor to wrap for output root proof RPC
addresses
:=
e2eutils
.
CollectAddresses
(
sd
,
dp
)
l1UserEnv
:=
&
BasicUserEnv
[
*
L1Bindings
]{
EthCl
:
l1Cl
,
Signer
:
types
.
LatestSigner
(
sd
.
L1Cfg
.
Config
),
AddressCorpora
:
addresses
,
Bindings
:
NewL1Bindings
(
t
,
l1Cl
,
&
sd
.
DeploymentsL1
),
}
l2UserEnv
:=
&
BasicUserEnv
[
*
L2Bindings
]{
EthCl
:
l2Cl
,
Signer
:
types
.
LatestSigner
(
sd
.
L2Cfg
.
Config
),
AddressCorpora
:
addresses
,
Bindings
:
NewL2Bindings
(
t
,
l2Cl
,
withdrawalsCl
),
}
alice
:=
NewCrossLayerUser
(
log
,
dp
.
Secrets
.
Alice
,
rand
.
New
(
rand
.
NewSource
(
1234
)))
alice
.
L1
.
SetUserEnv
(
l1UserEnv
)
alice
.
L2
.
SetUserEnv
(
l2UserEnv
)
// regular L2 tx, in new L2 block
alice
.
L2
.
ActResetTxOpts
(
t
)
alice
.
L2
.
ActSetTxToAddr
(
&
dp
.
Addresses
.
Bob
)(
t
)
alice
.
L2
.
ActMakeTx
(
t
)
seq
.
ActL2StartBlock
(
t
)
seqEngine
.
ActL2IncludeTx
(
alice
.
Address
())(
t
)
seq
.
ActL2EndBlock
(
t
)
alice
.
L2
.
ActCheckReceiptStatusOfLastTx
(
true
)(
t
)
// regular L1 tx, in new L1 block
alice
.
L1
.
ActResetTxOpts
(
t
)
alice
.
L1
.
ActSetTxToAddr
(
&
dp
.
Addresses
.
Bob
)(
t
)
alice
.
L1
.
ActMakeTx
(
t
)
miner
.
ActL1StartBlock
(
12
)(
t
)
miner
.
ActL1IncludeTx
(
alice
.
Address
())(
t
)
miner
.
ActL1EndBlock
(
t
)
alice
.
L1
.
ActCheckReceiptStatusOfLastTx
(
true
)(
t
)
// regular Deposit, in new L1 block
alice
.
ActDeposit
(
t
)
miner
.
ActL1StartBlock
(
12
)(
t
)
miner
.
ActL1IncludeTx
(
alice
.
Address
())(
t
)
miner
.
ActL1EndBlock
(
t
)
seq
.
ActL1HeadSignal
(
t
)
// sync sequencer build enough blocks to adopt latest L1 origin
for
seq
.
SyncStatus
()
.
UnsafeL2
.
L1Origin
.
Number
<
miner
.
l1Chain
.
CurrentBlock
()
.
NumberU64
()
{
seq
.
ActL2StartBlock
(
t
)
seq
.
ActL2EndBlock
(
t
)
}
// Now that the L2 chain adopted the latest L1 block, check that we processed the deposit
alice
.
ActCheckDepositStatus
(
true
,
true
)(
t
)
}
op-node/rollup/driver/sequencer.go
View file @
008a5921
...
...
@@ -91,6 +91,7 @@ func (d *Sequencer) CompleteBuildingBlock(ctx context.Context) (*eth.ExecutionPa
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to complete building on top of L2 chain %s, error (%d): %w"
,
d
.
buildingOnto
.
HeadBlockHash
,
errTyp
,
err
)
}
d
.
buildingID
=
eth
.
PayloadID
{}
return
payload
,
nil
}
...
...
@@ -103,7 +104,6 @@ func (d *Sequencer) CreateNewBlock(ctx context.Context, l2Head eth.L2BlockRef, l
if
err
!=
nil
{
return
l2Head
,
nil
,
err
}
d
.
buildingID
=
eth
.
PayloadID
{}
// Generate an L2 block ref from the payload.
ref
,
err
:=
derive
.
PayloadToBlockRef
(
payload
,
&
d
.
config
.
Genesis
)
...
...
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