github.com/0xsequence/ethkit@v1.25.0/ethmonitor/ethmonitor_test.go (about)

     1  package ethmonitor_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"os/exec"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/0xsequence/ethkit/ethmonitor"
    13  	"github.com/0xsequence/ethkit/ethrpc"
    14  	"github.com/0xsequence/ethkit/go-ethereum/common"
    15  	"github.com/0xsequence/ethkit/util"
    16  	"github.com/go-chi/httpvcr"
    17  	"github.com/stretchr/testify/assert"
    18  )
    19  
    20  func TestMonitorBasic(t *testing.T) {
    21  	testConfig, err := util.ReadTestConfig("../ethkit-test.json")
    22  	if err != nil {
    23  		t.Error(err)
    24  	}
    25  
    26  	ethNodeURL := testConfig["GOERLI_URL"]
    27  	if ethNodeURL == "" {
    28  		ethNodeURL = "http://localhost:8545"
    29  	}
    30  
    31  	ctx := context.Background()
    32  
    33  	vcr := httpvcr.New("ethmonitor_goerli")
    34  	vcr.Start(ctx)
    35  	defer vcr.Stop()
    36  
    37  	vcr.URLRewriter = func(url string) string {
    38  		// rewrite the url to hide the API keys
    39  		return "http://goerli/"
    40  	}
    41  
    42  	monitorOptions := ethmonitor.DefaultOptions
    43  	if vcr.Mode() == httpvcr.ModeReplay {
    44  		// change options to run replay tests faster
    45  		monitorOptions.PollingInterval = 5 * time.Millisecond
    46  	}
    47  
    48  	provider, err := ethrpc.NewProvider(ethNodeURL)
    49  	assert.NoError(t, err)
    50  
    51  	monitor, err := ethmonitor.NewMonitor(provider, monitorOptions)
    52  	assert.NoError(t, err)
    53  
    54  	go func(t *testing.T) {
    55  		err := monitor.Run(context.Background())
    56  		if err != nil {
    57  			panic(err)
    58  		}
    59  	}(t)
    60  	defer monitor.Stop()
    61  
    62  	sub := monitor.Subscribe("TestMonitorBasic")
    63  	defer sub.Unsubscribe()
    64  
    65  	subs := []ethmonitor.Subscription{}
    66  	go func() {
    67  		for i := 0; i < 10; i++ {
    68  			s := monitor.Subscribe(fmt.Sprintf("TestMonitorBasic/sub/%d", i))
    69  			subs = append(subs, s)
    70  		}
    71  
    72  		time.Sleep(1 * time.Second)
    73  		for _, s := range subs {
    74  			s.Unsubscribe()
    75  		}
    76  	}()
    77  
    78  	go func() {
    79  		for {
    80  			select {
    81  			case blocks := <-sub.Blocks():
    82  				_ = blocks
    83  
    84  				for _, b := range blocks {
    85  					fmt.Println("event:", b.Event, "block:", b.NumberU64(), b.Hash().Hex(), "parent:", b.ParentHash().Hex(), "# logs:", len(b.Logs))
    86  				}
    87  
    88  				finalBlock := monitor.LatestFinalBlock(3)
    89  				if finalBlock != nil {
    90  					fmt.Println("finalized block #", finalBlock.NumberU64())
    91  				}
    92  
    93  			case <-sub.Done():
    94  				return
    95  			}
    96  		}
    97  	}()
    98  
    99  	// Wait for requests to complete
   100  	select {
   101  	case <-vcr.Done():
   102  		break
   103  	case <-time.After(5 * time.Minute): // max amount of time to run the vcr recorder
   104  		break
   105  	}
   106  	monitor.Stop()
   107  
   108  	// Perform assertions
   109  	blocks := monitor.Chain().Blocks()
   110  
   111  	for _, b := range blocks {
   112  		_ = b
   113  		// fmt.Println("=> block", b.Hash())
   114  	}
   115  
   116  	expectedBlockHashes := []string{
   117  		"0x7f06fd3877f4d664c4525d33a0ada20287918dd0280b372ca859481bd09c81c4",
   118  		"0x0c4430bce8b4498657b88646827318c5d35cc52d3992d1fb97c28bcd69f0ff20",
   119  		"0x9e385c0a6a749f2b51b7ccf0412d02cb4e82e2600c56477b750fd580e5c98273",
   120  		"0x83fdf84b3cbfb4b0093025f9cc2db974e865d121b67dafafb70c2343837ccee2",
   121  		"0xdc482579c13aa25d0ae9ade680751ef8f80cf771b29ea9d3bb47af2bb474e251",
   122  		"0x2a45220ddbb622dad9107e84f3694b7fb03c52ced9e61e7293240595ffd5e620",
   123  		"0x4b45986f0409622d1fe90f476357135031aa5eee7d73586393c42524d20c1c12",
   124  		"0x377741ca51322567ab4d8f72226fd8d282bfc0c31cd506370430d71686c63641",
   125  		"0x0a096c3f639e40cdbf06dab7fca394310ddd689456aa0131edd94a495f706e68",
   126  		"0x56f1c94e1f3b466e4326f248e43d99d1bc8f55bc3147315a361dd4986fa5bd93",
   127  		"0x420d67c7494ab8ab878d4cbd66a451d60491794b54f3bb16bbbb76ac48f15072",
   128  		"0x8e00e1a3938f01b255e1cef3d8083730058745f0ae98f5cebc654d7ed6db3dbb",
   129  		"0xad24f03ed8bdfb9637232384e3257e076a47f3ac583492e8463a8d1a5fcdd31f",
   130  		"0x1147f217faa9a366f623d02453d099e60ddb30df36ec7ef9e80cc21adb43a730",
   131  		"0x253b4383d2e9afa7dbba30da47e44a691bd804114a10b37b803881fdc380da2a",
   132  		"0xb4cff922dd7cd5257acfdc433d672743dd1673030869da6f2278d4358803cf3c",
   133  		"0x5bc9ab2442688acb26b8e941660c62915418627452807367b9919011c4808b24",
   134  		"0x055ed7914285e38e30c69ae95bea80b49490a68d4057bd24b44b5444fcf34e70",
   135  		"0x9120c53b4d31cc0188e7e49b201df5ce091655491e37820f613cf7d44bb93bd7",
   136  		"0xdb1c79799e99a62c9dba44c7fb30e75f48da5f0ce67fdc4a2ecc08bf80bf52fb",
   137  	}
   138  
   139  	assert.True(t, len(expectedBlockHashes) <= len(blocks), "expected blocks returned part of retention")
   140  
   141  	for i := range expectedBlockHashes {
   142  		assert.Equal(t, expectedBlockHashes[i], blocks[i].Hash().Hex())
   143  	}
   144  
   145  	assert.NotNil(t, monitor.GetBlock(common.HexToHash("0x2a45220ddbb622dad9107e84f3694b7fb03c52ced9e61e7293240595ffd5e620")))
   146  	assert.Equal(t, common.HexToHash("0xdb1c79799e99a62c9dba44c7fb30e75f48da5f0ce67fdc4a2ecc08bf80bf52fb"), monitor.LatestBlock().Hash())
   147  
   148  	// only subscriber left is the main one
   149  	assert.True(t, monitor.NumSubscribers() == 1)
   150  }
   151  
   152  func GetIp(index uint) string {
   153  	output, err := exec.Command("yarn", "--silent", "--cwd", "../tools/reorgme", "chain:ip", "0").CombinedOutput()
   154  
   155  	if err != nil {
   156  		os.Stderr.WriteString(err.Error())
   157  	}
   158  
   159  	return strings.Replace(string(output), "\n", "", 1)
   160  }
   161  
   162  func WaitBlock(ctx context.Context, provider *ethrpc.Provider) error {
   163  	var lastBlock = uint64(0)
   164  
   165  	fmt.Println("Waiting a block")
   166  
   167  	for {
   168  		block, err := provider.BlockNumber(ctx)
   169  		if err != nil {
   170  			return err
   171  		}
   172  
   173  		if lastBlock == 0 {
   174  			lastBlock = block
   175  		}
   176  
   177  		if block != lastBlock {
   178  			return nil
   179  		}
   180  	}
   181  }
   182  
   183  func Fork(index uint) string {
   184  	fmt.Println("Forking...")
   185  	output, err := exec.Command("yarn", "--silent", "--cwd", "../tools/reorgme", "chain:fork").CombinedOutput()
   186  
   187  	if err != nil {
   188  		os.Stderr.WriteString(err.Error())
   189  	}
   190  
   191  	fmt.Println("Forked!")
   192  
   193  	return string(output)
   194  }
   195  
   196  func Join(index uint) string {
   197  	fmt.Println("Joining...")
   198  	output, err := exec.Command("yarn", "--silent", "--cwd", "../tools/reorgme", "chain:join").CombinedOutput()
   199  
   200  	if err != nil {
   201  		os.Stderr.WriteString(err.Error())
   202  	}
   203  
   204  	fmt.Println("Joined!")
   205  
   206  	return string(output)
   207  }
   208  
   209  func TestMonitorWithReorgme(t *testing.T) {
   210  	if strings.ToLower(os.Getenv("REORGME")) != "true" {
   211  		t.Logf("REORGME is not enabled, skipping this test case.")
   212  		return
   213  	}
   214  
   215  	ip := GetIp(0)
   216  
   217  	provider, err := ethrpc.NewProvider("http://" + ip + ":8545/")
   218  	assert.NoError(t, err)
   219  
   220  	monitorOptions := ethmonitor.DefaultOptions
   221  	monitorOptions.PollingInterval = 5 * time.Millisecond
   222  
   223  	monitor, err := ethmonitor.NewMonitor(provider, monitorOptions)
   224  	assert.NoError(t, err)
   225  
   226  	go func(t *testing.T) {
   227  		err := monitor.Run(context.Background())
   228  		if err != nil {
   229  			panic(err)
   230  		}
   231  	}(t)
   232  	defer monitor.Stop()
   233  
   234  	sub := monitor.Subscribe("TestMonitorWithReorgme")
   235  	defer sub.Unsubscribe()
   236  
   237  	events := make([]*ethmonitor.Block, 0)
   238  
   239  	go func() {
   240  		for {
   241  			select {
   242  			case blocks := <-sub.Blocks():
   243  				_ = blocks
   244  				for _, b := range blocks {
   245  					events = append(events, b)
   246  					fmt.Println("event:", b.Event, "block:", b.NumberU64(), b.Hash().Hex(), "parent:", b.ParentHash().Hex(), "# logs:", len(b.Logs))
   247  				}
   248  			case <-sub.Done():
   249  				return
   250  			}
   251  		}
   252  	}()
   253  
   254  	Fork(0)
   255  	events = make([]*ethmonitor.Block, 0)
   256  
   257  	WaitBlock(context.Background(), provider)
   258  	WaitBlock(context.Background(), provider)
   259  
   260  	time.Sleep(2 * time.Second)
   261  
   262  	for _, b := range events {
   263  		assert.Equal(t, b.Event, ethmonitor.Added)
   264  	}
   265  
   266  	revertedEvents := events
   267  	events = make([]*ethmonitor.Block, 0)
   268  
   269  	Join(0)
   270  
   271  	// Wait for reorg
   272  	WaitBlock(context.Background(), provider)
   273  	WaitBlock(context.Background(), provider)
   274  
   275  	time.Sleep(2 * time.Second)
   276  
   277  	offset := 0
   278  	for _, e := range events {
   279  		if e.Block.Hash() == revertedEvents[len(revertedEvents)-1].Hash() {
   280  			break
   281  		}
   282  
   283  		offset++
   284  	}
   285  
   286  	for i, b := range revertedEvents {
   287  		ri := len(revertedEvents) - 1 - i + offset
   288  		rb := events[ri]
   289  
   290  		// Should revert last blocks
   291  		assert.Equal(t, rb.Block.Number(), b.Block.Number())
   292  		assert.Equal(t, rb.Block.Hash(), b.Block.Hash())
   293  		assert.Equal(t, b.Event, ethmonitor.Removed)
   294  	}
   295  
   296  	monitor.Stop()
   297  }