synapsecns/sanguine

View on GitHub
services/explorer/node/explorer_test.go

Summary

Maintainability
D
2 days
Test Coverage
package node_test

import (
    "fmt"
    "github.com/brianvoe/gofakeit/v6"
    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    . "github.com/stretchr/testify/assert"
    "github.com/synapsecns/sanguine/ethergo/backends"
    "github.com/synapsecns/sanguine/ethergo/contracts"
    "github.com/synapsecns/sanguine/ethergo/mocks"
    indexerConfig "github.com/synapsecns/sanguine/services/explorer/config/indexer"
    "github.com/synapsecns/sanguine/services/explorer/contracts/bridge/testbridge"
    "github.com/synapsecns/sanguine/services/explorer/contracts/bridgeconfig"
    "github.com/synapsecns/sanguine/services/explorer/contracts/swap/testswap"
    "github.com/synapsecns/sanguine/services/explorer/db/sql"
    "github.com/synapsecns/sanguine/services/explorer/node"
    "github.com/synapsecns/sanguine/services/explorer/testutil/testcontracts"
    "math/big"
    "os"
)

var testTokens = make(map[uint32]TestToken)

// TestLive tests live recording of events.
func (c NodeSuite) TestLive() {
    if os.Getenv("CI") != "" {
        c.T().Skip("Network / processing test flake")
    }
    chainConfigs := []indexerConfig.ChainConfig{}
    backends := make(map[uint32]bind.ContractBackend)
    // ethclient.DialContext(ctx, chainConfig.RPCURL)
    for k := range c.testBackends {
        testTokens[k] = TestToken{
            tokenID: gofakeit.FirstName(),
            BridgeConfigV3Token: bridgeconfig.BridgeConfigV3Token{
                ChainId:       big.NewInt(int64(k)),
                TokenAddress:  mocks.MockAddress().String(),
                TokenDecimals: gofakeit.Uint8(),
                MaxSwap:       new(big.Int).SetUint64(gofakeit.Uint64()),
                // TODO: this should probably be smaller than maxswap
                MinSwap:       new(big.Int).SetUint64(gofakeit.Uint64()),
                SwapFee:       new(big.Int).SetUint64(gofakeit.Uint64()),
                MaxSwapFee:    new(big.Int).SetUint64(gofakeit.Uint64()),
                MinSwapFee:    new(big.Int).SetUint64(gofakeit.Uint64()),
                HasUnderlying: gofakeit.Bool(),
                IsUnderlying:  gofakeit.Bool(),
            }}
    }

    var deployInfo contracts.DeployedContract
    deployInfo, bridgeConfigContract := c.deployManager.GetBridgeConfigV3(c.GetTestContext(), c.testBackends[c.blockConfigChainID])
    for _, token := range testTokens {
        auth := c.testBackends[c.blockConfigChainID].GetTxContext(c.GetTestContext(), deployInfo.OwnerPtr())
        tx, err := token.SetTokenConfig(bridgeConfigContract, auth)
        c.Require().NoError(err)
        c.testBackends[c.blockConfigChainID].WaitForConfirmation(c.GetTestContext(), tx)
    }
    for k := range c.testBackends {
        backends[k] = c.testBackends[k]
        bridgeContract, bridgeRef := c.testDeployManager.GetTestSynapseBridge(c.GetTestContext(), c.testBackends[k])
        swapContractA, swapRefA := c.testDeployManager.GetTestSwapFlashLoan(c.GetTestContext(), c.testBackends[k])
        testDeployManagerB := testcontracts.NewDeployManager(c.T())
        swapContractB, swapRefB := testDeployManagerB.GetTestSwapFlashLoan(c.GetTestContext(), c.testBackends[k])
        transactOpts := c.testBackends[k].GetTxContext(c.GetTestContext(), nil)

        contracts := []indexerConfig.ContractConfig{
            {
                ContractType: "bridge",
                Address:      bridgeContract.Address().String(),
                StartBlock:   0,
            },

            {
                ContractType: "swap",
                Address:      swapContractA.Address().String(),
                StartBlock:   0,
            },
            {
                ContractType: "swap",
                Address:      swapContractB.Address().String(),
                StartBlock:   0,
            },
        }
        chainConfigs = append(chainConfigs, indexerConfig.ChainConfig{
            ChainID:             k,
            RPCURL:              gofakeit.URL(),
            FetchBlockIncrement: 100,
            MaxGoroutines:       5,
            Contracts:           contracts,
        })
        // go through each contract and save the end height in scribe
        for i := range contracts {
            //  the last block store per contract
            err := c.eventDB.StoreLastIndexed(c.GetTestContext(), common.HexToAddress(contracts[i].Address), k, 12, false)
            Nil(c.T(), err)
        }
        c.fillBlocks(bridgeRef, swapRefA, swapRefB, transactOpts, k)
    }

    // This structure is for reference
    explorerConfig := indexerConfig.Config{
        DefaultRefreshRate:  2,
        ScribeURL:           c.gqlClient.Client.BaseURL,
        BridgeConfigAddress: deployInfo.Address().String(),
        BridgeConfigChainID: c.blockConfigChainID,
        Chains:              chainConfigs,
    }

    explorerBackfiller, err := node.NewExplorerBackfiller(c.db, explorerConfig, backends, c.explorerMetrics)
    c.Nil(err)
    c.NotNil(explorerBackfiller)
    err = explorerBackfiller.Backfill(c.GetTestContext(), false)
    c.Nil(err)
    var counttemp int64
    dd := c.db.UNSAFE_DB().WithContext(c.GetTestContext()).Table("swap_events").Count(&counttemp)
    c.Nil(dd.Error)

    var count int64
    bridgeEvents := c.db.UNSAFE_DB().WithContext(c.GetTestContext()).Find(&sql.BridgeEvent{}).Count(&count)
    Nil(c.T(), bridgeEvents.Error)
    Equal(c.T(), int64(10*len(c.testBackends)), count)

    swapEvents := c.db.UNSAFE_DB().WithContext(c.GetTestContext()).Find(&sql.SwapEvent{}).Count(&count)
    Nil(c.T(), swapEvents.Error)
    Equal(c.T(), int64(10*len(c.testBackends)), count)

    for k := range c.testBackends {
        bridgeEventsChain := c.db.UNSAFE_DB().WithContext(c.GetTestContext()).Model(&sql.BridgeEvent{}).Where(&sql.BridgeEvent{ChainID: k}).Count(&count)
        Nil(c.T(), bridgeEventsChain.Error)
        Equal(c.T(), int64(10), count)

        swapEventsChain := c.db.UNSAFE_DB().WithContext(c.GetTestContext()).Model(&sql.SwapEvent{}).Where(&sql.SwapEvent{ChainID: k}).Count(&count)
        Nil(c.T(), swapEventsChain.Error)
        Equal(c.T(), int64(10), count)
    }
}

// nolinting until parity tests implemented
//
//nolint:unparam
func (c *NodeSuite) storeTestLog(tx *types.Transaction, chainID uint32, blockNumber uint64) (*types.Log, error) {
    c.testBackends[chainID].WaitForConfirmation(c.GetTestContext(), tx)
    receipt, err := c.testBackends[chainID].TransactionReceipt(c.GetTestContext(), tx.Hash())

    if err != nil {
        return nil, fmt.Errorf("failed to get receipt for transaction %s: %w", tx.Hash().String(), err)
    }

    receipt.Logs[0].BlockNumber = blockNumber

    err = c.eventDB.StoreLogs(c.GetTestContext(), chainID, *receipt.Logs[0])
    if err != nil {
        return nil, fmt.Errorf("error storing swap log: %w", err)
    }

    return receipt.Logs[0], nil
}

func (c NodeSuite) fillBlocks(bridgeRef *testbridge.TestBridgeRef, swapRefA *testswap.TestSwapRef, swapRefB *testswap.TestSwapRef, transactOpts backends.AuthType, chainID uint32) {
    // Store blocktimes for testing defillama and timestamp indexing.
    for i := uint64(0); i < 13; i++ {
        err := c.eventDB.StoreBlockTime(c.GetTestContext(), chainID, i, i)
        Nil(c.T(), err)
    }

    bridgeTx, err := bridgeRef.TestDeposit(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(5)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 5)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestRedeem(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(int64(gofakeit.Uint32()))), big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(5)), 2)
    _, err = c.storeTestLog(bridgeTx, chainID, 5)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestWithdraw(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), [32]byte{byte(gofakeit.Uint64())})
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(6)), 3)
    _, err = c.storeTestLog(bridgeTx, chainID, 6)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestMint(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), [32]byte{byte(gofakeit.Uint64())})
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(6)), 4)
    _, err = c.storeTestLog(bridgeTx, chainID, 6)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestDepositAndSwap(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), gofakeit.Uint8(), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(6)), 5)
    _, err = c.storeTestLog(bridgeTx, chainID, 6)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestRedeemAndSwap(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), gofakeit.Uint8(), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(7)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 7)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestRedeemAndRemove(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(8)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 8)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestMintAndSwap(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), common.BigToAddress(big.NewInt(gofakeit.Int64())), gofakeit.Uint8(), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), [32]byte{byte(gofakeit.Uint64())})
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(9)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 9)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestWithdrawAndRemove(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), common.BigToAddress(big.NewInt(gofakeit.Int64())), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint32())), big.NewInt(int64(gofakeit.Uint32())), [32]byte{byte(gofakeit.Uint64())})
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(10)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 10)
    Nil(c.T(), err)

    bridgeTx, err = bridgeRef.TestRedeemV2(transactOpts.TransactOpts, [32]byte{byte(gofakeit.Uint64())}, big.NewInt(int64(chainID)), common.HexToAddress(testTokens[chainID].TokenAddress), big.NewInt(int64(gofakeit.Uint32())))
    Nil(c.T(), err)
    c.storeEthTx(bridgeTx, big.NewInt(int64(chainID)), big.NewInt(int64(12)), 1)
    _, err = c.storeTestLog(bridgeTx, chainID, 12)
    Nil(c.T(), err)

    // Store every swap event across two different swap contracts.
    swapTx, err := swapRefA.TestSwap(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(5)), 1)
    _, err = c.storeTestLog(swapTx, chainID, 5)
    Nil(c.T(), err)

    swapTx, err = swapRefB.TestAddLiquidity(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), []*big.Int{big.NewInt(int64(gofakeit.Uint64()))}, []*big.Int{big.NewInt(int64(gofakeit.Uint64()))}, big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(5)), 2)
    _, err = c.storeTestLog(swapTx, chainID, 5)
    Nil(c.T(), err)

    swapTx, err = swapRefB.TestRemoveLiquidity(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), []*big.Int{big.NewInt(int64(gofakeit.Uint64()))}, big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(6)), 1)
    _, err = c.storeTestLog(swapTx, chainID, 6)
    Nil(c.T(), err)

    swapTx, err = swapRefA.TestRemoveLiquidityOne(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(7)), 1)
    _, err = c.storeTestLog(swapTx, chainID, 7)
    Nil(c.T(), err)

    swapTx, err = swapRefA.TestRemoveLiquidityImbalance(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), []*big.Int{big.NewInt(int64(gofakeit.Uint64()))}, []*big.Int{big.NewInt(int64(gofakeit.Uint64()))}, big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(8)), 1)
    _, err = c.storeTestLog(swapTx, chainID, 8)
    Nil(c.T(), err)

    swapTx, err = swapRefB.TestNewAdminFee(transactOpts.TransactOpts, big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(8)), 2)
    _, err = c.storeTestLog(swapTx, chainID, 8)
    Nil(c.T(), err)

    swapTx, err = swapRefA.TestNewSwapFee(transactOpts.TransactOpts, big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(8)), 3)
    _, err = c.storeTestLog(swapTx, chainID, 8)
    Nil(c.T(), err)

    swapTx, err = swapRefA.TestRampA(transactOpts.TransactOpts, big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(9)), 1)
    _, err = c.storeTestLog(swapTx, chainID, 9)
    Nil(c.T(), err)

    swapTx, err = swapRefB.TestStopRampA(transactOpts.TransactOpts, big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(9)), 2)
    _, err = c.storeTestLog(swapTx, chainID, 9)
    Nil(c.T(), err)

    swapTx, err = swapRefA.TestFlashLoan(transactOpts.TransactOpts, common.BigToAddress(big.NewInt(gofakeit.Int64())), gofakeit.Uint8(), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())), big.NewInt(int64(gofakeit.Uint64())))
    Nil(c.T(), err)
    c.storeEthTx(swapTx, big.NewInt(int64(chainID)), big.NewInt(int64(9)), 3)
    _, err = c.storeTestLog(swapTx, chainID, 9)
    Nil(c.T(), err)
}

// storeEthTx stores the eth transaction so the get sender functionality can be tested.
func (c *NodeSuite) storeEthTx(tx *types.Transaction, chainID *big.Int, blockNumber *big.Int, index int) {
    err := c.eventDB.StoreEthTx(c.GetTestContext(), tx, uint32(chainID.Uint64()), common.BigToHash(blockNumber), blockNumber.Uint64(), uint64(index))
    Nil(c.T(), err)
}