@@ -30,7 +30,7 @@ import (
3030// This is using the TxSealValidation mechanism for validating submitted
3131// transactions, which blocks until it receives the Flow transaction result.
3232func Test_ConcurrentTransactionSubmissionWithTxSeal (t * testing.T ) {
33- srv , err := startEmulator (true )
33+ srv , err := startEmulator (true , defaultServerConfig () )
3434 require .NoError (t , err )
3535
3636 ctx , cancel := context .WithCancel (context .Background ())
@@ -141,7 +141,7 @@ func Test_ConcurrentTransactionSubmissionWithTxSeal(t *testing.T) {
141141// This is using the LocalIndexValidation mechanism for validating submitted
142142// transactions, which is non-blocking and validates based on the local state.
143143func Test_ConcurrentTransactionSubmissionWithLocalIndex (t * testing.T ) {
144- srv , err := startEmulator (true )
144+ srv , err := startEmulator (true , defaultServerConfig () )
145145 require .NoError (t , err )
146146
147147 ctx , cancel := context .WithCancel (context .Background ())
@@ -246,7 +246,7 @@ func Test_ConcurrentTransactionSubmissionWithLocalIndex(t *testing.T) {
246246}
247247
248248func Test_EthClientTest (t * testing.T ) {
249- srv , err := startEmulator (true )
249+ srv , err := startEmulator (true , defaultServerConfig () )
250250 require .NoError (t , err )
251251
252252 ctx , cancel := context .WithCancel (context .Background ())
@@ -325,7 +325,7 @@ func Test_CloudKMSConcurrentTransactionSubmission(t *testing.T) {
325325 t .Skip ()
326326 }
327327
328- srv , err := startEmulator (true )
328+ srv , err := startEmulator (true , defaultServerConfig () )
329329 require .NoError (t , err )
330330
331331 ctx , cancel := context .WithCancel (context .Background ())
@@ -449,7 +449,7 @@ func Test_CloudKMSConcurrentTransactionSubmission(t *testing.T) {
449449// 2. No transactions are lost
450450// 3. The state remains consistent
451451func Test_ForceStartHeightIdempotency (t * testing.T ) {
452- srv , err := startEmulator (true )
452+ srv , err := startEmulator (true , defaultServerConfig () )
453453 require .NoError (t , err )
454454
455455 ctx , cancel := context .WithCancel (context .Background ())
@@ -592,3 +592,112 @@ func Test_ForceStartHeightIdempotency(t *testing.T) {
592592 return true
593593 }, time .Second * 15 , time .Second * 1 , "all transactions were not executed" )
594594}
595+
596+ // Test_AccessNodeBackupFunctionality verifies that the specified AccessNode
597+ // backup hosts, are used when the primary `AccessNodeHost` is unavailable
598+ // or whatever reason.
599+ func Test_AccessNodeBackupFunctionality (t * testing.T ) {
600+ srv , err := startEmulator (true , defaultServerConfig ())
601+ require .NoError (t , err )
602+
603+ ctx , cancel := context .WithCancel (context .Background ())
604+ defer func () {
605+ cancel ()
606+ srv .Stop ()
607+ }()
608+
609+ backupConfg := defaultServerConfig ()
610+ backupConfg .GRPCPort = 3599
611+ backupConfg .RESTPort = 9999
612+ backupConfg .AdminPort = 9090
613+ backupConfg .DebuggerPort = 3456
614+ backupSrv , err := startEmulator (true , backupConfg )
615+ require .NoError (t , err )
616+
617+ _ , backupCancel := context .WithCancel (context .Background ())
618+ defer func () {
619+ backupCancel ()
620+ backupSrv .Stop ()
621+ }()
622+
623+ grpcHost := "localhost:3569"
624+ emu := srv .Emulator ()
625+ service := emu .ServiceKey ()
626+
627+ client , err := grpc .NewClient (grpcHost )
628+ require .NoError (t , err )
629+
630+ time .Sleep (500 * time .Millisecond ) // some time to startup
631+
632+ // create new account with keys used for key-rotation
633+ keyCount := 5
634+ createdAddr , privateKey , err := bootstrap .CreateMultiKeyAccount (
635+ client ,
636+ keyCount ,
637+ service .Address ,
638+ sc .FungibleToken .Address .HexWithPrefix (),
639+ sc .FlowToken .Address .HexWithPrefix (),
640+ service .PrivateKey ,
641+ )
642+ require .NoError (t , err )
643+
644+ backupAccessNodeHost := fmt .Sprintf ("localhost:%d" , backupConfg .GRPCPort )
645+
646+ cfg := config.Config {
647+ DatabaseDir : t .TempDir (),
648+ AccessNodeHost : grpcHost ,
649+ AccessNodeBackupHosts : []string {backupAccessNodeHost },
650+ RPCPort : 8545 ,
651+ RPCHost : "127.0.0.1" ,
652+ FlowNetworkID : "flow-emulator" ,
653+ EVMNetworkID : types .FlowEVMPreviewNetChainID ,
654+ Coinbase : eoaTestAccount ,
655+ COAAddress : * createdAddr ,
656+ COAKey : privateKey ,
657+ GasPrice : new (big.Int ).SetUint64 (0 ),
658+ EnforceGasPrice : true ,
659+ LogLevel : zerolog .DebugLevel ,
660+ LogWriter : testLogWriter (),
661+ TxStateValidation : config .LocalIndexValidation ,
662+ }
663+
664+ boot , err := bootstrap .New (cfg )
665+ require .NoError (t , err )
666+ defer func () {
667+ // Stop the EVM GW service
668+ boot .Stop ()
669+ }()
670+
671+ ready := make (chan struct {})
672+ go func () {
673+ err = boot .Run (ctx , cfg , func () {
674+ close (ready )
675+ })
676+ require .NoError (t , err )
677+ }()
678+
679+ <- ready
680+
681+ time .Sleep (3 * time .Second ) // some time to startup
682+
683+ ethClient , err := ethclient .Dial ("http://127.0.0.1:8545" )
684+ require .NoError (t , err )
685+
686+ // This endpoint (`eth_syncing`), will make the following gRPC call,
687+ // `ExecuteScriptAtLatestBlock`. This gRPC call is served by the
688+ // first Emulator process, that is configured as the `AccessNodeHost`.
689+ _ , err = ethClient .SyncProgress (context .Background ())
690+ require .NoError (t , err )
691+
692+ // Shutdown the first Emulator process, that is configured as the
693+ // `AccessNodeHost`
694+ cancel ()
695+ srv .Stop ()
696+
697+ // This endpoint (`eth_syncing`), will make the following gRPC call,
698+ // `ExecuteScriptAtLatestBlock`. This gRPC call is served by the
699+ // first-available AccessNode specified in `AccessNodeBackupHosts`.
700+ // In this E2E test, that would be the second Emulator process.
701+ _ , err = ethClient .SyncProgress (context .Background ())
702+ require .NoError (t , err )
703+ }
0 commit comments