Skip to content

Commit b86775c

Browse files
committed
add test
1 parent 6c6be4b commit b86775c

File tree

3 files changed

+173
-60
lines changed

3 files changed

+173
-60
lines changed

consensus/system_contract/system_contract.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package system_contract
33
import (
44
"context"
55
"fmt"
6+
"math/big"
67
"sync"
78
"time"
89

10+
"github.com/scroll-tech/go-ethereum"
911
"github.com/scroll-tech/go-ethereum/common"
12+
"github.com/scroll-tech/go-ethereum/core/types"
1013
"github.com/scroll-tech/go-ethereum/log"
1114
"github.com/scroll-tech/go-ethereum/params"
1215
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
@@ -140,3 +143,53 @@ func (s *SystemContract) localSignerAddress() common.Address {
140143

141144
return s.signer
142145
}
146+
147+
// FakeEthClient implements a minimal version of sync_service.EthClient for testing purposes.
148+
type FakeEthClient struct {
149+
mu sync.Mutex
150+
// Value is the fixed Value to return from StorageAt.
151+
// We'll assume StorageAt returns a 32-byte Value representing an Ethereum address.
152+
Value common.Address
153+
}
154+
155+
// BlockNumber returns 0.
156+
func (f *FakeEthClient) BlockNumber(ctx context.Context) (uint64, error) {
157+
return 0, nil
158+
}
159+
160+
// ChainID returns a zero-value chain ID.
161+
func (f *FakeEthClient) ChainID(ctx context.Context) (*big.Int, error) {
162+
return big.NewInt(0), nil
163+
}
164+
165+
// FilterLogs returns an empty slice of logs.
166+
func (f *FakeEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
167+
return []types.Log{}, nil
168+
}
169+
170+
// HeaderByNumber returns nil.
171+
func (f *FakeEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
172+
return nil, nil
173+
}
174+
175+
// SubscribeFilterLogs returns a nil subscription.
176+
func (f *FakeEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
177+
return nil, nil
178+
}
179+
180+
// TransactionByHash returns (nil, false, nil).
181+
func (f *FakeEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
182+
return nil, false, nil
183+
}
184+
185+
// BlockByHash returns nil.
186+
func (f *FakeEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
187+
return nil, nil
188+
}
189+
190+
// StorageAt returns the byte representation of f.value.
191+
func (f *FakeEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
192+
f.mu.Lock()
193+
defer f.mu.Unlock()
194+
return f.Value.Bytes(), nil
195+
}

consensus/system_contract/system_contract_test.go

Lines changed: 6 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ package system_contract
33
import (
44
"context"
55
"math/big"
6-
"sync"
76
"testing"
87
"time"
98

109
"github.com/stretchr/testify/require"
1110

12-
"github.com/scroll-tech/go-ethereum"
1311
"github.com/scroll-tech/go-ethereum/accounts"
1412
"github.com/scroll-tech/go-ethereum/common"
1513
"github.com/scroll-tech/go-ethereum/core/types"
@@ -19,14 +17,14 @@ import (
1917
"github.com/scroll-tech/go-ethereum/trie"
2018
)
2119

22-
var _ sync_service.EthClient = &fakeEthClient{}
20+
var _ sync_service.EthClient = &FakeEthClient{}
2321

2422
func TestSystemContract_FetchSigner(t *testing.T) {
2523
log.Root().SetHandler(log.DiscardHandler())
2624

2725
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
2826

29-
fakeClient := &fakeEthClient{value: expectedSigner}
27+
fakeClient := &FakeEthClient{Value: expectedSigner}
3028

3129
config := &params.SystemContractConfig{
3230
SystemContractAddress: common.HexToAddress("0xFAKE"),
@@ -55,7 +53,7 @@ func TestSystemContract_AuthorizeCheck(t *testing.T) {
5553

5654
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
5755

58-
fakeClient := &fakeEthClient{value: expectedSigner}
56+
fakeClient := &FakeEthClient{Value: expectedSigner}
5957
config := &params.SystemContractConfig{
6058
SystemContractAddress: common.HexToAddress("0xFAKE"),
6159
Period: 10,
@@ -106,8 +104,8 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
106104
updatedSigner := common.HexToAddress("0x2222222222222222222222222222222222222222")
107105

108106
// Create a fake client that starts by returning the wrong signer.
109-
fakeClient := &fakeEthClient{
110-
value: oldSigner,
107+
fakeClient := &FakeEthClient{
108+
Value: oldSigner,
111109
}
112110

113111
config := &params.SystemContractConfig{
@@ -130,7 +128,7 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
130128

131129
// Now, simulate an update: change the fake client's returned value to updatedSigner.
132130
fakeClient.mu.Lock()
133-
fakeClient.value = updatedSigner
131+
fakeClient.Value = updatedSigner
134132
fakeClient.mu.Unlock()
135133

136134
// fetch new value from L1 (simulating a background poll)
@@ -171,53 +169,3 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
171169
t.Fatal("Timed out waiting for Seal to return a sealed block")
172170
}
173171
}
174-
175-
// fakeEthClient implements a minimal version of sync_service.EthClient for testing purposes.
176-
type fakeEthClient struct {
177-
mu sync.Mutex
178-
// value is the fixed value to return from StorageAt.
179-
// We'll assume StorageAt returns a 32-byte value representing an Ethereum address.
180-
value common.Address
181-
}
182-
183-
// BlockNumber returns 0.
184-
func (f *fakeEthClient) BlockNumber(ctx context.Context) (uint64, error) {
185-
return 0, nil
186-
}
187-
188-
// ChainID returns a zero-value chain ID.
189-
func (f *fakeEthClient) ChainID(ctx context.Context) (*big.Int, error) {
190-
return big.NewInt(0), nil
191-
}
192-
193-
// FilterLogs returns an empty slice of logs.
194-
func (f *fakeEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
195-
return []types.Log{}, nil
196-
}
197-
198-
// HeaderByNumber returns nil.
199-
func (f *fakeEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
200-
return nil, nil
201-
}
202-
203-
// SubscribeFilterLogs returns a nil subscription.
204-
func (f *fakeEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
205-
return nil, nil
206-
}
207-
208-
// TransactionByHash returns (nil, false, nil).
209-
func (f *fakeEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
210-
return nil, false, nil
211-
}
212-
213-
// BlockByHash returns nil.
214-
func (f *fakeEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
215-
return nil, nil
216-
}
217-
218-
// StorageAt returns the byte representation of f.value.
219-
func (f *fakeEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
220-
f.mu.Lock()
221-
defer f.mu.Unlock()
222-
return f.value.Bytes(), nil
223-
}

miner/scroll_worker_test.go

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package miner
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"math"
2223
"math/big"
@@ -33,6 +34,8 @@ import (
3334
"github.com/scroll-tech/go-ethereum/consensus"
3435
"github.com/scroll-tech/go-ethereum/consensus/clique"
3536
"github.com/scroll-tech/go-ethereum/consensus/ethash"
37+
"github.com/scroll-tech/go-ethereum/consensus/system_contract"
38+
"github.com/scroll-tech/go-ethereum/consensus/wrapper"
3639
"github.com/scroll-tech/go-ethereum/core"
3740
"github.com/scroll-tech/go-ethereum/core/rawdb"
3841
"github.com/scroll-tech/go-ethereum/core/types"
@@ -77,6 +80,14 @@ var (
7780
MaxAccountsNum: math.MaxInt,
7881
CCCMaxWorkers: 2,
7982
}
83+
84+
testConfigAllowEmpty = &Config{
85+
Recommit: time.Second,
86+
GasCeil: params.GenesisGasLimit,
87+
MaxAccountsNum: math.MaxInt,
88+
CCCMaxWorkers: 2,
89+
AllowEmpty: true,
90+
}
8091
)
8192

8293
func init() {
@@ -138,6 +149,15 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
138149
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
139150
return crypto.Sign(crypto.Keccak256(data), testBankKey)
140151
})
152+
case *wrapper.UpgradableEngine:
153+
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
154+
gspec.Timestamp = uint64(time.Now().Unix())
155+
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
156+
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
157+
return crypto.Sign(crypto.Keccak256(data), testBankKey)
158+
}, func(account accounts.Account, s string, data []byte) ([]byte, error) {
159+
return crypto.Sign(crypto.Keccak256(data), testBankKey)
160+
})
141161
case *ethash.Ethash:
142162
default:
143163
t.Fatalf("unexpected consensus engine type: %T", engine)
@@ -207,14 +227,26 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
207227
return tx
208228
}
209229

210-
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
230+
func testWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, allowEmpty bool) (*worker, *testWorkerBackend) {
211231
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
212232
backend.txPool.AddLocals(pendingTxs)
213-
w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, false)
233+
config := testConfig
234+
if allowEmpty {
235+
config = testConfigAllowEmpty
236+
}
237+
w := newWorker(config, chainConfig, engine, backend, new(event.TypeMux), nil, false, false)
214238
w.setEtherbase(testBankAddress)
215239
return w, backend
216240
}
217241

242+
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
243+
return testWorker(t, chainConfig, engine, db, blocks, false)
244+
}
245+
246+
func newTestWorkerWithEmptyBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
247+
return testWorker(t, chainConfig, engine, db, blocks, true)
248+
}
249+
218250
func TestGenerateBlockAndImportClique(t *testing.T) {
219251
testGenerateBlockAndImport(t, true)
220252
}
@@ -1355,3 +1387,83 @@ func TestEuclidV2HardForkMessageQueue(t *testing.T) {
13551387
}
13561388
}
13571389
}
1390+
1391+
// TestEuclidV2TransitionVerification tests that the upgradable consensus engine
1392+
// can successfully verify the EuclidV2 transition chain.
1393+
func TestEuclidV2TransitionVerification(t *testing.T) {
1394+
// patch time.Now() to be able to simulate hard fork time
1395+
patches := gomonkey.NewPatches()
1396+
defer patches.Reset()
1397+
var timeCount int64
1398+
patches.ApplyFunc(time.Now, func() time.Time {
1399+
timeCount++
1400+
return time.Unix(timeCount, 0)
1401+
})
1402+
1403+
// init chain config
1404+
chainConfig := params.AllCliqueProtocolChanges.Clone()
1405+
chainConfig.EuclidTime = newUint64(0)
1406+
chainConfig.EuclidV2Time = newUint64(10000)
1407+
chainConfig.Clique = &params.CliqueConfig{Period: 1, Epoch: 30000}
1408+
chainConfig.SystemContract = &params.SystemContractConfig{Period: 1}
1409+
1410+
// init worker
1411+
db := rawdb.NewMemoryDatabase()
1412+
cliqueEngine := clique.New(chainConfig.Clique, db)
1413+
sysEngine := system_contract.New(context.Background(), chainConfig.SystemContract, &system_contract.FakeEthClient{Value: testBankAddress})
1414+
engine := wrapper.NewUpgradableEngine(chainConfig.IsEuclidV2, cliqueEngine, sysEngine)
1415+
w, b := newTestWorkerWithEmptyBlock(t, chainConfig, engine, db, 0)
1416+
defer w.close()
1417+
b.genesis.MustCommit(db)
1418+
1419+
// collect mined blocks
1420+
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
1421+
defer sub.Unsubscribe()
1422+
w.start()
1423+
1424+
blocks := []*types.Block{}
1425+
headers := []*types.Header{}
1426+
1427+
for i := 0; i < 6; i++ {
1428+
select {
1429+
case ev := <-sub.Chan():
1430+
// activate EuclidV2 at next block
1431+
if i == 2 {
1432+
timeCount = int64(*chainConfig.EuclidV2Time)
1433+
}
1434+
1435+
block := ev.Data.(core.NewMinedBlockEvent).Block
1436+
blocks = append(blocks, block)
1437+
headers = append(headers, block.Header())
1438+
1439+
case <-time.After(3 * time.Second):
1440+
t.Fatalf("timeout")
1441+
}
1442+
}
1443+
1444+
// sanity check: we generated the EuclidV2 transition block
1445+
assert.False(t, chainConfig.IsEuclidV2(headers[0].Time))
1446+
assert.True(t, chainConfig.IsEuclidV2(headers[len(headers)-1].Time))
1447+
1448+
// import headers into new chain
1449+
chainDb := rawdb.NewMemoryDatabase()
1450+
b.genesis.MustCommit(chainDb)
1451+
chain, err := core.NewBlockChain(chainDb, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
1452+
assert.NoError(t, err)
1453+
defer chain.Stop()
1454+
1455+
// previously this would fail with `unknown ancestor`
1456+
_, err = chain.InsertHeaderChain(headers, 0)
1457+
assert.NoError(t, err)
1458+
1459+
// import headers into new chain
1460+
chainDb = rawdb.NewMemoryDatabase()
1461+
b.genesis.MustCommit(chainDb)
1462+
chain, err = core.NewBlockChain(chainDb, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
1463+
assert.NoError(t, err)
1464+
defer chain.Stop()
1465+
1466+
// previously this would fail with `unknown ancestor`
1467+
_, err = chain.InsertChain(blocks)
1468+
assert.NoError(t, err)
1469+
}

0 commit comments

Comments
 (0)