@@ -2,6 +2,7 @@ package server
22
33import (
44 "context"
5+ "strings"
56 "testing"
67
78 "github.com/onflow/flow-emulator/convert"
@@ -14,8 +15,6 @@ import (
1415)
1516
1617// TestForkingAgainstTestnet exercises the forking path by wiring a remote store
17- // We do not test Mainnet as at the time of writing Mainnet is not compatibible
18- // with the latest upstream Forte upgrade available in the latest emulator releases.
1918func TestForkingAgainstTestnet (t * testing.T ) {
2019 logger := zerolog .Nop ()
2120
@@ -150,3 +149,142 @@ func TestForkingAgainstTestnet(t *testing.T) {
150149 }
151150 }
152151}
152+
153+ // TestForkingAgainstMainnet exercises the forking path with mainnet and tests the account key deduplication shim
154+ func TestForkingAgainstMainnet (t * testing.T ) {
155+ logger := zerolog .Nop ()
156+
157+ // Get remote latest sealed height to pin fork
158+ conn , err := grpc .NewClient ("access.mainnet.nodes.onflow.org:9000" , grpc .WithTransportCredentials (insecure .NewCredentials ()))
159+ if err != nil {
160+ t .Fatalf ("dial remote: %v" , err )
161+ }
162+ defer conn .Close ()
163+ remote := flowaccess .NewAccessAPIClient (conn )
164+ rh , err := remote .GetLatestBlockHeader (context .Background (), & flowaccess.GetLatestBlockHeaderRequest {IsSealed : true })
165+ if err != nil {
166+ t .Fatalf ("get remote header: %v" , err )
167+ }
168+ remoteHeight := rh .Block .Height
169+
170+ cfg := & Config {
171+ // Do not start listeners; NewEmulatorServer only configures components.
172+ DBPath : "" ,
173+ Persist : false ,
174+ Snapshot : false ,
175+ SkipTransactionValidation : true ,
176+ ChainID : flowgo .Mainnet , // will be overridden by detectRemoteChainID
177+ ForkURL : "access.mainnet.nodes.onflow.org:9000" ,
178+ ForkBlockNumber : remoteHeight ,
179+ }
180+
181+ srv := NewEmulatorServer (& logger , cfg )
182+ if srv == nil {
183+ t .Fatal ("NewEmulatorServer returned nil" )
184+ }
185+
186+ if cfg .ChainID != flowgo .Mainnet {
187+ t .Fatalf ("expected ChainID to be Mainnet after fork detection, got %q" , cfg .ChainID )
188+ }
189+
190+ // Create an initial local block so we have a valid reference block ID in the forked store
191+ if _ , _ , err := srv .Emulator ().ExecuteAndCommitBlock (); err != nil {
192+ t .Fatalf ("prime local block: %v" , err )
193+ }
194+
195+ // Test account key retrieval for a known mainnet account with multiple keys
196+ // This tests the account key deduplication shim
197+ testAccountScript := []byte (`
198+ transaction {
199+ prepare(acct: auth(Storage) &Account) {
200+ // Test getting account keys for a known mainnet account
201+ let account = getAccount(0xe467b9dd11fa00df)
202+
203+ // Test accessing specific key indices
204+ let key0 = account.keys.get(keyIndex: 0)
205+ if key0 != nil {
206+ log("Key 0 weight: ".concat(key0!.weight.toString()))
207+ if key0!.isRevoked {
208+ log("Key 0 is revoked")
209+ } else {
210+ log("Key 0 is not revoked")
211+ }
212+ }
213+
214+ let key1 = account.keys.get(keyIndex: 1)
215+ if key1 != nil {
216+ log("Key 1 weight: ".concat(key1!.weight.toString()))
217+ if key1!.isRevoked {
218+ log("Key 1 is revoked")
219+ } else {
220+ log("Key 1 is not revoked")
221+ }
222+ }
223+
224+ // Test that we can access keys without errors
225+ log("Account key access test completed")
226+ }
227+ }
228+ ` )
229+
230+ latest , err := srv .Emulator ().GetLatestBlock ()
231+ if err != nil {
232+ t .Fatalf ("get latest block: %v" , err )
233+ }
234+ // Allow emulator height to be equal to or one greater than remote (if remote advanced by one between queries)
235+ if latest .Height != remoteHeight + 1 {
236+ t .Fatalf ("fork height mismatch: emulator %d not in {remote, remote+1} where remote=%d" , latest .Height , remoteHeight )
237+ }
238+ sk := srv .Emulator ().ServiceKey ()
239+
240+ tx := flowsdk .NewTransaction ().
241+ SetScript (testAccountScript ).
242+ SetReferenceBlockID (flowsdk .Identifier (latest .ID ())).
243+ SetProposalKey (flowsdk .Address (sk .Address ), sk .Index , sk .SequenceNumber ).
244+ SetPayer (flowsdk .Address (sk .Address )).
245+ AddAuthorizer (flowsdk .Address (sk .Address ))
246+
247+ signer , err := sk .Signer ()
248+ if err != nil {
249+ t .Fatalf ("signer: %v" , err )
250+ }
251+ if err := tx .SignEnvelope (flowsdk .Address (sk .Address ), sk .Index , signer ); err != nil {
252+ t .Fatalf ("sign envelope: %v" , err )
253+ }
254+ if err := srv .Emulator ().AddTransaction (* convert .SDKTransactionToFlow (* tx )); err != nil {
255+ t .Fatalf ("add tx: %v" , err )
256+ }
257+ if _ , results , err := srv .Emulator ().ExecuteAndCommitBlock (); err != nil {
258+ t .Fatalf ("execute block: %v" , err )
259+ } else {
260+ if len (results ) != 1 {
261+ t .Fatalf ("expected 1 tx result, got %d" , len (results ))
262+ }
263+ r := results [0 ]
264+ if ! r .Succeeded () {
265+ t .Fatalf ("tx failed: %v" , r .Error )
266+ }
267+
268+ // Check that we got meaningful logs about the account keys
269+ logs := r .Logs
270+ hasKeyWeight := false
271+ hasCompletion := false
272+ for _ , log := range logs {
273+ if strings .Contains (log , "weight:" ) {
274+ hasKeyWeight = true
275+ }
276+ if strings .Contains (log , "Account key access test completed" ) {
277+ hasCompletion = true
278+ }
279+ }
280+
281+ if ! hasKeyWeight {
282+ t .Fatalf ("expected log with key weight, got: %v" , logs )
283+ }
284+ if ! hasCompletion {
285+ t .Fatalf ("expected completion log, got: %v" , logs )
286+ }
287+
288+ t .Logf ("Account key test successful. Logs: %v" , logs )
289+ }
290+ }
0 commit comments