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
096d9c2b
Unverified
Commit
096d9c2b
authored
Feb 23, 2023
by
Matthew Slipper
Committed by
GitHub
Feb 23, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4961 from ethereum-optimism/regolith-sys-deposits
op-node: regolith fork systemtx change
parents
0c1f45e1
68e2b490
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
81 additions
and
16 deletions
+81
-16
op_geth_test.go
op-e2e/op_geth_test.go
+1
-1
attributes.go
op-node/rollup/derive/attributes.go
+1
-1
attributes_queue_test.go
op-node/rollup/derive/attributes_queue_test.go
+1
-1
attributes_test.go
op-node/rollup/derive/attributes_test.go
+50
-3
l1_block_info.go
op-node/rollup/derive/l1_block_info.go
+15
-5
l1_block_info_test.go
op-node/rollup/derive/l1_block_info_test.go
+10
-2
l1_block_info_tob_test.go
op-node/rollup/derive/l1_block_info_tob_test.go
+2
-2
sequencer_test.go
op-node/rollup/driver/sequencer_test.go
+1
-1
No files found.
op-e2e/op_geth_test.go
View file @
096d9c2b
...
@@ -147,7 +147,7 @@ func TestInvalidDepositInFCU(t *testing.T) {
...
@@ -147,7 +147,7 @@ func TestInvalidDepositInFCU(t *testing.T) {
require
.
Nil
(
t
,
err
)
require
.
Nil
(
t
,
err
)
// Create the test data (L1 Info Tx and then always failing deposit)
// Create the test data (L1 Info Tx and then always failing deposit)
l1Info
,
err
:=
derive
.
L1InfoDepositBytes
(
1
,
l1Block
,
rollupGenesis
.
SystemConfig
)
l1Info
,
err
:=
derive
.
L1InfoDepositBytes
(
1
,
l1Block
,
rollupGenesis
.
SystemConfig
,
false
)
require
.
Nil
(
t
,
err
)
require
.
Nil
(
t
,
err
)
// Create a deposit from alice that will always fail (not enough funds)
// Create a deposit from alice that will always fail (not enough funds)
...
...
op-node/rollup/derive/attributes.go
View file @
096d9c2b
...
@@ -100,7 +100,7 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
...
@@ -100,7 +100,7 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
l2Parent
,
nextL2Time
,
eth
.
ToBlockID
(
l1Info
),
l1Info
.
Time
()))
l2Parent
,
nextL2Time
,
eth
.
ToBlockID
(
l1Info
),
l1Info
.
Time
()))
}
}
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
seqNumber
,
l1Info
,
sysConfig
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
seqNumber
,
l1Info
,
sysConfig
,
ba
.
cfg
.
IsRegolith
(
nextL2Time
)
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
NewCriticalError
(
fmt
.
Errorf
(
"failed to create l1InfoTx: %w"
,
err
))
return
nil
,
NewCriticalError
(
fmt
.
Errorf
(
"failed to create l1InfoTx: %w"
,
err
))
}
}
...
...
op-node/rollup/derive/attributes_queue_test.go
View file @
096d9c2b
...
@@ -66,7 +66,7 @@ func TestAttributesQueue(t *testing.T) {
...
@@ -66,7 +66,7 @@ func TestAttributesQueue(t *testing.T) {
l2Fetcher
:=
&
testutils
.
MockL2Client
{}
l2Fetcher
:=
&
testutils
.
MockL2Client
{}
l2Fetcher
.
ExpectSystemConfigByL2Hash
(
safeHead
.
Hash
,
parentL1Cfg
,
nil
)
l2Fetcher
.
ExpectSystemConfigByL2Hash
(
safeHead
.
Hash
,
parentL1Cfg
,
nil
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
safeHead
.
SequenceNumber
+
1
,
l1Info
,
expectedL1Cfg
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
safeHead
.
SequenceNumber
+
1
,
l1Info
,
expectedL1Cfg
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
attrs
:=
eth
.
PayloadAttributes
{
attrs
:=
eth
.
PayloadAttributes
{
Timestamp
:
eth
.
Uint64Quantity
(
safeHead
.
Time
+
cfg
.
BlockTime
),
Timestamp
:
eth
.
Uint64Quantity
(
safeHead
.
Time
+
cfg
.
BlockTime
),
...
...
op-node/rollup/derive/attributes_test.go
View file @
096d9c2b
...
@@ -113,7 +113,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
...
@@ -113,7 +113,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1Info
.
InfoParentHash
=
l2Parent
.
L1Origin
.
Hash
l1Info
.
InfoParentHash
=
l2Parent
.
L1Origin
.
Hash
l1Info
.
InfoNum
=
l2Parent
.
L1Origin
.
Number
+
1
l1Info
.
InfoNum
=
l2Parent
.
L1Origin
.
Number
+
1
epoch
:=
l1Info
.
ID
()
epoch
:=
l1Info
.
ID
()
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
0
,
l1Info
,
testSysCfg
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
0
,
l1Info
,
testSysCfg
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
l1Fetcher
.
ExpectFetchReceipts
(
epoch
.
Hash
,
l1Info
,
nil
,
nil
)
l1Fetcher
.
ExpectFetchReceipts
(
epoch
.
Hash
,
l1Info
,
nil
,
nil
)
attrBuilder
:=
NewFetchingAttributesBuilder
(
cfg
,
l1Fetcher
,
l1CfgFetcher
)
attrBuilder
:=
NewFetchingAttributesBuilder
(
cfg
,
l1Fetcher
,
l1CfgFetcher
)
...
@@ -150,7 +150,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
...
@@ -150,7 +150,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
epoch
:=
l1Info
.
ID
()
epoch
:=
l1Info
.
ID
()
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
0
,
l1Info
,
testSysCfg
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
0
,
l1Info
,
testSysCfg
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
l2Txs
:=
append
(
append
(
make
([]
eth
.
Data
,
0
),
l1InfoTx
),
usedDepositTxs
...
)
l2Txs
:=
append
(
append
(
make
([]
eth
.
Data
,
0
),
l1InfoTx
),
usedDepositTxs
...
)
...
@@ -180,7 +180,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
...
@@ -180,7 +180,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1Info
.
InfoNum
=
l2Parent
.
L1Origin
.
Number
l1Info
.
InfoNum
=
l2Parent
.
L1Origin
.
Number
epoch
:=
l1Info
.
ID
()
epoch
:=
l1Info
.
ID
()
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
l2Parent
.
SequenceNumber
+
1
,
l1Info
,
testSysCfg
)
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
l2Parent
.
SequenceNumber
+
1
,
l1Info
,
testSysCfg
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
l1Fetcher
.
ExpectInfoByHash
(
epoch
.
Hash
,
l1Info
,
nil
)
l1Fetcher
.
ExpectInfoByHash
(
epoch
.
Hash
,
l1Info
,
nil
)
...
@@ -195,6 +195,53 @@ func TestPreparePayloadAttributes(t *testing.T) {
...
@@ -195,6 +195,53 @@ func TestPreparePayloadAttributes(t *testing.T) {
require
.
Equal
(
t
,
l1InfoTx
,
[]
byte
(
attrs
.
Transactions
[
0
]))
require
.
Equal
(
t
,
l1InfoTx
,
[]
byte
(
attrs
.
Transactions
[
0
]))
require
.
True
(
t
,
attrs
.
NoTxPool
)
require
.
True
(
t
,
attrs
.
NoTxPool
)
})
})
// Test that the payload attributes builder changes the deposit format based on L2-time-based regolith activation
t
.
Run
(
"regolith"
,
func
(
t
*
testing
.
T
)
{
testCases
:=
[]
struct
{
name
string
l1Time
uint64
l2ParentTime
uint64
regolithTime
uint64
regolith
bool
}{
{
"exactly"
,
900
,
1000
-
cfg
.
BlockTime
,
1000
,
true
},
{
"almost"
,
900
,
1000
-
cfg
.
BlockTime
-
1
,
1000
,
false
},
{
"inactive"
,
700
,
700
,
1000
,
false
},
{
"l1 time before regolith"
,
1000
,
1001
,
1001
,
true
},
{
"l1 time way before regolith"
,
1000
,
2000
,
2000
,
true
},
{
"l1 time before regoltih and l2 after"
,
1000
,
3000
,
2000
,
true
},
}
for
_
,
tc
:=
range
testCases
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
cfgCopy
:=
*
cfg
// copy, we are making regolith config modifications
cfg
:=
&
cfgCopy
rng
:=
rand
.
New
(
rand
.
NewSource
(
1234
))
l1Fetcher
:=
&
testutils
.
MockL1Source
{}
defer
l1Fetcher
.
AssertExpectations
(
t
)
l2Parent
:=
testutils
.
RandomL2BlockRef
(
rng
)
cfg
.
RegolithTime
=
&
tc
.
regolithTime
l2Parent
.
Time
=
tc
.
l2ParentTime
l1CfgFetcher
:=
&
testutils
.
MockL2Client
{}
l1CfgFetcher
.
ExpectSystemConfigByL2Hash
(
l2Parent
.
Hash
,
testSysCfg
,
nil
)
defer
l1CfgFetcher
.
AssertExpectations
(
t
)
l1Info
:=
testutils
.
RandomBlockInfo
(
rng
)
l1Info
.
InfoParentHash
=
l2Parent
.
L1Origin
.
Hash
l1Info
.
InfoNum
=
l2Parent
.
L1Origin
.
Number
+
1
l1Info
.
InfoTime
=
tc
.
l1Time
epoch
:=
l1Info
.
ID
()
l1InfoTx
,
err
:=
L1InfoDepositBytes
(
0
,
l1Info
,
testSysCfg
,
tc
.
regolith
)
require
.
NoError
(
t
,
err
)
l1Fetcher
.
ExpectFetchReceipts
(
epoch
.
Hash
,
l1Info
,
nil
,
nil
)
attrBuilder
:=
NewFetchingAttributesBuilder
(
cfg
,
l1Fetcher
,
l1CfgFetcher
)
attrs
,
err
:=
attrBuilder
.
PreparePayloadAttributes
(
context
.
Background
(),
l2Parent
,
epoch
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
l1InfoTx
,
[]
byte
(
attrs
.
Transactions
[
0
]))
})
}
})
}
}
func
encodeDeposits
(
deposits
[]
*
types
.
DepositTx
)
(
out
[]
eth
.
Data
,
err
error
)
{
func
encodeDeposits
(
deposits
[]
*
types
.
DepositTx
)
(
out
[]
eth
.
Data
,
err
error
)
{
...
...
op-node/rollup/derive/l1_block_info.go
View file @
096d9c2b
...
@@ -26,6 +26,10 @@ var (
...
@@ -26,6 +26,10 @@ var (
L1BlockAddress
=
predeploys
.
L1BlockAddr
L1BlockAddress
=
predeploys
.
L1BlockAddr
)
)
const
(
RegolithSystemTxGas
=
1
_000_000
)
// L1BlockInfo presents the information stored in a L1Block.setL1BlockValues call
// L1BlockInfo presents the information stored in a L1Block.setL1BlockValues call
type
L1BlockInfo
struct
{
type
L1BlockInfo
struct
{
Number
uint64
Number
uint64
...
@@ -115,7 +119,7 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) {
...
@@ -115,7 +119,7 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) {
// L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block,
// L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block,
// and the L2 block-height difference with the start of the epoch.
// and the L2 block-height difference with the start of the epoch.
func
L1InfoDeposit
(
seqNumber
uint64
,
block
eth
.
BlockInfo
,
sysCfg
eth
.
SystemConfig
)
(
*
types
.
DepositTx
,
error
)
{
func
L1InfoDeposit
(
seqNumber
uint64
,
block
eth
.
BlockInfo
,
sysCfg
eth
.
SystemConfig
,
regolith
bool
)
(
*
types
.
DepositTx
,
error
)
{
infoDat
:=
L1BlockInfo
{
infoDat
:=
L1BlockInfo
{
Number
:
block
.
NumberU64
(),
Number
:
block
.
NumberU64
(),
Time
:
block
.
Time
(),
Time
:
block
.
Time
(),
...
@@ -137,7 +141,7 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
...
@@ -137,7 +141,7 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
}
}
// Set a very large gas limit with `IsSystemTransaction` to ensure
// Set a very large gas limit with `IsSystemTransaction` to ensure
// that the L1 Attributes Transaction does not run out of gas.
// that the L1 Attributes Transaction does not run out of gas.
return
&
types
.
DepositTx
{
out
:=
&
types
.
DepositTx
{
SourceHash
:
source
.
SourceHash
(),
SourceHash
:
source
.
SourceHash
(),
From
:
L1InfoDepositerAddress
,
From
:
L1InfoDepositerAddress
,
To
:
&
L1BlockAddress
,
To
:
&
L1BlockAddress
,
...
@@ -146,12 +150,18 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
...
@@ -146,12 +150,18 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
Gas
:
150
_000_000
,
Gas
:
150
_000_000
,
IsSystemTransaction
:
true
,
IsSystemTransaction
:
true
,
Data
:
data
,
Data
:
data
,
},
nil
}
// With the regolith fork we disable the IsSystemTx functionality, and allocate real gas
if
regolith
{
out
.
IsSystemTransaction
=
false
out
.
Gas
=
RegolithSystemTxGas
}
return
out
,
nil
}
}
// L1InfoDepositBytes returns a serialized L1-info attributes transaction.
// L1InfoDepositBytes returns a serialized L1-info attributes transaction.
func
L1InfoDepositBytes
(
seqNumber
uint64
,
l1Info
eth
.
BlockInfo
,
sysCfg
eth
.
SystemConfig
)
([]
byte
,
error
)
{
func
L1InfoDepositBytes
(
seqNumber
uint64
,
l1Info
eth
.
BlockInfo
,
sysCfg
eth
.
SystemConfig
,
regolith
bool
)
([]
byte
,
error
)
{
dep
,
err
:=
L1InfoDeposit
(
seqNumber
,
l1Info
,
sysCfg
)
dep
,
err
:=
L1InfoDeposit
(
seqNumber
,
l1Info
,
sysCfg
,
regolith
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create L1 info tx: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to create L1 info tx: %w"
,
err
)
}
}
...
...
op-node/rollup/derive/l1_block_info_test.go
View file @
096d9c2b
...
@@ -65,7 +65,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
...
@@ -65,7 +65,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
info
:=
testCase
.
mkInfo
(
rng
)
info
:=
testCase
.
mkInfo
(
rng
)
l1Cfg
:=
testCase
.
mkL1Cfg
(
rng
,
info
)
l1Cfg
:=
testCase
.
mkL1Cfg
(
rng
,
info
)
seqNr
:=
testCase
.
seqNr
(
rng
)
seqNr
:=
testCase
.
seqNr
(
rng
)
depTx
,
err
:=
L1InfoDeposit
(
seqNr
,
info
,
l1Cfg
)
depTx
,
err
:=
L1InfoDeposit
(
seqNr
,
info
,
l1Cfg
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
res
,
err
:=
L1InfoDepositTxData
(
depTx
.
Data
)
res
,
err
:=
L1InfoDepositTxData
(
depTx
.
Data
)
require
.
NoError
(
t
,
err
,
"expected valid deposit info"
)
require
.
NoError
(
t
,
err
,
"expected valid deposit info"
)
...
@@ -95,11 +95,19 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
...
@@ -95,11 +95,19 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
t
.
Run
(
"invalid selector"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"invalid selector"
,
func
(
t
*
testing
.
T
)
{
rng
:=
rand
.
New
(
rand
.
NewSource
(
1234
))
rng
:=
rand
.
New
(
rand
.
NewSource
(
1234
))
info
:=
testutils
.
MakeBlockInfo
(
nil
)(
rng
)
info
:=
testutils
.
MakeBlockInfo
(
nil
)(
rng
)
depTx
,
err
:=
L1InfoDeposit
(
randomSeqNr
(
rng
),
info
,
randomL1Cfg
(
rng
,
info
))
depTx
,
err
:=
L1InfoDeposit
(
randomSeqNr
(
rng
),
info
,
randomL1Cfg
(
rng
,
info
)
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
_
,
err
=
rand
.
Read
(
depTx
.
Data
[
0
:
4
])
_
,
err
=
rand
.
Read
(
depTx
.
Data
[
0
:
4
])
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
_
,
err
=
L1InfoDepositTxData
(
depTx
.
Data
)
_
,
err
=
L1InfoDepositTxData
(
depTx
.
Data
)
require
.
ErrorContains
(
t
,
err
,
"function signature"
)
require
.
ErrorContains
(
t
,
err
,
"function signature"
)
})
})
t
.
Run
(
"regolith"
,
func
(
t
*
testing
.
T
)
{
rng
:=
rand
.
New
(
rand
.
NewSource
(
1234
))
info
:=
testutils
.
MakeBlockInfo
(
nil
)(
rng
)
depTx
,
err
:=
L1InfoDeposit
(
randomSeqNr
(
rng
),
info
,
randomL1Cfg
(
rng
,
info
),
true
)
require
.
NoError
(
t
,
err
)
require
.
False
(
t
,
depTx
.
IsSystemTransaction
)
require
.
Equal
(
t
,
depTx
.
Gas
,
uint64
(
RegolithSystemTxGas
))
})
}
}
op-node/rollup/derive/l1_block_info_tob_test.go
View file @
096d9c2b
...
@@ -27,7 +27,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) {
...
@@ -27,7 +27,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) {
typeProvider
.
Fuzz
(
&
sysCfg
)
typeProvider
.
Fuzz
(
&
sysCfg
)
// Create our deposit tx from our info
// Create our deposit tx from our info
depTx
,
err
:=
L1InfoDeposit
(
seqNr
,
&
l1Info
,
sysCfg
)
depTx
,
err
:=
L1InfoDeposit
(
seqNr
,
&
l1Info
,
sysCfg
,
false
)
require
.
NoError
(
t
,
err
,
"error creating deposit tx from L1 info"
)
require
.
NoError
(
t
,
err
,
"error creating deposit tx from L1 info"
)
// Get our info from out deposit tx
// Get our info from out deposit tx
...
@@ -71,7 +71,7 @@ func FuzzDecodeDepositTxDataToL1Info(f *testing.F) {
...
@@ -71,7 +71,7 @@ func FuzzDecodeDepositTxDataToL1Info(f *testing.F) {
GasLimit
:
uint64
(
0
),
GasLimit
:
uint64
(
0
),
}
}
depTx
,
err
:=
L1InfoDeposit
(
res
.
SequenceNumber
,
&
l1Info
,
sysCfg
)
depTx
,
err
:=
L1InfoDeposit
(
res
.
SequenceNumber
,
&
l1Info
,
sysCfg
,
false
)
require
.
NoError
(
t
,
err
,
"error creating deposit tx from L1 info"
)
require
.
NoError
(
t
,
err
,
"error creating deposit tx from L1 info"
)
require
.
Equal
(
t
,
depTx
.
Data
,
fuzzedData
)
require
.
Equal
(
t
,
depTx
.
Data
,
fuzzedData
)
})
})
...
...
op-node/rollup/driver/sequencer_test.go
View file @
096d9c2b
...
@@ -245,7 +245,7 @@ func TestSequencerChaosMonkey(t *testing.T) {
...
@@ -245,7 +245,7 @@ func TestSequencerChaosMonkey(t *testing.T) {
InfoBaseFee
:
big
.
NewInt
(
1234
),
InfoBaseFee
:
big
.
NewInt
(
1234
),
InfoReceiptRoot
:
common
.
Hash
{},
InfoReceiptRoot
:
common
.
Hash
{},
}
}
infoDep
,
err
:=
derive
.
L1InfoDepositBytes
(
seqNr
,
l1Info
,
cfg
.
Genesis
.
SystemConfig
)
infoDep
,
err
:=
derive
.
L1InfoDepositBytes
(
seqNr
,
l1Info
,
cfg
.
Genesis
.
SystemConfig
,
false
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
testGasLimit
:=
eth
.
Uint64Quantity
(
10
_000_000
)
testGasLimit
:=
eth
.
Uint64Quantity
(
10
_000_000
)
...
...
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