Skip to content

Commit

Permalink
rename integration tests, write one for burning on noble, create list…
Browse files Browse the repository at this point in the history
…ener/broadcaster/minter
  • Loading branch information
bd21 committed Oct 2, 2023
1 parent a9bb0aa commit 72ae89d
Show file tree
Hide file tree
Showing 11 changed files with 363 additions and 46 deletions.
104 changes: 104 additions & 0 deletions cmd/ethereum/broadcast.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package ethereum

import (
"context"
"cosmossdk.io/log"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
ctypes "github.com/cometbft/cometbft/rpc/core/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"io"
"net/http"
"strconv"
"time"
)

// Broadcast broadcasts a message to Ethereum
func Broadcast(
cfg config.Config,
logger log.Logger,
msg *types.MessageState,
sequenceMap *types.SequenceMap,
) (*ctypes.ResultBroadcastTx, error) {

// build txn
attestationBytes, err := hex.DecodeString(msg.Attestation[2:])
if err != nil {
return nil, errors.New("unable to decode message attestation")
}

// get priv key
ethereumAddress := cfg.Networks.Minters[0].MinterAddress
keyBz, _ := hex.DecodeString(cfg.Networks.Minters[4].MinterPrivateKey)
privKey := secp256k1.PrivKey{Key: keyBz}

// sign tx
addr, _ := bech32.ConvertAndEncode("noble", privKey.PubKey().Address())
if addr != ethereumAddress {
return nil, fmt.Errorf("private key (%s) does not match noble address (%s)", addr, ethereumAddress)
}

// set up eth client

// broadcast txn

for attempt := 0; attempt <= cfg.Networks.Destination.Ethereum.BroadcastRetries; attempt++ {
logger.Debug(fmt.Sprintf(
"Broadcasting %s message from %d to %d: with source tx hash %s",
msg.Type,
msg.SourceDomain,
msg.DestDomain,
msg.SourceTxHash))

// TODO Account sequence lock is implemented but gets out of sync with remote.
// accountSequence := sequenceMap.Next(cfg.Networks.Destination.Noble.DomainId)
nonce, err := GetEthereumAccountNonce(cfg.Networks.Destination.Ethereum.RPC, ethereumAddress)
if err != nil {
logger.Error("unable to retrieve account nonce")
}

// broadcast txn
// TODO do for Erh
rpcResponse, err := rpcClient.BroadcastTxSync(context.Background(), txBytes)
if err == nil && rpcResponse.Code == 0 {
msg.Status = types.Complete
return rpcResponse, nil
}
if err != nil {
logger.Error(fmt.Sprintf("error during broadcast: %s", err.Error()))
logger.Info(fmt.Sprintf("Retrying in %d seconds", cfg.Networks.Destination.Noble.BroadcastRetryInterval))
time.Sleep(time.Duration(cfg.Networks.Destination.Noble.BroadcastRetryInterval) * time.Second)
continue
}
// check tx response code
logger.Error(fmt.Sprintf("received non zero : %d - %s", rpcResponse.Code, rpcResponse.Log))
logger.Info(fmt.Sprintf("Retrying in %d seconds", cfg.Networks.Destination.Noble.BroadcastRetryInterval))
time.Sleep(time.Duration(cfg.Networks.Destination.Noble.BroadcastRetryInterval) * time.Second)
}
msg.Status = types.Failed

return nil, errors.New("reached max number of broadcast attempts")
}

// TODO
func GetEthereumAccountNonce(urlBase string, address string) (int64, error) {
rawResp, err := http.Get(fmt.Sprintf("%s/cosmos/auth/v1beta1/accounts/%s", urlBase, address))
if err != nil {
return 0, errors.New("unable to fetch account number, sequence")
}
body, _ := io.ReadAll(rawResp.Body)
var resp types.AccountResp
err = json.Unmarshal(body, &resp)
if err != nil {
return 0, errors.New("unable to parse account number, sequence")
}
accountNumber, _ := strconv.ParseInt(resp.AccountNumber, 10, 0)
accountSequence, _ := strconv.ParseInt(resp.Sequence, 10, 0)

return accountNumber, nil
}
20 changes: 20 additions & 0 deletions cmd/noble/listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package noble

import (
"cosmossdk.io/log"
"fmt"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
)

func StartListener(cfg config.Config, logger log.Logger, processingQueue chan *types.MessageState) {
// set up client

logger.Info(fmt.Sprintf(
"Starting Noble listener at block %d looking back %d blocks",
cfg.Networks.Source.Noble.StartBlock,
cfg.Networks.Source.Noble.LookbackPeriod))

// constantly query for blocks

}
51 changes: 51 additions & 0 deletions cmd/noble/listener_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package noble

import (
"cosmossdk.io/log"
"github.com/rs/zerolog"
"github.com/strangelove-ventures/noble-cctp-relayer/config"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"github.com/stretchr/testify/require"
"os"
"testing"
"time"
)

var cfg config.Config
var logger log.Logger
var processingQueue chan *types.MessageState

func init() {
cfg = config.Parse("../../.ignore/unit_tests.yaml")

logger = log.NewLogger(os.Stdout, log.LevelOption(zerolog.ErrorLevel))
processingQueue = make(chan *types.MessageState, 10000)
}

// TODO
func TestStartListener(t *testing.T) {

cfg.Networks.Source.Noble.StartBlock = 9702735
cfg.Networks.Source.Noble.LookbackPeriod = 0
go StartListener(cfg, logger, processingQueue)

time.Sleep(5 * time.Second)

msg := <-processingQueue

expectedMsg := &types.MessageState{
IrisLookupId: "a404f4155166a1fc7ffee145b5cac6d0f798333745289ab1db171344e226ef0c",
Type: "mint",
Status: "created",
SourceDomain: 0,
DestDomain: 4,
SourceTxHash: "0xe1d7729de300274ee3a2fd20ba179b14a8e3ffcd9d847c506b06760f0dad7802",
}
require.Equal(t, expectedMsg.IrisLookupId, msg.IrisLookupId)
require.Equal(t, expectedMsg.Type, msg.Type)
require.Equal(t, expectedMsg.Status, msg.Status)
require.Equal(t, expectedMsg.SourceDomain, msg.SourceDomain)
require.Equal(t, expectedMsg.DestDomain, msg.DestDomain)
require.Equal(t, expectedMsg.SourceTxHash, msg.SourceTxHash)

}
3 changes: 3 additions & 0 deletions cmd/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ func Start(cmd *cobra.Command, args []string) {
if Cfg.Networks.Source.Ethereum.Enabled {
ethereum.StartListener(Cfg, Logger, processingQueue)
}
if Cfg.Networks.Source.Noble.Enabled {
noble.StartListener(Cfg, Logger, processingQueue)
}
// ...register more chain listeners here

wg.Wait()
Expand Down
11 changes: 10 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,23 @@ func init() {
Logger.Info("successfully parsed config file", "location", cfgFile)
// set defaults

// if start block not set, default to latest
// if Ethereum start block not set, default to latest
if Cfg.Networks.Source.Ethereum.StartBlock == 0 {
client, _ := ethclient.Dial(Cfg.Networks.Source.Ethereum.RPC)
defer client.Close()
header, _ := client.HeaderByNumber(context.Background(), nil)
Cfg.Networks.Source.Ethereum.StartBlock = header.Number.Uint64()
}

// if Noble start block not set, default to latest
if Cfg.Networks.Source.Ethereum.StartBlock == 0 {
// TODO set to latest block
client, _ := ethclient.Dial(Cfg.Networks.Source.Ethereum.RPC)
defer client.Close()
header, _ := client.HeaderByNumber(context.Background(), nil)
Cfg.Networks.Source.Ethereum.StartBlock = header.Number.Uint64()
}

// start api server
go startApi()
})
Expand Down
14 changes: 14 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,22 @@ type Config struct {
LookbackPeriod uint64 `yaml:"lookback-period"`
Enabled bool `yaml:"enabled"`
} `yaml:"ethereum"`
Noble struct {
DomainId uint32 `yaml:"domain-id"`
RPC string `yaml:"rpc"`
RequestQueueSize uint32 `yaml:"request-queue-size"`
StartBlock uint64 `yaml:"start-block"`
LookbackPeriod uint64 `yaml:"lookback-period"`
Enabled bool `yaml:"enabled"`
} `yaml:"noble"`
} `yaml:"source"`
Destination struct {
Ethereum struct {
DomainId uint32 `yaml:"domain-id"`
RPC string `yaml:"rpc"`
BroadcastRetries int `yaml:"broadcast-retries"`
BroadcastRetryInterval int `yaml:"broadcast-retry-interval"`
} `yaml:"ethereum"`
Noble struct {
DomainId uint32 `yaml:"domain-id"`
RPC string `yaml:"rpc"`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package integration_testing

import (
"context"
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/bech32"
Expand All @@ -14,16 +12,13 @@ import (
eth "github.com/strangelove-ventures/noble-cctp-relayer/cmd/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"github.com/stretchr/testify/require"
"io"
"math/big"
"net/http"
"strconv"
"testing"
"time"
)

// TestGenerateEthDepositForBurn generates and broadcasts a depositForBurnWithMetadata on Ethereum Goerli
func TestGenerateEthDepositForBurnWithForward(t *testing.T) {
// TestEthBurnToNobleMintAndForward generates a depositForBurn on Ethereum Goerli and mints + forwards on Noble
func TestEthBurnToNobleMintAndForward(t *testing.T) {
setupTest()

// start up relayer
Expand Down Expand Up @@ -102,22 +97,3 @@ func TestGenerateEthDepositForBurnWithForward(t *testing.T) {
// verify dydx balance
require.Equal(t, originalDydx+BurnAmount.Uint64(), getDydxBalance(dydxAddress))
}

func getDydxBalance(address string) uint64 {
rawResponse, _ := http.Get(fmt.Sprintf(
"https://dydx-testnet-api.polkachu.com/cosmos/bank/v1beta1/balances/%s/by_denom?denom=ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", address))
body, _ := io.ReadAll(rawResponse.Body)
response := BalanceResponse{}
_ = json.Unmarshal(body, &response)
res, _ := strconv.ParseInt(response.Balance.Amount, 0, 0)
return uint64(res)
}

func getEthereumLatestBlockHeight(t *testing.T) uint64 {
client, err := ethclient.Dial(cfg.Networks.Source.Ethereum.RPC)
require.Nil(t, err)

header, err := client.HeaderByNumber(context.Background(), nil)
require.Nil(t, err)
return header.Number.Uint64()
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package integration_testing

import (
"encoding/json"
"fmt"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
"github.com/cosmos/cosmos-sdk/types/bech32"
Expand All @@ -13,16 +12,13 @@ import (
eth "github.com/strangelove-ventures/noble-cctp-relayer/cmd/ethereum"
"github.com/strangelove-ventures/noble-cctp-relayer/types"
"github.com/stretchr/testify/require"
"io"
"math/big"
"net/http"
"strconv"
"testing"
"time"
)

// TestGenerateEthDepositForBurn generates and broadcasts a depositForBurn on Ethereum Goerli
func TestGenerateEthDepositForBurn(t *testing.T) {
// TestEthBurnToNobleMint generates a depositForBurn on Ethereum Goerli and mints on Noble
func TestEthBurnToNobleMint(t *testing.T) {
setupTest()

// start up relayer
Expand Down Expand Up @@ -89,12 +85,3 @@ func TestGenerateEthDepositForBurn(t *testing.T) {
// verify noble balance
require.Equal(t, originalNobleBalance+burnAmount.Uint64(), getNobleBalance(nobleAddress))
}

func getNobleBalance(address string) uint64 {
rawResponse, _ := http.Get(fmt.Sprintf("https://lcd.testnet.noble.strange.love/cosmos/bank/v1beta1/balances/%s/by_denom?denom=uusdc", address))
body, _ := io.ReadAll(rawResponse.Body)
response := BalanceResponse{}
_ = json.Unmarshal(body, &response)
result, _ := strconv.ParseInt(response.Balance.Amount, 10, 0)
return uint64(result)
}
Loading

0 comments on commit 72ae89d

Please sign in to comment.