@@ -2,6 +2,7 @@ package tests
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"math/big"
6
7
"testing"
7
8
"time"
@@ -10,7 +11,9 @@ import (
10
11
"github.com/Fantom-foundation/go-opera/gossip/gasprice"
11
12
"github.com/Fantom-foundation/go-opera/inter"
12
13
"github.com/Fantom-foundation/go-opera/opera"
14
+ "github.com/Fantom-foundation/go-opera/opera/contracts/driver"
13
15
"github.com/ethereum/go-ethereum/common"
16
+ "github.com/ethereum/go-ethereum/common/hexutil"
14
17
"github.com/ethereum/go-ethereum/core/types"
15
18
"github.com/ethereum/go-ethereum/ethclient"
16
19
"github.com/ethereum/go-ethereum/rpc"
@@ -19,7 +22,7 @@ import (
19
22
)
20
23
21
24
func TestBlockHeader_SatisfiesInvariants (t * testing.T ) {
22
- const numBlocks = 5
25
+ const numBlocks = 10
23
26
require := require .New (t )
24
27
25
28
net , err := StartIntegrationTestNet (t .TempDir ())
@@ -47,57 +50,66 @@ func TestBlockHeader_SatisfiesInvariants(t *testing.T) {
47
50
headers = append (headers , header )
48
51
}
49
52
50
- t .Run ("BlockNumberEqualsPositionInChain" , func (t * testing.T ) {
51
- testHeaders_BlockNumberEqualsPositionInChain (t , headers )
52
- })
53
+ // Run twice - once before and once after a node restart.
54
+ for range 2 {
55
+ t .Run ("BlockNumberEqualsPositionInChain" , func (t * testing.T ) {
56
+ testHeaders_BlockNumberEqualsPositionInChain (t , headers )
57
+ })
53
58
54
- t .Run ("ParentHashCoversParentContent" , func (t * testing.T ) {
55
- testHeaders_ParentHashCoversParentContent (t , headers )
56
- })
59
+ t .Run ("ParentHashCoversParentContent" , func (t * testing.T ) {
60
+ testHeaders_ParentHashCoversParentContent (t , headers )
61
+ })
57
62
58
- t .Run ("GasUsedIsBelowGasLimit" , func (t * testing.T ) {
59
- testHeaders_GasUsedIsBelowGasLimit (t , headers )
60
- })
63
+ t .Run ("GasUsedIsBelowGasLimit" , func (t * testing.T ) {
64
+ testHeaders_GasUsedIsBelowGasLimit (t , headers )
65
+ })
61
66
62
- t .Run ("EncodesDurationAndNanoTimeInExtraData" , func (t * testing.T ) {
63
- testHeaders_EncodesDurationAndNanoTimeInExtraData (t , headers )
64
- })
67
+ t .Run ("EncodesDurationAndNanoTimeInExtraData" , func (t * testing.T ) {
68
+ testHeaders_EncodesDurationAndNanoTimeInExtraData (t , headers )
69
+ })
65
70
66
- t .Run ("BaseFeeEvolutionFollowsPricingRules" , func (t * testing.T ) {
67
- testHeaders_BaseFeeEvolutionFollowsPricingRules (t , headers )
68
- })
71
+ t .Run ("BaseFeeEvolutionFollowsPricingRules" , func (t * testing.T ) {
72
+ testHeaders_BaseFeeEvolutionFollowsPricingRules (t , headers )
73
+ })
69
74
70
- t .Run ("TransactionRootMatchesHashOfBlockTxs" , func (t * testing.T ) {
71
- testHeaders_TransactionRootMatchesBlockTxsHash (t , headers , client )
72
- })
75
+ t .Run ("TransactionRootMatchesHashOfBlockTxs" , func (t * testing.T ) {
76
+ testHeaders_TransactionRootMatchesBlockTxsHash (t , headers , client )
77
+ })
73
78
74
- t .Run ("ReceiptRootMatchesBlockReceipts" , func (t * testing.T ) {
75
- testHeaders_ReceiptRootMatchesBlockReceipts (t , headers , client )
76
- })
79
+ t .Run ("ReceiptRootMatchesBlockReceipts" , func (t * testing.T ) {
80
+ testHeaders_ReceiptRootMatchesBlockReceipts (t , headers , client )
81
+ })
77
82
78
- t .Run ("LogsBloomMatchesLogsInReceipts" , func (t * testing.T ) {
79
- testHeaders_LogsBloomMatchesLogsInReceipts (t , headers , client )
80
- })
83
+ t .Run ("LogsBloomMatchesLogsInReceipts" , func (t * testing.T ) {
84
+ testHeaders_LogsBloomMatchesLogsInReceipts (t , headers , client )
85
+ })
81
86
82
- t .Run ("CoinbaseIsZeroForAllBlocks" , func (t * testing.T ) {
83
- testHeaders_CoinbaseIsZeroForAllBlocks (t , headers )
84
- })
87
+ t .Run ("CoinbaseIsZeroForAllBlocks" , func (t * testing.T ) {
88
+ testHeaders_CoinbaseIsZeroForAllBlocks (t , headers )
89
+ })
85
90
86
- t .Run ("DifficultyIsZeroForAllBlocks" , func (t * testing.T ) {
87
- testHeaders_DifficultyIsZeroForAllBlocks (t , headers )
88
- })
91
+ t .Run ("DifficultyIsZeroForAllBlocks" , func (t * testing.T ) {
92
+ testHeaders_DifficultyIsZeroForAllBlocks (t , headers )
93
+ })
89
94
90
- t .Run ("NonceIsZeroForAllBlocks" , func (t * testing.T ) {
91
- testHeaders_NonceIsZeroForAllBlocks (t , headers )
92
- })
95
+ t .Run ("NonceIsZeroForAllBlocks" , func (t * testing.T ) {
96
+ testHeaders_NonceIsZeroForAllBlocks (t , headers )
97
+ })
93
98
94
- t .Run ("TimeProgressesMonotonically" , func (t * testing.T ) {
95
- testHeaders_TimeProgressesMonotonically (t , headers )
96
- })
99
+ t .Run ("TimeProgressesMonotonically" , func (t * testing.T ) {
100
+ testHeaders_TimeProgressesMonotonically (t , headers )
101
+ })
97
102
98
- t .Run ("MixDigestDiffersForAllBlocks" , func (t * testing.T ) {
99
- testHeaders_MixDigestDiffersForAllBlocks (t , headers )
100
- })
103
+ t .Run ("MixDigestDiffersForAllBlocks" , func (t * testing.T ) {
104
+ testHeaders_MixDigestDiffersForAllBlocks (t , headers )
105
+ })
106
+
107
+ t .Run ("LastBlockOfEpochContainsSealingTransaction" , func (t * testing.T ) {
108
+ testHeaders_LastBlockOfEpochContainsSealingTransaction (t , headers , client )
109
+ })
110
+
111
+ require .NoError (net .Restart ())
112
+ }
101
113
}
102
114
103
115
func testHeaders_BlockNumberEqualsPositionInChain (t * testing.T , headers []* types.Header ) {
@@ -272,3 +284,58 @@ func testHeaders_MixDigestDiffersForAllBlocks(t *testing.T, headers []*types.Hea
272
284
seen [headers [i ].MixDigest ] = struct {}{}
273
285
}
274
286
}
287
+
288
+ func testHeaders_LastBlockOfEpochContainsSealingTransaction (t * testing.T , headers []* types.Header , client * ethclient.Client ) {
289
+ require := require .New (t )
290
+
291
+ maxEpoch := 0
292
+ for i := 0 ; i < len (headers )- 1 ; i ++ {
293
+
294
+ block , err := client .BlockByNumber (context .Background (), big .NewInt (int64 (i )))
295
+ require .NoError (err , "failed to get block body" )
296
+
297
+ currentBlockEpoch , err := getEpochOfBlock (client , i )
298
+ require .NoError (err , "failed to get epoch of block %d" , i )
299
+
300
+ nextBlockEpoch , err := getEpochOfBlock (client , i + 1 )
301
+ require .NoError (err , "failed to get epoch of block %d" , i + 1 )
302
+
303
+ shouldContainSealingTx := currentBlockEpoch != nextBlockEpoch
304
+
305
+ containsSealingTx := false
306
+ for _ , tx := range block .Transactions () {
307
+ // Any call to the driver is considered a sealing transaction.
308
+ if tx .To () != nil && * tx .To () == driver .ContractAddress {
309
+ containsSealingTx = true
310
+ break
311
+ }
312
+ }
313
+
314
+ require .Equal (
315
+ shouldContainSealingTx ,
316
+ containsSealingTx ,
317
+ "block %d, current epoch %d, next epoch %d" ,
318
+ i , currentBlockEpoch , nextBlockEpoch ,
319
+ )
320
+
321
+ if currentBlockEpoch > maxEpoch {
322
+ maxEpoch = currentBlockEpoch
323
+ }
324
+ }
325
+ }
326
+
327
+ func getEpochOfBlock (client * ethclient.Client , blockNumber int ) (int , error ) {
328
+ var result struct {
329
+ Epoch hexutil.Uint64
330
+ }
331
+ err := client .Client ().Call (
332
+ & result ,
333
+ "eth_getBlockByNumber" ,
334
+ fmt .Sprintf ("0x%x" , blockNumber ),
335
+ false ,
336
+ )
337
+ if err != nil {
338
+ return 0 , err
339
+ }
340
+ return int (result .Epoch ), nil
341
+ }
0 commit comments