Skip to content

Commit 4047f24

Browse files
committed
Implement unit tests
1 parent a77524f commit 4047f24

File tree

4 files changed

+235
-2
lines changed

4 files changed

+235
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package system_contract
2+
3+
import (
4+
"context"
5+
"github.com/scroll-tech/go-ethereum"
6+
"github.com/scroll-tech/go-ethereum/accounts"
7+
"github.com/scroll-tech/go-ethereum/common"
8+
"github.com/scroll-tech/go-ethereum/core/types"
9+
"github.com/scroll-tech/go-ethereum/log"
10+
"github.com/scroll-tech/go-ethereum/params"
11+
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
12+
"github.com/scroll-tech/go-ethereum/trie"
13+
"github.com/stretchr/testify/require"
14+
"math/big"
15+
"sync"
16+
"testing"
17+
"time"
18+
)
19+
20+
var _ sync_service.EthClient = &fakeEthClient{}
21+
22+
func TestSystemContract_FetchSigner(t *testing.T) {
23+
log.Root().SetHandler(log.DiscardHandler())
24+
25+
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
26+
27+
fakeClient := &fakeEthClient{value: expectedSigner}
28+
29+
config := &params.SystemContractConfig{
30+
SystemContractAddress: common.HexToAddress("0xFAKE"),
31+
// The slot number can be arbitrary – fake client doesn't use it.
32+
Period: 10,
33+
RelaxedPeriod: false,
34+
}
35+
36+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
37+
defer cancel()
38+
39+
sys := New(ctx, config, fakeClient)
40+
defer sys.Close()
41+
42+
// Since the SystemContract's Start() routine fetches and updates s.signerAddressL1
43+
// in a separate goroutine, wait a bit for that to complete.
44+
time.Sleep(2 * time.Second)
45+
46+
// Acquire a read lock to safely read the value.
47+
sys.lock.RLock()
48+
actualSigner := sys.signerAddressL1
49+
sys.lock.RUnlock()
50+
51+
// Verify that the fetched signer equals the expectedSigner from our fake client.
52+
require.Equal(t, expectedSigner, actualSigner, "The SystemContract should update signerAddressL1 to the value provided by the client")
53+
}
54+
55+
func TestSystemContract_AuthorizeCheck(t *testing.T) {
56+
// This test verifies that if the local signer does not match the authorized signer,
57+
// then the Seal() function returns an error.
58+
59+
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
60+
61+
fakeClient := &fakeEthClient{value: expectedSigner}
62+
config := &params.SystemContractConfig{
63+
SystemContractAddress: common.HexToAddress("0xFAKE"),
64+
Period: 10,
65+
RelaxedPeriod: false,
66+
}
67+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
68+
defer cancel()
69+
70+
sys := New(ctx, config, fakeClient)
71+
defer sys.Close()
72+
73+
// Wait to ensure that the background routine has updated signerAddressL1.
74+
time.Sleep(2 * time.Second)
75+
76+
// Authorize with a different signer than expected.
77+
differentSigner := common.HexToAddress("0xABCDEFabcdefABCDEFabcdefabcdefABCDEFABCD")
78+
sys.Authorize(differentSigner, func(acc accounts.Account, mimeType string, message []byte) ([]byte, error) {
79+
// For testing, return a dummy signature
80+
return []byte("dummy_sig"), nil
81+
})
82+
83+
// Create a dummy block header.
84+
// We only need the block number and extra data length for this test.
85+
header := &types.Header{
86+
Number: big.NewInt(100),
87+
// We use an extra slice with length equal to extraSeal
88+
Extra: make([]byte, extraSeal),
89+
}
90+
91+
// Call Seal() and expect an error since local signer != authorized signer.
92+
results := make(chan *types.Block)
93+
stop := make(chan struct{})
94+
err := sys.Seal(nil, (&types.Block{}).WithSeal(header), results, stop)
95+
96+
require.Error(t, err, "Seal should return an error when the local signer is not authorized")
97+
}
98+
99+
// TestSystemContract_SignsAfterUpdate simulates:
100+
// 1. Initially, the SystemContract authorized signer (from StorageAt) is not the signer of the Block.
101+
// 2. Later, after updating the fake client to the correct signer, the background
102+
// poll updates the SystemContract.
103+
// 3. Once updated, if the local signing key is set to match, Seal() should succeed.
104+
func TestSystemContract_SignsAfterUpdate(t *testing.T) {
105+
// Silence logging during tests.
106+
log.Root().SetHandler(log.DiscardHandler())
107+
108+
// Define two addresses: one "wrong" and one "correct".
109+
oldSigner := common.HexToAddress("0x1111111111111111111111111111111111111111")
110+
updatedSigner := common.HexToAddress("0x2222222222222222222222222222222222222222")
111+
112+
// Create a fake client that starts by returning the wrong signer.
113+
fakeClient := &fakeEthClient{
114+
value: oldSigner,
115+
}
116+
117+
config := &params.SystemContractConfig{
118+
SystemContractAddress: common.HexToAddress("0xFAKE"), // Dummy value
119+
Period: 10, // arbitrary non-zero value
120+
RelaxedPeriod: false,
121+
}
122+
123+
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
124+
defer cancel()
125+
126+
sys := New(ctx, config, fakeClient)
127+
defer sys.Close()
128+
129+
// Wait for the background routine to poll at least once.
130+
time.Sleep(2 * time.Second)
131+
132+
// Verify that initially the fetched signer equals oldSigner.
133+
sys.lock.RLock()
134+
initialSigner := sys.signerAddressL1
135+
sys.lock.RUnlock()
136+
require.Equal(t, oldSigner, initialSigner, "Initial signerAddressL1 should be oldSigner")
137+
138+
// Now, simulate an update: change the fake client's returned value to updatedSigner.
139+
fakeClient.mu.Lock()
140+
fakeClient.value = updatedSigner
141+
fakeClient.mu.Unlock()
142+
143+
// Wait enough for the background polling routine to fetch the new value.
144+
time.Sleep(1 + time.Second + defaultSyncInterval)
145+
146+
// Verify that system contract's signerAddressL1 is now updated to updatedSigner.
147+
sys.lock.RLock()
148+
newSigner := sys.signerAddressL1
149+
sys.lock.RUnlock()
150+
require.Equal(t, newSigner, updatedSigner, "SignerAddressL1 should update to updatedSigner after polling")
151+
152+
// Now simulate authorizing with the correct local signer.
153+
sys.Authorize(updatedSigner, func(acc accounts.Account, mimeType string, message []byte) ([]byte, error) {
154+
// For testing, return a dummy signature.
155+
return []byte("dummy_signature"), nil
156+
})
157+
158+
// Create a dummy header for sealing.
159+
header := &types.Header{
160+
Number: big.NewInt(100),
161+
Extra: make([]byte, extraSeal),
162+
}
163+
164+
// Construct a new block from the header using NewBlock constructor.
165+
block := types.NewBlock(header, nil, nil, nil, trie.NewStackTrie(nil))
166+
167+
results := make(chan *types.Block)
168+
stop := make(chan struct{})
169+
170+
// Call Seal. It should succeed (i.e. return no error) because local signer now equals authorized signer.
171+
err := sys.Seal(nil, block, results, stop)
172+
require.NoError(t, err, "Seal should succeed when the local signer is authorized after update")
173+
174+
// Wait for the result from Seal's goroutine.
175+
select {
176+
case sealedBlock := <-results:
177+
require.NotNil(t, sealedBlock, "Seal should eventually return a sealed block")
178+
// Optionally, you may log or further inspect sealedBlock here.
179+
case <-time.After(15 * time.Second):
180+
t.Fatal("Timed out waiting for Seal to return a sealed block")
181+
}
182+
}
183+
184+
// fakeEthClient implements a minimal version of sync_service.EthClient for testing purposes.
185+
type fakeEthClient struct {
186+
mu sync.Mutex
187+
// value is the fixed value to return from StorageAt.
188+
// We'll assume StorageAt returns a 32-byte value representing an Ethereum address.
189+
value common.Address
190+
}
191+
192+
// BlockNumber returns 0.
193+
func (f *fakeEthClient) BlockNumber(ctx context.Context) (uint64, error) {
194+
return 0, nil
195+
}
196+
197+
// ChainID returns a zero-value chain ID.
198+
func (f *fakeEthClient) ChainID(ctx context.Context) (*big.Int, error) {
199+
return big.NewInt(0), nil
200+
}
201+
202+
// FilterLogs returns an empty slice of logs.
203+
func (f *fakeEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
204+
return []types.Log{}, nil
205+
}
206+
207+
// HeaderByNumber returns nil.
208+
func (f *fakeEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
209+
return nil, nil
210+
}
211+
212+
// SubscribeFilterLogs returns a nil subscription.
213+
func (f *fakeEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
214+
return nil, nil
215+
}
216+
217+
// TransactionByHash returns (nil, false, nil).
218+
func (f *fakeEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
219+
return nil, false, nil
220+
}
221+
222+
// BlockByHash returns nil.
223+
func (f *fakeEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
224+
return nil, nil
225+
}
226+
227+
// StorageAt returns the byte representation of f.value.
228+
func (f *fakeEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
229+
f.mu.Lock()
230+
defer f.mu.Unlock()
231+
return f.value.Bytes(), nil
232+
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ require (
2121
github.com/edsrzf/mmap-go v1.0.0
2222
github.com/ethereum/c-kzg-4844/bindings/go v0.0.0-20230126171313-363c7d7593b4
2323
github.com/fatih/color v1.7.0
24-
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
24+
github.com/fjl/memsize v0.0.2
2525
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
2626
github.com/go-stack/stack v1.8.1
2727
github.com/golang/protobuf v1.4.3

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
136136
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
137137
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
138138
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
139+
github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA=
140+
github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
139141
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
140142
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
141143
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=

rollup/rollup_sync_service/l1client_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,3 @@ func (m *mockEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*typ
7676
func (m *mockEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
7777
return nil, nil
7878
}
79-

0 commit comments

Comments
 (0)