synapsecns/sanguine

View on GitHub
services/cctp-relayer/relayer/synapse_test.go

Summary

Maintainability
A
0 mins
Test Coverage
package relayer_test

import (
    "context"
    "math/big"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/synapsecns/sanguine/services/cctp-relayer/attestation"
    "github.com/synapsecns/sanguine/services/cctp-relayer/contracts/cctp"
    "github.com/synapsecns/sanguine/services/cctp-relayer/relayer"
    relayTypes "github.com/synapsecns/sanguine/services/cctp-relayer/types"
    omniClient "github.com/synapsecns/sanguine/services/omnirpc/client"
)

func (s *CCTPRelayerSuite) TestHandleCircleRequestSent() {
    // setup
    originChain := s.testBackends[0]
    destChain := s.testBackends[1]
    _, originSynapseCCTP := s.deployManager.GetSynapseCCTP(s.GetTestContext(), originChain)
    _, originMockUsdc := s.deployManager.GetMockMintBurnTokenType(s.GetTestContext(), originChain)

    // create a new relayer
    mockAPI := attestation.NewMockCircleAPI()
    omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
    relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
    s.Nil(err)

    // mint token
    opts := originChain.GetTxContext(s.GetTestContext(), nil)
    amount := big.NewInt(1000000000000000000)
    tx, err := originMockUsdc.MintPublic(opts.TransactOpts, opts.From, amount)
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // approve token
    tx, err = originMockUsdc.Approve(opts.TransactOpts, originSynapseCCTP.Address(), amount)
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // send token
    tx, err = originSynapseCCTP.SendCircleToken(opts.TransactOpts, opts.From, big.NewInt(int64(destChain.GetChainID())), originMockUsdc.Address(), amount, 0, []byte{})
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // handle send request
    msg, err := relay.FetchAndProcessSentEvent(s.GetTestContext(), tx.Hash(), uint32(originChain.GetChainID()))
    s.Nil(err)
    s.Equal(msg.OriginTxHash, tx.Hash().String())
    s.Equal(msg.State, relayTypes.Pending)

    // verify that the request is stored in the db
    var storedMsg relayTypes.Message
    err = s.testStore.DB().Where("origin_tx_hash = ?", msg.OriginTxHash).First(&storedMsg).Error
    s.Nil(err)
    s.Equal(*msg, storedMsg)
}

func (s *CCTPRelayerSuite) TestStoreCircleRequestFulfilled() {
    // setup
    originChain := s.testBackends[0]
    destChain := s.testBackends[1]

    // create a new relayer
    mockAPI := attestation.NewMockCircleAPI()
    omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
    relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
    s.Nil(err)

    // inject a fulfilled event to be handled by relayer
    blockNumber, err := destChain.BlockNumber(s.GetTestContext())
    s.Nil(err)
    message1 := s.mockMessage(uint32(originChain.GetChainID()), uint32(destChain.GetChainID()), uint32(blockNumber))
    fulfilledLog := &types.Log{
        TxHash:      common.HexToHash(message1.DestTxHash),
        BlockNumber: blockNumber,
    }
    var requestID [32]byte
    requestIDBytes := common.Hex2Bytes(message1.RequestID)
    copy(requestID[:], requestIDBytes)
    fulfilledEvent := &cctp.SynapseCCTPEventsCircleRequestFulfilled{
        OriginDomain: message1.OriginChainID,
        RequestID:    requestID,
    }
    err = relay.StoreCircleRequestFulfilled(s.GetTestContext(), fulfilledLog, fulfilledEvent, uint32(destChain.GetChainID()))
    s.Nil(err)

    // verify that the request is stored as Complete in the db
    var storedMsg relayTypes.Message
    err = s.testStore.DB().Where("request_id = ?", message1.RequestID).First(&storedMsg).Error
    s.Nil(err)
    s.Equal(message1.DestTxHash, storedMsg.DestTxHash)
    s.Equal(message1.OriginChainID, storedMsg.OriginChainID)
    s.Equal(message1.DestChainID, storedMsg.DestChainID)
    s.Equal(message1.RequestID, storedMsg.RequestID)
    s.Equal(message1.BlockNumber, storedMsg.BlockNumber)
    s.Equal(relayTypes.Complete, storedMsg.State)

    // store a Submitted message
    message2 := s.mockMessage(uint32(originChain.GetChainID()), uint32(destChain.GetChainID()), uint32(blockNumber))
    err = s.testStore.DB().Create(&message2).Error
    s.Nil(err)

    // process the corresponding fulfilled event
    requestIDBytes = common.Hex2Bytes(message2.RequestID)
    copy(requestID[:], requestIDBytes)
    fulfilledEvent = &cctp.SynapseCCTPEventsCircleRequestFulfilled{
        RequestID: requestID,
    }
    err = relay.StoreCircleRequestFulfilled(s.GetTestContext(), &types.Log{TxHash: common.HexToHash(message2.DestTxHash)}, fulfilledEvent, uint32(destChain.GetChainID()))
    s.Nil(err)

    // verify that the message is stored as Complete in the db
    err = s.testStore.DB().Where("request_id = ?", message2.RequestID).First(&storedMsg).Error
    s.Nil(err)
    message2.State = relayTypes.Complete
    s.Equal(message2, storedMsg)
}

func (s *CCTPRelayerSuite) TestSubmitReceiveCircleToken() {
    // create a new relayer
    mockAPI := attestation.NewMockCircleAPI()
    omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
    relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
    s.Nil(err)

    // build test msg
    testHash := "0x5dba62229dba62f233dca8f3fd14488fdc45d2a86537da2dea7a5683b5e7f622"
    originChain := s.testBackends[0]
    originChainID, err := originChain.ChainID(s.GetTestContext())
    s.Nil(err)
    destChain := s.testBackends[1]
    destChainID, err := destChain.ChainID(s.GetTestContext())
    s.Nil(err)
    msg := relayTypes.Message{
        OriginTxHash:     testHash,
        OriginChainID:    uint32(originChainID.Int64()),
        DestChainID:      uint32(destChainID.Int64()),
        Message:          []byte{},
        MessageHash:      testHash,
        FormattedRequest: []byte{},
    }

    // submit ReceiveCircleToken()
    // nolint: wrapcheck
    err = relay.SubmitReceiveMessage(s.GetTestContext(), &msg)
    s.Nil(err)

    // verify that the attested request is stored in the db
    var storedMsg relayTypes.Message
    err = s.testStore.DB().Where("origin_tx_hash = ?", msg.OriginTxHash).First(&storedMsg).Error
    s.Nil(err)
    msg.State = relayTypes.Submitted
    s.Equal(msg, storedMsg)
}

func (s *CCTPRelayerSuite) TestBridgeSynapseCCTP() {
    // create a new relayer
    mockAPI := attestation.NewMockCircleAPI()
    omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
    relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
    s.Nil(err)

    // start relayer
    ctx, cancel := context.WithCancel(s.GetTestContext())
    defer cancel()
    //nolint:errcheck
    go relay.Run(ctx)

    // mint some USDC on send chain
    originChain := s.testBackends[0]
    destChain := s.testBackends[1]
    _, originMockUsdc := s.deployManager.GetMockMintBurnTokenType(s.GetTestContext(), originChain)
    originChainID, err := originChain.ChainID(s.GetTestContext())
    s.Nil(err)
    bridgeAmount := big.NewInt(1000000000) // 1000 USDC
    opts := originChain.GetTxContext(s.GetTestContext(), nil)
    tx, err := originMockUsdc.MintPublic(opts.TransactOpts, opts.From, bridgeAmount)
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // approve USDC for spending
    _, originSynapseCCTP := s.deployManager.GetSynapseCCTP(s.GetTestContext(), originChain)
    tx, err = originMockUsdc.Approve(opts.TransactOpts, originSynapseCCTP.Address(), bridgeAmount)
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // send USDC from originChain
    destChainID, err := destChain.ChainID(s.GetTestContext())
    s.Nil(err)
    tx, err = originSynapseCCTP.SendCircleToken(opts.TransactOpts, opts.From, destChainID, originMockUsdc.Address(), bridgeAmount, 0, []byte{})
    s.Nil(err)
    originChain.WaitForConfirmation(s.GetTestContext(), tx)

    // TODO: figure out why log is not streamed properly by relayer.
    // for now, inject the log manually
    receipt, err := originChain.TransactionReceipt(s.GetTestContext(), tx.Hash())
    s.Nil(err)
    var sentLog *types.Log
    for _, log := range receipt.Logs {
        if log.Topics[0] == cctp.CircleRequestSentTopic {
            sentLog = log
            break
        }
    }
    s.Require().NotNil(sentLog)
    _, err = relay.HandleLog(s.GetTestContext(), sentLog, uint32(originChainID.Int64()))
    s.Require().Nil(err)

    // verify that the confirmed request is stored in the backend
    s.Eventually(func() bool {
        var storedMsg relayTypes.Message
        // TODO: shuld make this check for completion
        err = s.testStore.DB().Where("state = ?", relayTypes.Submitted).Last(&storedMsg).Error
        if err != nil {
            return false
        }
        return storedMsg.OriginTxHash == tx.Hash().String()
    })

    // TODO: verify USDC is credited on recv chain
    // _, recvMockUsdc := c.deployManager.GetMockMintBurnTokenType(c.GetTestContext(), destChain)
    // c.Nil(err)
    // expectedBalance := bridgeAmount
    // c.Eventually(func() bool {
    //     balance, err := recvMockUsdc.BalanceOf(nil, opts.From)
    //     c.Nil(err)
    //     return c.Equal(expectedBalance, balance)
    // })
}